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
@@ -0,0 +1,159 @@
|
||||
// EterLib CFileLoaderThread / CThread / Mutex over the Win32Crt kernel-object shims.
|
||||
// 40250 leaves CResourceManager::ms_loadingThread un-Created (the Create call is commented out), so the
|
||||
// live path is a static, zero-initialised loader whose Request finds no semaphore and whose Fetch
|
||||
// never yields; a Created loader reads each request through CEterPackManager on its own thread.
|
||||
#include "EterLib/StdAfx.h"
|
||||
#include "EterLib/FileLoaderThread.h"
|
||||
#include "EterLib/Thread.h"
|
||||
#include "EterLib/Mutex.h"
|
||||
#include "EterLib/ResourceManager.h"
|
||||
#include "EterPack/EterPackManager.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
int failures = 0;
|
||||
|
||||
void check(bool ok, const char * name)
|
||||
{
|
||||
if (!ok)
|
||||
{
|
||||
std::printf("FAIL %s\n", name);
|
||||
++failures;
|
||||
}
|
||||
}
|
||||
|
||||
CFileLoaderThread s_idleLoader; // like CResourceManager::ms_loadingThread: static storage, never Created
|
||||
CFileLoaderThread s_loader;
|
||||
|
||||
std::atomic<int> s_counter{0};
|
||||
|
||||
class CCountThread : public CThread
|
||||
{
|
||||
public:
|
||||
UINT m_setup = 1;
|
||||
void * m_arg = NULL;
|
||||
|
||||
protected:
|
||||
UINT Setup() override { return m_setup; }
|
||||
UINT Execute(void * arg) override
|
||||
{
|
||||
m_arg = arg;
|
||||
++s_counter;
|
||||
return 7;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Semaphore: counts, the maximum, timeouts.
|
||||
{
|
||||
HANDLE sem = CreateSemaphore(NULL, 1, 2, NULL);
|
||||
check(sem != NULL, "CreateSemaphore");
|
||||
check(!CreateSemaphore(NULL, 3, 2, NULL), "CreateSemaphore initial > maximum");
|
||||
check(WaitForSingleObject(sem, 0) == WAIT_OBJECT_0, "semaphore initial count");
|
||||
check(WaitForSingleObject(sem, 10) == WAIT_TIMEOUT, "semaphore empty times out");
|
||||
LONG previous = -1;
|
||||
check(ReleaseSemaphore(sem, 2, &previous) && previous == 0, "ReleaseSemaphore previous count");
|
||||
check(!ReleaseSemaphore(sem, 1, NULL), "ReleaseSemaphore past maximum fails");
|
||||
check(WaitForSingleObject(sem, 0) == WAIT_OBJECT_0 && WaitForSingleObject(sem, 0) == WAIT_OBJECT_0
|
||||
&& WaitForSingleObject(sem, 0) == WAIT_TIMEOUT, "semaphore count kept after failed release");
|
||||
check(!ReleaseSemaphore(NULL, 1, NULL), "ReleaseSemaphore NULL handle");
|
||||
check(WaitForSingleObject(NULL, 0) == WAIT_FAILED, "WaitForSingleObject NULL handle");
|
||||
|
||||
std::thread waker([sem] {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
ReleaseSemaphore(sem, 1, NULL);
|
||||
});
|
||||
check(WaitForSingleObject(sem, INFINITE) == WAIT_OBJECT_0, "semaphore wakes a waiter");
|
||||
waker.join();
|
||||
check(CloseHandle(sem) && !CloseHandle(NULL), "CloseHandle");
|
||||
}
|
||||
|
||||
// Mutex is recursive (CRITICAL_SECTION); CThread runs Setup then Execute on a new thread.
|
||||
{
|
||||
Mutex mutex;
|
||||
mutex.Lock();
|
||||
mutex.Lock();
|
||||
mutex.Unlock();
|
||||
mutex.Unlock();
|
||||
|
||||
CCountThread thread;
|
||||
int arg = 0;
|
||||
check(thread.Create(&arg) != 0, "CThread Create");
|
||||
for (int i = 0; i < 200 && s_counter.load() == 0; ++i)
|
||||
Sleep(5);
|
||||
check(s_counter.load() == 1 && thread.m_arg == &arg, "CThread Execute with arg");
|
||||
|
||||
CCountThread noSetup;
|
||||
noSetup.m_setup = 0;
|
||||
check(noSetup.Create(NULL) != 0, "CThread Create without Setup");
|
||||
Sleep(30);
|
||||
check(s_counter.load() == 1, "CThread skips Execute when Setup fails");
|
||||
}
|
||||
|
||||
char dir[] = "/tmp/mt_fileloader_XXXXXX";
|
||||
check(mkdtemp(dir) != NULL, "mkdtemp");
|
||||
std::string path = std::string(dir) + "/a.txt";
|
||||
FILE * fp = std::fopen(path.c_str(), "wb");
|
||||
std::fwrite("hello", 1, 5, fp);
|
||||
std::fclose(fp);
|
||||
|
||||
CEterPackManager packs; // SEARCH_FILE_FIRST: requests read the file from disk
|
||||
g_iLoadingDelayTime = 0;
|
||||
|
||||
// 40250 live path: never Created.
|
||||
{
|
||||
std::string name = path;
|
||||
s_idleLoader.Request(name);
|
||||
CFileLoaderThread::TData * pData = NULL;
|
||||
check(!s_idleLoader.Fetch(&pData), "un-Created loader never completes a request");
|
||||
s_idleLoader.Shutdown();
|
||||
}
|
||||
|
||||
// Created: requests complete in order with the file contents; a missing file completes empty.
|
||||
{
|
||||
check(s_loader.Create(NULL) != 0, "CFileLoaderThread Create");
|
||||
Sleep(20); // Setup (the semaphore) runs on the loader thread
|
||||
std::string name = path;
|
||||
std::string missing = std::string(dir) + "/missing.txt";
|
||||
s_loader.Request(name);
|
||||
s_loader.Request(missing);
|
||||
|
||||
CFileLoaderThread::TData * got[2] = {};
|
||||
int count = 0;
|
||||
for (int i = 0; i < 400 && count < 2; ++i)
|
||||
{
|
||||
if (!s_loader.Fetch(&got[count]))
|
||||
Sleep(5);
|
||||
else
|
||||
++count;
|
||||
}
|
||||
check(count == 2, "both requests complete");
|
||||
if (count == 2)
|
||||
{
|
||||
check(got[0]->stFileName == path && got[0]->dwSize == 5 && std::memcmp(got[0]->pvBuf, "hello", 5) == 0,
|
||||
"request loaded through CEterPackManager");
|
||||
check(got[1]->dwSize == 0 && got[1]->pvBuf == NULL, "missing file completes empty");
|
||||
for (CFileLoaderThread::TData * pData : got)
|
||||
{
|
||||
delete [] ((char *) pData->pvBuf);
|
||||
delete pData;
|
||||
}
|
||||
}
|
||||
s_loader.Shutdown();
|
||||
}
|
||||
|
||||
unlink(path.c_str());
|
||||
rmdir(dir);
|
||||
if (!failures)
|
||||
std::printf("ok\n");
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user