port(EterLib): Mutex/Thread/FileLoaderThread/GrpVertexBufferDynamic verbatim over Win32Crt kernel-object shims

Adds CreateSemaphore/ReleaseSemaphore/WaitForSingleObject/CloseHandle/
SetThreadPriority/_beginthreadex to Win32Crt; port.file_loader_thread covers
the shims, CThread and both the un-Created (40250 live) and Created loader.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-30 12:56:24 +09:00
co-authored by Claude Opus 5.5
parent e0cdcee2e6
commit 042bddf195
13 changed files with 758 additions and 75 deletions
+3
View File
@@ -213,6 +213,9 @@ if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME)
add_executable(port_screenshot_test ${PROJECT_SOURCE_DIR}/tests/port/port_screenshot_test.cpp)
target_link_libraries(port_screenshot_test PRIVATE port_platform)
add_test(NAME port.screenshot COMMAND $<TARGET_FILE:port_screenshot_test>)
add_executable(port_file_loader_thread_test ${PROJECT_SOURCE_DIR}/tests/port/port_file_loader_thread_test.cpp)
target_link_libraries(port_file_loader_thread_test PRIVATE port_platform)
add_test(NAME port.file_loader_thread COMMAND $<TARGET_FILE:port_file_loader_thread_test>)
# EterBase/Debug.cpp: syserr.txt / log.txt (redirects its own stderr).
add_executable(port_debug_log_test ${PROJECT_SOURCE_DIR}/tests/port/port_debug_log_test.cpp)
+136
View File
@@ -3,6 +3,10 @@
#if !defined(_WIN32)
#include <cctype>
#include <chrono>
#include <atomic>
#include <condition_variable>
#include <system_error>
#include <memory>
#include <cstdio>
#include <cstring>
#include <mutex>
@@ -170,6 +174,138 @@ void Sleep(DWORD milliseconds)
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
// A HANDLE from CreateSemaphore or _beginthreadex points at one of these. The waitable state is shared
// with a running thread, so CloseHandle on a live thread only drops the handle, as on Win32.
namespace
{
struct KernelObject
{
virtual ~KernelObject() = default;
virtual DWORD Wait(DWORD milliseconds) = 0;
};
template <class Ready>
DWORD wait_until(std::unique_lock<std::mutex>& lock, std::condition_variable& cv, DWORD milliseconds, Ready ready)
{
if (milliseconds == INFINITE)
cv.wait(lock, ready);
else if (!cv.wait_for(lock, std::chrono::milliseconds(milliseconds), ready))
return WAIT_TIMEOUT;
return WAIT_OBJECT_0;
}
struct Semaphore final : KernelObject
{
std::mutex mutex;
std::condition_variable cv;
LONG count = 0;
LONG maximum = 0;
DWORD Wait(DWORD milliseconds) override
{
std::unique_lock<std::mutex> lock(mutex);
const DWORD result = wait_until(lock, cv, milliseconds, [this] { return count > 0; });
if (result == WAIT_OBJECT_0)
--count;
return result;
}
};
struct ThreadState
{
std::mutex mutex;
std::condition_variable cv;
bool done = false;
};
struct Thread final : KernelObject
{
std::shared_ptr<ThreadState> state = std::make_shared<ThreadState>();
DWORD Wait(DWORD milliseconds) override
{
std::unique_lock<std::mutex> lock(state->mutex);
return wait_until(lock, state->cv, milliseconds, [this] { return state->done; });
}
};
}
HANDLE CreateSemaphore(void*, LONG initial_count, LONG maximum_count, LPCSTR)
{
if (maximum_count <= 0 || initial_count < 0 || initial_count > maximum_count)
return NULL;
Semaphore* semaphore = new Semaphore;
semaphore->count = initial_count;
semaphore->maximum = maximum_count;
return static_cast<KernelObject*>(semaphore);
}
// Fails without changing the count when the release would pass the maximum (ERROR_TOO_MANY_POSTS).
BOOL ReleaseSemaphore(HANDLE handle, LONG release_count, LONG* previous_count)
{
Semaphore* semaphore = handle ? dynamic_cast<Semaphore*>(static_cast<KernelObject*>(handle)) : nullptr;
if (!semaphore || release_count <= 0)
return FALSE;
{
std::lock_guard<std::mutex> lock(semaphore->mutex);
if (release_count > semaphore->maximum - semaphore->count)
return FALSE;
if (previous_count)
*previous_count = semaphore->count;
semaphore->count += release_count;
}
semaphore->cv.notify_all();
return TRUE;
}
DWORD WaitForSingleObject(HANDLE handle, DWORD milliseconds)
{
if (!handle || handle == INVALID_HANDLE_VALUE)
return WAIT_FAILED;
return static_cast<KernelObject*>(handle)->Wait(milliseconds);
}
BOOL CloseHandle(HANDLE handle)
{
if (!handle || handle == INVALID_HANDLE_VALUE)
return FALSE;
delete static_cast<KernelObject*>(handle);
return TRUE;
}
BOOL SetThreadPriority(HANDLE handle, int)
{
return handle ? TRUE : FALSE;
}
uintptr_t _beginthreadex(void*, unsigned, unsigned (*start_address)(void*), void* arglist, unsigned, unsigned* thrdaddr)
{
Thread* thread = new Thread;
std::shared_ptr<ThreadState> state = thread->state;
try
{
std::thread([state, start_address, arglist] {
start_address(arglist);
{
std::lock_guard<std::mutex> lock(state->mutex);
state->done = true;
}
state->cv.notify_all();
}).detach();
}
catch (const std::system_error&)
{
delete thread;
return 0;
}
if (thrdaddr)
{
static std::atomic<unsigned> next_id{1};
*thrdaddr = next_id++;
}
return reinterpret_cast<uintptr_t>(static_cast<KernelObject*>(thread));
}
char* _strlwr(char* s)
{
for (char* p = s; *p; ++p)
+15
View File
@@ -73,6 +73,21 @@ BOOL FindNextFile(HANDLE handle, WIN32_FIND_DATA* data);
BOOL FindClose(HANDLE handle);
void Sleep(DWORD milliseconds);
// kernel32 synchronization objects used by EterLib (FileLoaderThread): a counting semaphore and a
// _beginthreadex thread, both waitable with WaitForSingleObject and freed with CloseHandle.
// Only the unnamed, default-security forms exist; thread priority is accepted and ignored.
#define WAIT_OBJECT_0 0x00000000L
#define WAIT_TIMEOUT 0x00000102L
#define WAIT_FAILED 0xFFFFFFFF
#define THREAD_PRIORITY_NORMAL 0
HANDLE CreateSemaphore(void* security_attributes, LONG initial_count, LONG maximum_count, LPCSTR name);
BOOL ReleaseSemaphore(HANDLE semaphore, LONG release_count, LONG* previous_count);
DWORD WaitForSingleObject(HANDLE handle, DWORD milliseconds);
BOOL CloseHandle(HANDLE handle);
BOOL SetThreadPriority(HANDLE thread, int priority);
uintptr_t _beginthreadex(void* security, unsigned stack_size, unsigned (*start_address)(void*), void* arglist,
unsigned initflag, unsigned* thrdaddr);
// MSVC <stdlib.h> __min / __max
#ifndef __min
#define __min(a, b) (((a) < (b)) ? (a) : (b))