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:
co-authored by
Claude Opus 5.5
parent
e0cdcee2e6
commit
042bddf195
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user