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
@@ -607,3 +607,7 @@
|
|||||||
{"date": "2026-09-30", "unit": "EterBase/FileBase.cpp", "action": "all units mapped (FILE* over Win32 HANDLE); stale fstream note corrected", "evidence": "port.utils CRC32/CFileBase/CMappedFile checks"}
|
{"date": "2026-09-30", "unit": "EterBase/FileBase.cpp", "action": "all units mapped (FILE* over Win32 HANDLE); stale fstream note corrected", "evidence": "port.utils CRC32/CFileBase/CMappedFile checks"}
|
||||||
{"date": "2026-09-30", "unit": "EterBase/MappedFile.cpp", "action": "Map/Unmap ADAPTED (mmap), rest PORTED verbatim; stale ifstream note corrected", "evidence": "port.utils CRC32/CFileBase/CMappedFile checks"}
|
{"date": "2026-09-30", "unit": "EterBase/MappedFile.cpp", "action": "Map/Unmap ADAPTED (mmap), rest PORTED verbatim; stale ifstream note corrected", "evidence": "port.utils CRC32/CFileBase/CMappedFile checks"}
|
||||||
{"date": "2026-09-30", "unit": "EterBase/Debug.cpp", "action": "Log*/Trace*/LogFile*/OpenLogFile PORTED (non-_DEBUG), OpenConsoleWindow N_A", "evidence": "port.debug_log"}
|
{"date": "2026-09-30", "unit": "EterBase/Debug.cpp", "action": "Log*/Trace*/LogFile*/OpenLogFile PORTED (non-_DEBUG), OpenConsoleWindow N_A", "evidence": "port.debug_log"}
|
||||||
|
{"date": "2026-09-30", "unit": "EterLib/Mutex.cpp", "action": "all PORTED verbatim over the CRITICAL_SECTION shim", "evidence": "port.file_loader_thread"}
|
||||||
|
{"date": "2026-09-30", "unit": "EterLib/Thread.cpp", "action": "all PORTED verbatim over new Win32Crt _beginthreadex shim", "evidence": "port.file_loader_thread"}
|
||||||
|
{"date": "2026-09-30", "unit": "EterLib/FileLoaderThread.cpp", "action": "all PORTED verbatim over new Win32Crt semaphore/thread/WaitForSingleObject/CloseHandle shims", "evidence": "port.file_loader_thread"}
|
||||||
|
{"date": "2026-09-30", "unit": "EterLib/GrpVertexBufferDynamic.cpp", "action": "all PORTED verbatim (no 40250 instantiation)", "evidence": "build"}
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
{
|
||||||
|
"reference": "EterLib/FileLoaderThread.cpp",
|
||||||
|
"reference_sha256": "fe40ffa69088383baec56f2ef3c0b2a70448e018f5661b6aaf6dfb75e4efbf5a",
|
||||||
|
"priority": "P4",
|
||||||
|
"contracts": [],
|
||||||
|
"functions": {
|
||||||
|
"CFileLoaderThread::CFileLoaderThread": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/FileLoaderThread.cpp:CFileLoaderThread::CFileLoaderThread"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; semaphore/thread/wait calls are the Win32Crt kernel-object shims. 40250 never Creates CResourceManager::ms_loadingThread, so Request logs the ReleaseSemaphore error and Fetch never yields, as tested."
|
||||||
|
},
|
||||||
|
"CFileLoaderThread::~CFileLoaderThread": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/FileLoaderThread.cpp:CFileLoaderThread::~CFileLoaderThread"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; semaphore/thread/wait calls are the Win32Crt kernel-object shims. 40250 never Creates CResourceManager::ms_loadingThread, so Request logs the ReleaseSemaphore error and Fetch never yields, as tested."
|
||||||
|
},
|
||||||
|
"CFileLoaderThread::Create": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/FileLoaderThread.cpp:CFileLoaderThread::Create"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; semaphore/thread/wait calls are the Win32Crt kernel-object shims. 40250 never Creates CResourceManager::ms_loadingThread, so Request logs the ReleaseSemaphore error and Fetch never yields, as tested."
|
||||||
|
},
|
||||||
|
"CFileLoaderThread::Run": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/FileLoaderThread.cpp:CFileLoaderThread::Run"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; semaphore/thread/wait calls are the Win32Crt kernel-object shims. 40250 never Creates CResourceManager::ms_loadingThread, so Request logs the ReleaseSemaphore error and Fetch never yields, as tested."
|
||||||
|
},
|
||||||
|
"CFileLoaderThread::EntryPoint": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/FileLoaderThread.cpp:CFileLoaderThread::EntryPoint"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; semaphore/thread/wait calls are the Win32Crt kernel-object shims. 40250 never Creates CResourceManager::ms_loadingThread, so Request logs the ReleaseSemaphore error and Fetch never yields, as tested."
|
||||||
|
},
|
||||||
|
"CFileLoaderThread::Destroy": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/FileLoaderThread.cpp:CFileLoaderThread::Destroy"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; semaphore/thread/wait calls are the Win32Crt kernel-object shims. 40250 never Creates CResourceManager::ms_loadingThread, so Request logs the ReleaseSemaphore error and Fetch never yields, as tested."
|
||||||
|
},
|
||||||
|
"CFileLoaderThread::Setup": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/FileLoaderThread.cpp:CFileLoaderThread::Setup"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; semaphore/thread/wait calls are the Win32Crt kernel-object shims. 40250 never Creates CResourceManager::ms_loadingThread, so Request logs the ReleaseSemaphore error and Fetch never yields, as tested."
|
||||||
|
},
|
||||||
|
"CFileLoaderThread::Shutdown": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/FileLoaderThread.cpp:CFileLoaderThread::Shutdown"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; semaphore/thread/wait calls are the Win32Crt kernel-object shims. 40250 never Creates CResourceManager::ms_loadingThread, so Request logs the ReleaseSemaphore error and Fetch never yields, as tested."
|
||||||
|
},
|
||||||
|
"CFileLoaderThread::Execute": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/FileLoaderThread.cpp:CFileLoaderThread::Execute"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; semaphore/thread/wait calls are the Win32Crt kernel-object shims. 40250 never Creates CResourceManager::ms_loadingThread, so Request logs the ReleaseSemaphore error and Fetch never yields, as tested."
|
||||||
|
},
|
||||||
|
"CFileLoaderThread::Request": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/FileLoaderThread.cpp:CFileLoaderThread::Request"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; semaphore/thread/wait calls are the Win32Crt kernel-object shims. 40250 never Creates CResourceManager::ms_loadingThread, so Request logs the ReleaseSemaphore error and Fetch never yields, as tested."
|
||||||
|
},
|
||||||
|
"CFileLoaderThread::Fetch": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/FileLoaderThread.cpp:CFileLoaderThread::Fetch"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; semaphore/thread/wait calls are the Win32Crt kernel-object shims. 40250 never Creates CResourceManager::ms_loadingThread, so Request logs the ReleaseSemaphore error and Fetch never yields, as tested."
|
||||||
|
},
|
||||||
|
"CFileLoaderThread::Process": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/FileLoaderThread.cpp:CFileLoaderThread::Process"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; semaphore/thread/wait calls are the Win32Crt kernel-object shims. 40250 never Creates CResourceManager::ms_loadingThread, so Request logs the ReleaseSemaphore error and Fetch never yields, as tested."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"reference": "EterLib/GrpVertexBufferDynamic.cpp",
|
||||||
|
"reference_sha256": "8bffcb4a24733a2ee4312bf45a690a1f0b46c6290e17867b763f4799055f3d3c",
|
||||||
|
"priority": "P4",
|
||||||
|
"contracts": [],
|
||||||
|
"functions": {
|
||||||
|
"CDynamicVertexBuffer::Create": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/GrpVertexBufferDynamic.cpp:CDynamicVertexBuffer::Create"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250. No 40250 code instantiates CDynamicVertexBuffer."
|
||||||
|
},
|
||||||
|
"CDynamicVertexBuffer::CDynamicVertexBuffer": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/GrpVertexBufferDynamic.cpp:CDynamicVertexBuffer::CDynamicVertexBuffer"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250. No 40250 code instantiates CDynamicVertexBuffer."
|
||||||
|
},
|
||||||
|
"CDynamicVertexBuffer::~CDynamicVertexBuffer": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/GrpVertexBufferDynamic.cpp:CDynamicVertexBuffer::~CDynamicVertexBuffer"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250. No 40250 code instantiates CDynamicVertexBuffer."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"reference": "EterLib/Mutex.cpp",
|
||||||
|
"reference_sha256": "b480c0ad1f568caddcb50264b6d704dac9337ee8f7631b0f19002663f36999ff",
|
||||||
|
"priority": "P4",
|
||||||
|
"contracts": [],
|
||||||
|
"functions": {
|
||||||
|
"Mutex::Mutex": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/Mutex.cpp:Mutex::Mutex"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; CRITICAL_SECTION is the Win32Crt recursive-mutex shim."
|
||||||
|
},
|
||||||
|
"Mutex::~Mutex": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/Mutex.cpp:Mutex::~Mutex"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; CRITICAL_SECTION is the Win32Crt recursive-mutex shim."
|
||||||
|
},
|
||||||
|
"Mutex::Lock": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/Mutex.cpp:Mutex::Lock"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; CRITICAL_SECTION is the Win32Crt recursive-mutex shim."
|
||||||
|
},
|
||||||
|
"Mutex::Unlock": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/Mutex.cpp:Mutex::Unlock"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; CRITICAL_SECTION is the Win32Crt recursive-mutex shim."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"reference": "EterLib/Thread.cpp",
|
||||||
|
"reference_sha256": "b5475b7d5d3ce06b96ab9b534346648896deb6b76f459feabb09c9f4148860b5",
|
||||||
|
"priority": "P4",
|
||||||
|
"contracts": [],
|
||||||
|
"functions": {
|
||||||
|
"CThread::CThread": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/Thread.cpp:CThread::CThread"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; _beginthreadex/SetThreadPriority are the Win32Crt shims (detached std::thread, waitable handle)."
|
||||||
|
},
|
||||||
|
"CThread::Create": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/Thread.cpp:CThread::Create"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; _beginthreadex/SetThreadPriority are the Win32Crt shims (detached std::thread, waitable handle)."
|
||||||
|
},
|
||||||
|
"CThread::Run": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/Thread.cpp:CThread::Run"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; _beginthreadex/SetThreadPriority are the Win32Crt shims (detached std::thread, waitable handle)."
|
||||||
|
},
|
||||||
|
"CThread::EntryPoint": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"src/platform/EterLib/Thread.cpp:CThread::EntryPoint"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"tests/port/port_file_loader_thread_test.cpp"
|
||||||
|
],
|
||||||
|
"note": "Verbatim 40250; _beginthreadex/SetThreadPriority are the Win32Crt shims (detached std::thread, waitable handle)."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,72 +1,181 @@
|
|||||||
// Platform skeleton for EterLib/FileLoaderThread.h (40250 EterLib/FileLoaderThread.cpp), generated by platform_stub.py.
|
// 40250 EterLib/FileLoaderThread.cpp, verbatim. The kernel32 calls resolve to the Win32Crt thread/semaphore/critical-section shims.
|
||||||
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
|
|
||||||
#include "EterLib/StdAfx.h"
|
#include "EterLib/StdAfx.h"
|
||||||
|
#include "EterPack/EterPackManager.h"
|
||||||
#include "EterLib/FileLoaderThread.h"
|
#include "EterLib/FileLoaderThread.h"
|
||||||
|
#include "EterLib/ResourceManager.h"
|
||||||
|
|
||||||
#include "../PlatformStub.h"
|
CFileLoaderThread::CFileLoaderThread() : m_bShutdowned(false), m_pArg(NULL), m_hThread(NULL), m_uThreadID(0)
|
||||||
|
|
||||||
CFileLoaderThread::CFileLoaderThread()
|
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CFileLoaderThread::~CFileLoaderThread()
|
CFileLoaderThread::~CFileLoaderThread()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CFileLoaderThread::Create(void *) -> int
|
int CFileLoaderThread::Create(void * arg)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
Arg(arg);
|
||||||
return mt_platform_stub_return<int>();
|
m_hThread = (HANDLE) _beginthreadex(NULL, 0, EntryPoint, this, 0, &m_uThreadID);
|
||||||
|
|
||||||
|
if (!m_hThread)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SetThreadPriority(m_hThread, THREAD_PRIORITY_NORMAL);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CFileLoaderThread::Request(std::string &) -> void
|
UINT CFileLoaderThread::Run(void * arg)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if (!Setup())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (Execute(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CFileLoaderThread::Fetch(TData **) -> bool
|
/* Static */
|
||||||
|
UINT CALLBACK CFileLoaderThread::EntryPoint(void * pThis)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
CFileLoaderThread * pThread = (CFileLoaderThread *) pThis;
|
||||||
return mt_platform_stub_return<bool>();
|
return pThread->Run(pThread->Arg());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CFileLoaderThread::Shutdown() -> void
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
void CFileLoaderThread::Destroy()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if (m_hSemaphore)
|
||||||
|
{
|
||||||
|
CloseHandle(m_hSemaphore);
|
||||||
|
m_hSemaphore = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
stl_wipe(m_pRequestDeque);
|
||||||
|
stl_wipe(m_pCompleteDeque);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CFileLoaderThread::EntryPoint(void *) -> UINT
|
UINT CFileLoaderThread::Setup()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
m_hSemaphore = CreateSemaphore(NULL, // no security attributes
|
||||||
return mt_platform_stub_return<UINT>();
|
0, // initial count
|
||||||
|
65535, // maximum count
|
||||||
|
NULL); // unnamed semaphore
|
||||||
|
if (!m_hSemaphore)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CFileLoaderThread::Run(void *) -> UINT
|
void CFileLoaderThread::Shutdown()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if (!m_hSemaphore)
|
||||||
return mt_platform_stub_return<UINT>();
|
return;
|
||||||
|
|
||||||
|
BOOL bRet;
|
||||||
|
|
||||||
|
m_bShutdowned = true;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bRet = ReleaseSemaphore(m_hSemaphore, 1, NULL);
|
||||||
|
}
|
||||||
|
while (!bRet);
|
||||||
|
|
||||||
|
WaitForSingleObject(m_hThread, 10000); // 쓰레드가 종료 되기를 10초 기다림
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CFileLoaderThread::Setup() -> UINT
|
UINT CFileLoaderThread::Execute(void * /*pvArg*/)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
while (!m_bShutdowned)
|
||||||
return mt_platform_stub_return<UINT>();
|
{
|
||||||
|
DWORD dwWaitResult;
|
||||||
|
|
||||||
|
dwWaitResult = WaitForSingleObject(m_hSemaphore, INFINITE);
|
||||||
|
|
||||||
|
if (m_bShutdowned)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (dwWaitResult)
|
||||||
|
{
|
||||||
|
case WAIT_OBJECT_0:
|
||||||
|
{
|
||||||
|
Process();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WAIT_TIMEOUT:
|
||||||
|
TraceError("CFileLoaderThread::Execute: Timeout occured while time-out interval is INIFITE");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Destroy();
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CFileLoaderThread::Execute(void *) -> UINT
|
void CFileLoaderThread::Request(std::string & c_rstFileName) // called in main thread
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
TData * pData = new TData;
|
||||||
return mt_platform_stub_return<UINT>();
|
|
||||||
|
pData->dwSize = 0;
|
||||||
|
pData->pvBuf = NULL;
|
||||||
|
pData->stFileName = c_rstFileName;
|
||||||
|
|
||||||
|
m_RequestMutex.Lock();
|
||||||
|
m_pRequestDeque.push_back(pData);
|
||||||
|
m_RequestMutex.Unlock();
|
||||||
|
|
||||||
|
++m_iRestSemCount;
|
||||||
|
|
||||||
|
if (!ReleaseSemaphore(m_hSemaphore, m_iRestSemCount, NULL))
|
||||||
|
TraceError("CFileLoaderThread::Request: ReleaseSemaphore error");
|
||||||
|
|
||||||
|
--m_iRestSemCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CFileLoaderThread::Destroy() -> void
|
bool CFileLoaderThread::Fetch(TData ** ppData) // called in main thread
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
m_CompleteMutex.Lock();
|
||||||
|
|
||||||
|
if (m_pCompleteDeque.empty())
|
||||||
|
{
|
||||||
|
m_CompleteMutex.Unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ppData = m_pCompleteDeque.front();
|
||||||
|
m_pCompleteDeque.pop_front();
|
||||||
|
|
||||||
|
m_CompleteMutex.Unlock();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CFileLoaderThread::Process() -> void
|
void CFileLoaderThread::Process() // called in loader thread
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
m_RequestMutex.Lock();
|
||||||
|
|
||||||
|
if (m_pRequestDeque.empty())
|
||||||
|
{
|
||||||
|
m_RequestMutex.Unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TData * pData = m_pRequestDeque.front();
|
||||||
|
m_pRequestDeque.pop_front();
|
||||||
|
|
||||||
|
m_RequestMutex.Unlock();
|
||||||
|
|
||||||
|
LPCVOID pvBuf;
|
||||||
|
|
||||||
|
if (CEterPackManager::Instance().Get(pData->File, pData->stFileName.c_str(), &pvBuf))
|
||||||
|
{
|
||||||
|
pData->dwSize = pData->File.Size();
|
||||||
|
pData->pvBuf = new char [pData->dwSize];
|
||||||
|
memcpy(pData->pvBuf, pvBuf, pData->dwSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_CompleteMutex.Lock();
|
||||||
|
m_pCompleteDeque.push_back(pData);
|
||||||
|
m_CompleteMutex.Unlock();
|
||||||
|
|
||||||
|
Sleep(g_iLoadingDelayTime);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,30 @@
|
|||||||
// Platform skeleton for EterLib/GrpVertexBufferDynamic.h (40250 EterLib/GrpVertexBufferDynamic.cpp), generated by platform_stub.py.
|
// 40250 EterLib/GrpVertexBufferDynamic.cpp, verbatim.
|
||||||
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
|
|
||||||
#include "EterLib/StdAfx.h"
|
#include "EterLib/StdAfx.h"
|
||||||
#include "EterLib/GrpVertexBufferDynamic.h"
|
#include "EterLib/GrpVertexBufferDynamic.h"
|
||||||
|
|
||||||
#include "../PlatformStub.h"
|
bool CDynamicVertexBuffer::Create(int vtxCount, int fvf)
|
||||||
|
{
|
||||||
|
if (m_lpd3dVB)
|
||||||
|
{
|
||||||
|
if (m_fvf == fvf)
|
||||||
|
{
|
||||||
|
if (m_vtxCount >= vtxCount)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_vtxCount = vtxCount;
|
||||||
|
m_fvf = fvf;
|
||||||
|
|
||||||
|
return CGraphicVertexBuffer::Create(m_vtxCount, m_fvf, D3DUSAGE_DYNAMIC, D3DPOOL_SYSTEMMEM);
|
||||||
|
}
|
||||||
|
|
||||||
CDynamicVertexBuffer::CDynamicVertexBuffer()
|
CDynamicVertexBuffer::CDynamicVertexBuffer()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
m_vtxCount = 0;
|
||||||
|
m_fvf = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CDynamicVertexBuffer::~CDynamicVertexBuffer()
|
CDynamicVertexBuffer::~CDynamicVertexBuffer()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CDynamicVertexBuffer::Create(int, int) -> bool
|
|
||||||
{
|
|
||||||
MT_PLATFORM_STUB();
|
|
||||||
return mt_platform_stub_return<bool>();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,23 @@
|
|||||||
// Platform skeleton for EterLib/Mutex.h (40250 EterLib/Mutex.cpp), generated by platform_stub.py.
|
// 40250 EterLib/Mutex.cpp, verbatim. The kernel32 calls resolve to the Win32Crt thread/semaphore/critical-section shims.
|
||||||
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
|
|
||||||
#include "EterLib/StdAfx.h"
|
#include "EterLib/StdAfx.h"
|
||||||
#include "EterLib/Mutex.h"
|
#include "EterLib/Mutex.h"
|
||||||
|
|
||||||
#include "../PlatformStub.h"
|
|
||||||
|
|
||||||
Mutex::Mutex()
|
Mutex::Mutex()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
InitializeCriticalSection(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex::~Mutex()
|
Mutex::~Mutex()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
DeleteCriticalSection(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Mutex::Lock() -> void
|
void Mutex::Lock()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
EnterCriticalSection(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Mutex::Unlock() -> void
|
void Mutex::Unlock()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
LeaveCriticalSection(&lock);
|
||||||
}
|
|
||||||
|
|
||||||
auto Mutex::Trylock() -> bool
|
|
||||||
{
|
|
||||||
MT_PLATFORM_STUB();
|
|
||||||
return mt_platform_stub_return<bool>();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,34 @@
|
|||||||
// Platform skeleton for EterLib/Thread.h (40250 EterLib/Thread.cpp), generated by platform_stub.py.
|
// 40250 EterLib/Thread.cpp, verbatim. The kernel32 calls resolve to the Win32Crt thread/semaphore/critical-section shims.
|
||||||
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
|
|
||||||
#include "EterLib/StdAfx.h"
|
#include "EterLib/StdAfx.h"
|
||||||
#include "EterLib/Thread.h"
|
#include "EterLib/Thread.h"
|
||||||
|
|
||||||
#include "../PlatformStub.h"
|
CThread::CThread() : m_pArg(NULL), m_hThread(NULL), m_uThreadID(0)
|
||||||
|
|
||||||
CThread::CThread()
|
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CThread::Create(void *) -> int
|
int CThread::Create(void * arg)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
Arg(arg);
|
||||||
return mt_platform_stub_return<int>();
|
m_hThread = (HANDLE) _beginthreadex(NULL, 0, EntryPoint, this, 0, &m_uThreadID);
|
||||||
|
|
||||||
|
if (!m_hThread)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SetThreadPriority(m_hThread, THREAD_PRIORITY_NORMAL);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CThread::EntryPoint(void *) -> UINT
|
UINT CThread::Run(void * arg)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if (!Setup())
|
||||||
return mt_platform_stub_return<UINT>();
|
return 0;
|
||||||
|
|
||||||
|
return (Execute(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CThread::Run(void *) -> UINT
|
/* Static */
|
||||||
|
UINT CALLBACK CThread::EntryPoint(void * pThis)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
CThread * pThread = (CThread *) pThis;
|
||||||
return mt_platform_stub_return<UINT>();
|
return pThread->Run(pThread->Arg());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
add_executable(port_screenshot_test ${PROJECT_SOURCE_DIR}/tests/port/port_screenshot_test.cpp)
|
||||||
target_link_libraries(port_screenshot_test PRIVATE port_platform)
|
target_link_libraries(port_screenshot_test PRIVATE port_platform)
|
||||||
add_test(NAME port.screenshot COMMAND $<TARGET_FILE:port_screenshot_test>)
|
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).
|
# 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)
|
add_executable(port_debug_log_test ${PROJECT_SOURCE_DIR}/tests/port/port_debug_log_test.cpp)
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <system_error>
|
||||||
|
#include <memory>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -170,6 +174,138 @@ void Sleep(DWORD milliseconds)
|
|||||||
std::this_thread::sleep_for(std::chrono::milliseconds(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)
|
char* _strlwr(char* s)
|
||||||
{
|
{
|
||||||
for (char* p = s; *p; ++p)
|
for (char* p = s; *p; ++p)
|
||||||
|
|||||||
@@ -73,6 +73,21 @@ BOOL FindNextFile(HANDLE handle, WIN32_FIND_DATA* data);
|
|||||||
BOOL FindClose(HANDLE handle);
|
BOOL FindClose(HANDLE handle);
|
||||||
void Sleep(DWORD milliseconds);
|
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
|
// MSVC <stdlib.h> __min / __max
|
||||||
#ifndef __min
|
#ifndef __min
|
||||||
#define __min(a, b) (((a) < (b)) ? (a) : (b))
|
#define __min(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
|||||||
@@ -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