diff --git a/Aerofoil/GpSystemServices_Win32.cpp b/Aerofoil/GpSystemServices_Win32.cpp index 97f69d8..4fbfd99 100644 --- a/Aerofoil/GpSystemServices_Win32.cpp +++ b/Aerofoil/GpSystemServices_Win32.cpp @@ -9,6 +9,26 @@ #undef CreateMutex #endif +struct GpSystemServices_Win32_ThreadStartParams +{ + GpSystemServices_Win32::ThreadFunc_t m_threadFunc; + void *m_threadContext; + PortabilityLayer::HostThreadEvent *m_threadStartEvent; +}; + +static DWORD WINAPI StaticStartThread(LPVOID lpThreadParameter) +{ + const GpSystemServices_Win32_ThreadStartParams *threadParams = static_cast(lpThreadParameter); + + GpSystemServices_Win32::ThreadFunc_t threadFunc = threadParams->m_threadFunc; + void *threadContext = threadParams->m_threadContext; + PortabilityLayer::HostThreadEvent *threadStartEvent = threadParams->m_threadStartEvent; + + threadStartEvent->Signal(); + + return threadFunc(threadContext); +} + GpSystemServices_Win32::GpSystemServices_Win32() : m_isTouchscreenSimulation(false) { @@ -67,6 +87,30 @@ PortabilityLayer::HostThreadEvent *GpSystemServices_Win32::CreateThreadEvent(boo return GpThreadEvent_Win32::Create(autoReset, startSignaled); } +void *GpSystemServices_Win32::CreateThread(ThreadFunc_t threadFunc, void *context) +{ + PortabilityLayer::HostThreadEvent *evt = CreateThreadEvent(true, false); + if (!evt) + return nullptr; + + GpSystemServices_Win32_ThreadStartParams startParams; + startParams.m_threadContext = context; + startParams.m_threadFunc = threadFunc; + startParams.m_threadStartEvent = evt; + + HANDLE threadHdl = ::CreateThread(nullptr, 0, StaticStartThread, &startParams, 0, nullptr); + if (threadHdl == nullptr) + { + evt->Destroy(); + return nullptr; + } + + evt->Wait(); + evt->Destroy(); + + return threadHdl; +} + uint64_t GpSystemServices_Win32::GetFreeMemoryCosmetic() const { MEMORYSTATUSEX memStatus; @@ -100,6 +144,13 @@ bool GpSystemServices_Win32::IsTextInputObstructive() const return false; } +unsigned int GpSystemServices_Win32::GetCPUCount() const +{ + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + return sysInfo.dwNumberOfProcessors; +} + void GpSystemServices_Win32::SetTouchscreenSimulation(bool isTouchscreenSimulation) { m_isTouchscreenSimulation = isTouchscreenSimulation; diff --git a/Aerofoil/GpSystemServices_Win32.h b/Aerofoil/GpSystemServices_Win32.h index 3528136..4d2be2c 100644 --- a/Aerofoil/GpSystemServices_Win32.h +++ b/Aerofoil/GpSystemServices_Win32.h @@ -9,6 +9,11 @@ #undef CreateMutex #endif +#pragma push_macro("CreateThread") +#ifdef CreateThread +#undef CreateThread +#endif + class GpSystemServices_Win32 final : public PortabilityLayer::HostSystemServices { @@ -19,12 +24,14 @@ public: void GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const override; PortabilityLayer::HostMutex *CreateMutex() override; PortabilityLayer::HostMutex *CreateRecursiveMutex() override; + void *CreateThread(ThreadFunc_t threadFunc, void *context) override; PortabilityLayer::HostThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) override; uint64_t GetFreeMemoryCosmetic() const override; void Beep() const override; bool IsTouchscreen() const override; bool IsUsingMouseAsTouch() const override; bool IsTextInputObstructive() const override; + unsigned int GetCPUCount() const override; void SetTouchscreenSimulation(bool isTouchscreenSimulation); @@ -37,3 +44,4 @@ private: }; #pragma pop_macro("CreateMutex") +#pragma pop_macro("CreateThread") diff --git a/AerofoilAndroid/app/jni/main/GpSystemServices_Android.cpp b/AerofoilAndroid/app/jni/main/GpSystemServices_Android.cpp index 6b5839a..a2710e0 100644 --- a/AerofoilAndroid/app/jni/main/GpSystemServices_Android.cpp +++ b/AerofoilAndroid/app/jni/main/GpSystemServices_Android.cpp @@ -1,10 +1,32 @@ #include "GpSystemServices_Android.h" #include "HostMutex.h" #include "HostThreadEvent.h" +#include "SDL.h" #include #include #include +#include + +struct GpSystemServices_Android_ThreadStartParams +{ + GpSystemServices_Android::ThreadFunc_t m_threadFunc; + void *m_threadContext; + PortabilityLayer::HostThreadEvent *m_threadStartEvent; +}; + +static int SDLCALL StaticStartThread(void *lpThreadParameter) +{ + const GpSystemServices_Android_ThreadStartParams *threadParams = static_cast(lpThreadParameter); + + GpSystemServices_Android::ThreadFunc_t threadFunc = threadParams->m_threadFunc; + void *threadContext = threadParams->m_threadContext; + PortabilityLayer::HostThreadEvent *threadStartEvent = threadParams->m_threadStartEvent; + + threadStartEvent->Signal(); + + return threadFunc(threadContext); +} template class GpMutex_Cpp11 final : public PortabilityLayer::HostMutex @@ -173,6 +195,31 @@ PortabilityLayer::HostMutex *GpSystemServices_Android::CreateMutex() return new (mutex) GpMutex_Cpp11_Vanilla(); } + +void *GpSystemServices_Android::CreateThread(ThreadFunc_t threadFunc, void *context) +{ + PortabilityLayer::HostThreadEvent *evt = CreateThreadEvent(true, false); + if (!evt) + return nullptr; + + GpSystemServices_Android_ThreadStartParams startParams; + startParams.m_threadContext = context; + startParams.m_threadFunc = threadFunc; + startParams.m_threadStartEvent = evt; + + SDL_Thread *thread = SDL_CreateThread(StaticStartThread, "WorkerThread", &startParams); + if (thread == nullptr) + { + evt->Destroy(); + return nullptr; + } + + evt->Wait(); + evt->Destroy(); + + return thread; +} + PortabilityLayer::HostMutex *GpSystemServices_Android::CreateRecursiveMutex() { GpMutex_Cpp11_Recursive *mutex = static_cast(malloc(sizeof(GpMutex_Cpp11_Recursive))); @@ -193,7 +240,9 @@ PortabilityLayer::HostThreadEvent *GpSystemServices_Android::CreateThreadEvent(b uint64_t GpSystemServices_Android::GetFreeMemoryCosmetic() const { - return 0; + long pages = sysconf(_SC_AVPHYS_PAGES); + long pageSize = sysconf(_SC_PAGE_SIZE); + return pages * pageSize; } void GpSystemServices_Android::Beep() const @@ -215,6 +264,11 @@ bool GpSystemServices_Android::IsTextInputObstructive() const return true; } +unsigned int GpSystemServices_Android::GetCPUCount() const +{ + return SDL_GetCPUCount(); +} + GpSystemServices_Android *GpSystemServices_Android::GetInstance() { return &ms_instance; diff --git a/AerofoilAndroid/app/jni/main/GpSystemServices_Android.h b/AerofoilAndroid/app/jni/main/GpSystemServices_Android.h index 8fe5ff6..efcbcee 100644 --- a/AerofoilAndroid/app/jni/main/GpSystemServices_Android.h +++ b/AerofoilAndroid/app/jni/main/GpSystemServices_Android.h @@ -12,12 +12,14 @@ public: void GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const override; PortabilityLayer::HostMutex *CreateMutex() override; PortabilityLayer::HostMutex *CreateRecursiveMutex() override; + void *CreateThread(ThreadFunc_t threadFunc, void *context) override; PortabilityLayer::HostThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) override; uint64_t GetFreeMemoryCosmetic() const override; void Beep() const override; bool IsTouchscreen() const override; bool IsUsingMouseAsTouch() const override; bool IsTextInputObstructive() const override; + unsigned int GetCPUCount() const override; static GpSystemServices_Android *GetInstance(); diff --git a/ApplicationResourcePatches/PICT/1302.bmp b/ApplicationResourcePatches/PICT/1302.bmp new file mode 100644 index 0000000..3ebb4c1 Binary files /dev/null and b/ApplicationResourcePatches/PICT/1302.bmp differ diff --git a/ApplicationResourcePatches/manifest.json b/ApplicationResourcePatches/manifest.json index 12c2ab4..f8f0a04 100644 --- a/ApplicationResourcePatches/manifest.json +++ b/ApplicationResourcePatches/manifest.json @@ -11,6 +11,7 @@ "DITL/2006.json" : "ApplicationResourcePatches/DITL/2006.json", "PICT/1300.bmp" : "ApplicationResourcePatches/PICT/1300.bmp", "PICT/1301.bmp" : "ApplicationResourcePatches/PICT/1301.bmp", + "PICT/1302.bmp" : "ApplicationResourcePatches/PICT/1302.bmp", "PICT/1971.bmp" : "ApplicationResourcePatches/PICT/1971.bmp", "PICT/1972.bmp" : "ApplicationResourcePatches/PICT/1972.bmp", "PICT/1973.bmp" : "ApplicationResourcePatches/PICT/1973.bmp", diff --git a/GpApp/Main.cpp b/GpApp/Main.cpp index 26e1a8f..caa6085 100644 --- a/GpApp/Main.cpp +++ b/GpApp/Main.cpp @@ -6,7 +6,8 @@ //============================================================================ -#include +#include "WindowDef.h" +#include "BitmapImage.h" #include "PLApplication.h" #include "PLKeyEncoding.h" #include "PLStandardColors.h" @@ -16,19 +17,30 @@ #include "FontFamily.h" #include "GpRenderedFontMetrics.h" #include "HostDisplayDriver.h" +#include "HostSystemServices.h" +#include "HostThreadEvent.h" #include "IGpDisplayDriver.h" #include "GpIOStream.h" #include "House.h" #include "MainMenuUI.h" +#include "MemoryManager.h" #include "MenuManager.h" +#include "QDPixMap.h" #include "RenderedFont.h" #include "ResolveCachingColor.h" +#include "ResourceManager.h" +#include "Utilities.h" #include "WindowManager.h" +#include "WorkerThread.h" + +#include +int loadScreenRingStep; WindowPtr loadScreenWindow; Rect loadScreenProgressBarRect; int loadScreenProgress; +DrawSurface *loadScreenRingSurface; #define kPrefsVersion 0x0038 @@ -345,6 +357,23 @@ void WriteOutPrefs (void) UnivSetSoundVolume(wasVolume, thisMac.hasSM3); } +void StepLoadScreenRing() +{ + if (loadScreenWindow) + { + const int loadScreenStepGranularity = 2; + loadScreenRingStep++; + if (loadScreenRingStep == 24 * loadScreenStepGranularity) + loadScreenRingStep = 0; + + Rect ringDestRect = Rect::Create(8, 8, 24, 24); + Rect ringSrcRect = Rect::Create(0, 0, 16, 16) + Point::Create((loadScreenRingStep / loadScreenStepGranularity) * 16, 0); + + CopyBits(*loadScreenRingSurface->m_port.GetPixMap(), *loadScreenWindow->GetDrawSurface()->m_port.GetPixMap(), &ringSrcRect, &ringDestRect, srcCopy); + loadScreenWindow->GetDrawSurface()->m_port.SetDirty(PortabilityLayer::QDPortDirtyFlag_Contents); + } +} + void StepLoadScreen(int steps) { if (loadScreenWindow) @@ -362,9 +391,15 @@ void StepLoadScreen(int steps) loadScreenWindow->GetDrawSurface()->FillRect(Rect::Create(loadScreenProgressBarRect.top, loadScreenProgressBarRect.left + prevStep, loadScreenProgressBarRect.bottom, loadScreenProgressBarRect.left + thisStep), blackColor); ForceSyncFrame(); - } - SpinCursor(steps); + for (int i = 0; i < steps; i++) + { + StepLoadScreenRing(); + Delay(1, nullptr); + } + } + else + SpinCursor(steps); } void InitLoadingWindow() @@ -374,19 +409,28 @@ void InitLoadingWindow() return; static const int kLoadScreenHeight = 32; + static const int kLoadRingResource = 1302; - int kLoadScreenWidth = 256; + int kLoadScreenWidth = 296; PLPasStr loadingText = PSTR("Loading..."); if (!isPrefsLoaded) { loadingText = PSTR("Performing First-Time Setup..."); - kLoadScreenWidth = 420; + kLoadScreenWidth = 440; } ForceSyncFrame(); PLSysCalls::Sleep(1); + THandle loadRingImageH = PortabilityLayer::ResourceManager::GetInstance()->GetAppResource('PICT', kLoadRingResource).StaticCast(); + BitmapImage *loadRingImage = *loadRingImageH; + + DrawSurface *loadRingSurface = nullptr; + Rect loadRingRect = loadRingImage->GetRect(); + CreateOffScreenGWorld(&loadRingSurface, &loadRingRect); + loadRingSurface->DrawPicture(loadRingImageH, loadRingRect); + int32_t lsX = (thisMac.fullScreen.Width() - kLoadScreenWidth) / 2; int32_t lsY = (thisMac.fullScreen.Height() - kLoadScreenHeight) / 2; @@ -409,37 +453,101 @@ void InitLoadingWindow() PortabilityLayer::RenderedFont *font = GetApplicationFont(18, PortabilityLayer::FontFamilyFlag_None, true); int32_t textY = (kLoadScreenHeight + font->GetMetrics().m_ascent) / 2; - surface->DrawString(Point::Create(4+16, textY), loadingText, blackColor, font); + surface->DrawString(Point::Create(4+16+8, textY), loadingText, blackColor, font); static const int32_t loadBarPadding = 16; - int32_t loadBarStartX = static_cast(font->MeasureString(loadingText.UChars(), loadingText.Length())) + 4 + 16 + loadBarPadding; + int32_t loadBarStartX = static_cast(font->MeasureString(loadingText.UChars(), loadingText.Length())) + 4 + 16 + 8 + loadBarPadding; int32_t loadBarEndX = loadScreenLocalRect.right - loadBarPadding; loadScreenProgressBarRect = Rect::Create((loadScreenLocalRect.Height() - 8) / 2, loadBarStartX, (loadScreenLocalRect.Height() + 8) / 2, loadBarEndX); loadScreenProgress = 0; surface->FrameRect(loadScreenProgressBarRect, blackColor); + + Rect ringDestRect = Rect::Create(8, 8, 24, 24); + Rect ringSrcRect = Rect::Create(0, 0, 16, 16); + CopyBits(*loadRingSurface->m_port.GetPixMap(), *surface->m_port.GetPixMap(), &ringSrcRect, &ringDestRect, srcCopy); + + loadRingImageH.Dispose(); + + loadScreenRingSurface = loadRingSurface; +} + +enum PreloadFontCategory +{ + FontCategory_System, + FontCategory_Application, + FontCategory_Handwriting, + FontCategory_Monospace, +}; + +struct PreloadFontSpec +{ + PreloadFontCategory m_category; + int m_size; + int m_flags; + bool m_aa; +}; + +struct PreloadFontWorkSlot +{ + PortabilityLayer::HostThreadEvent *m_completedEvent; + PortabilityLayer::WorkerThread *m_workerThread; + std::atomic m_singleJobCompleted; + const PreloadFontSpec *m_spec; + bool m_queued; + + PreloadFontWorkSlot(); + ~PreloadFontWorkSlot(); +}; + +PreloadFontWorkSlot::PreloadFontWorkSlot() + : m_completedEvent(nullptr) + , m_workerThread(nullptr) + , m_spec(nullptr) + , m_queued(false) +{ +} + +PreloadFontWorkSlot::~PreloadFontWorkSlot() +{ + if (m_workerThread) + m_workerThread->Destroy(); +} + +void PreloadSingleFont (const PreloadFontSpec &spec) +{ + switch (spec.m_category) + { + case FontCategory_Application: + GetApplicationFont(spec.m_size, spec.m_flags, spec.m_aa); + break; + case FontCategory_System: + GetSystemFont(spec.m_size, spec.m_flags, spec.m_aa); + break; + case FontCategory_Handwriting: + GetHandwritingFont(spec.m_size, spec.m_flags, spec.m_aa); + break; + case FontCategory_Monospace: + GetMonospaceFont(spec.m_size, spec.m_flags, spec.m_aa); + break; + default: + break; + } +} + +void PreloadThreadFunc(void *context) +{ + PreloadFontWorkSlot *wSlot = static_cast(context); + + PreloadSingleFont(*wSlot->m_spec); + ++wSlot->m_singleJobCompleted; + wSlot->m_completedEvent->Signal(); } void PreloadFonts() { - enum FontCategory - { - FontCategory_System, - FontCategory_Application, - FontCategory_Handwriting, - FontCategory_Monospace, - }; - - struct FontSpec - { - FontCategory m_category; - int m_size; - int m_flags; - bool m_aa; - }; - - FontSpec specs[] = + static PreloadFontSpec specs[] = { { FontCategory_System, 9, PortabilityLayer::FontFamilyFlag_Bold, true }, { FontCategory_System, 10, PortabilityLayer::FontFamilyFlag_Bold, true }, @@ -456,32 +564,51 @@ void PreloadFonts() { FontCategory_Monospace, 10, PortabilityLayer::FontFamilyFlag_None, true }, }; + PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance(); + + const int numFontSpecs = sizeof(specs) / sizeof(specs[0]); - for (int i = 0; i < numFontSpecs; i++) - { - const FontSpec &spec = specs[i]; + int queuedSpecs = 0; + int completedSpecs = 0; - switch (spec.m_category) + // We can't actually slot these because FT isn't thread-safe when accessing the same font, + // but we can do this to unclog the render thread. + PreloadFontWorkSlot slot; + slot.m_workerThread = PortabilityLayer::WorkerThread::Create(); + slot.m_completedEvent = PortabilityLayer::HostSystemServices::GetInstance()->CreateThreadEvent(true, false); + + while (completedSpecs < numFontSpecs) + { + if (slot.m_queued) { - case FontCategory_Application: - GetApplicationFont(spec.m_size, spec.m_flags, spec.m_aa); - break; - case FontCategory_System: - GetSystemFont(spec.m_size, spec.m_flags, spec.m_aa); - break; - case FontCategory_Handwriting: - GetHandwritingFont(spec.m_size, spec.m_flags, spec.m_aa); - break; - case FontCategory_Monospace: - GetMonospaceFont(spec.m_size, spec.m_flags, spec.m_aa); - break; - default: - break; + if (slot.m_singleJobCompleted.load(std::memory_order_relaxed) != 0) + { + slot.m_completedEvent->Wait(); + slot.m_queued = false; + completedSpecs++; + + StepLoadScreen(1); + } } - StepLoadScreen(1); + if (!slot.m_queued) + { + if (queuedSpecs < numFontSpecs) + { + slot.m_queued = true; + slot.m_singleJobCompleted.store(0); + slot.m_spec = specs + queuedSpecs; + slot.m_workerThread->AsyncExecuteTask(PreloadThreadFunc, &slot); + + queuedSpecs++; + } + } + + StepLoadScreenRing(); + Delay(1, nullptr); } + } void gpAppInit() @@ -556,6 +683,12 @@ int gpAppMain() PLSysCalls::Sleep(15); } + if (loadScreenRingSurface) + { + DisposeGWorld(loadScreenRingSurface); + loadScreenRingSurface = nullptr; + } + OpenMainWindow(); if (isDoColorFade) diff --git a/PortabilityLayer/Android.mk b/PortabilityLayer/Android.mk index 0c70898..3749665 100644 --- a/PortabilityLayer/Android.mk +++ b/PortabilityLayer/Android.mk @@ -105,6 +105,7 @@ LOCAL_SRC_FILES := \ UTF16.cpp \ WindowDef.cpp \ WindowManager.cpp \ + WorkerThread.cpp \ XModemCRC.cpp \ ZipFileProxy.cpp diff --git a/PortabilityLayer/HostSystemServices.h b/PortabilityLayer/HostSystemServices.h index 4268836..000eb7c 100644 --- a/PortabilityLayer/HostSystemServices.h +++ b/PortabilityLayer/HostSystemServices.h @@ -6,6 +6,10 @@ #error "CreateMutex was macrod" #endif +#ifdef CreateThread +#error "CreateThread was macrod" +#endif + namespace PortabilityLayer { class HostMutex; @@ -14,16 +18,20 @@ namespace PortabilityLayer class HostSystemServices { public: + typedef int (*ThreadFunc_t)(void *context); + virtual int64_t GetTime() const = 0; virtual void GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const = 0; virtual HostMutex *CreateMutex() = 0; virtual HostMutex *CreateRecursiveMutex() = 0; + virtual void *CreateThread(ThreadFunc_t threadFunc, void *context) = 0; virtual HostThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) = 0; virtual uint64_t GetFreeMemoryCosmetic() const = 0; // Returns free memory in bytes, does not have to be accurate virtual void Beep() const = 0; virtual bool IsTouchscreen() const = 0; virtual bool IsUsingMouseAsTouch() const = 0; virtual bool IsTextInputObstructive() const = 0; + virtual unsigned int GetCPUCount() const = 0; static void SetInstance(HostSystemServices *instance); static HostSystemServices *GetInstance(); diff --git a/PortabilityLayer/PortabilityLayer.vcxproj b/PortabilityLayer/PortabilityLayer.vcxproj index 8a2f023..110182e 100644 --- a/PortabilityLayer/PortabilityLayer.vcxproj +++ b/PortabilityLayer/PortabilityLayer.vcxproj @@ -165,6 +165,7 @@ + @@ -320,6 +321,7 @@ + diff --git a/PortabilityLayer/PortabilityLayer.vcxproj.filters b/PortabilityLayer/PortabilityLayer.vcxproj.filters index a7d7b1d..c9e2b53 100644 --- a/PortabilityLayer/PortabilityLayer.vcxproj.filters +++ b/PortabilityLayer/PortabilityLayer.vcxproj.filters @@ -462,6 +462,9 @@ Header Files + + Header Files + @@ -737,5 +740,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/PortabilityLayer/WorkerThread.cpp b/PortabilityLayer/WorkerThread.cpp new file mode 100644 index 0000000..3d6914f --- /dev/null +++ b/PortabilityLayer/WorkerThread.cpp @@ -0,0 +1,152 @@ +#include "WorkerThread.h" +#include "HostSystemServices.h" +#include "HostThreadEvent.h" + +#include +#include + +namespace PortabilityLayer +{ + class WorkerThreadImpl final : public WorkerThread + { + public: + WorkerThreadImpl(); + + bool Init(); + void Destroy() override; + + void AsyncExecuteTask(Callback_t callback, void *context) override; + + private: + ~WorkerThreadImpl() override; + + static int StaticThreadFuncThunk(void *context); + int ThreadFunc(); + + HostThreadEvent *m_wakeSignal; + HostThreadEvent *m_wakeConsumedSignal; + + Callback_t m_waitingCallback; + void *m_waitingContext; + + bool m_terminated; + bool m_threadRunning; + }; +} + +void PortabilityLayer::WorkerThreadImpl::Destroy() +{ + this->~WorkerThreadImpl(); + free(this); +} + +void PortabilityLayer::WorkerThreadImpl::AsyncExecuteTask(PortabilityLayer::WorkerThread::Callback_t callback, void *context) +{ + m_waitingCallback = callback; + m_waitingContext = context; + m_wakeSignal->Signal(); + m_wakeConsumedSignal->Wait(); +} + +PortabilityLayer::WorkerThreadImpl::WorkerThreadImpl() + : m_wakeSignal(nullptr) + , m_wakeConsumedSignal(nullptr) + , m_waitingCallback(nullptr) + , m_waitingContext(nullptr) + , m_terminated(false) + , m_threadRunning(false) +{ +} + +PortabilityLayer::WorkerThreadImpl::~WorkerThreadImpl() +{ + if (m_threadRunning) + { + m_terminated = true; + m_wakeSignal->Signal(); + m_wakeConsumedSignal->Wait(); + } + + if (m_wakeSignal) + m_wakeSignal->Destroy(); + if (m_wakeConsumedSignal) + m_wakeConsumedSignal->Destroy(); +} + + +int PortabilityLayer::WorkerThreadImpl::StaticThreadFuncThunk(void *context) +{ + return static_cast(context)->ThreadFunc(); +} + +int PortabilityLayer::WorkerThreadImpl::ThreadFunc() +{ + m_wakeSignal->Wait(); + m_wakeConsumedSignal->Signal(); + + for (;;) + { + m_wakeSignal->Wait(); + + if (m_terminated) + { + m_wakeConsumedSignal->Signal(); + return 0; + } + else + { + Callback_t callback = m_waitingCallback; + void *context = m_waitingContext; + m_wakeConsumedSignal->Signal(); + + callback(context); + } + } +} + +bool PortabilityLayer::WorkerThreadImpl::Init() +{ + HostSystemServices *sysServices = HostSystemServices::GetInstance(); + + m_wakeSignal = sysServices->CreateThreadEvent(true, false); + if (!m_wakeSignal) + return false; + + m_wakeConsumedSignal = sysServices->CreateThreadEvent(true, false); + if (!m_wakeConsumedSignal) + return false; + + if (!sysServices->CreateThread(PortabilityLayer::WorkerThreadImpl::StaticThreadFuncThunk, this)) + return false; + + m_threadRunning = true; + m_wakeSignal->Signal(); + m_wakeConsumedSignal->Wait(); + + return true; +} + + +PortabilityLayer::WorkerThread::WorkerThread() +{ +} + +PortabilityLayer::WorkerThread::~WorkerThread() +{ +} + +PortabilityLayer::WorkerThread *PortabilityLayer::WorkerThread::Create() +{ + void *storage = malloc(sizeof(PortabilityLayer::WorkerThreadImpl)); + if (!storage) + return nullptr; + + PortabilityLayer::WorkerThreadImpl *thread = new (storage) PortabilityLayer::WorkerThreadImpl(); + if (!thread->Init()) + { + thread->Destroy(); + return nullptr; + } + + return thread; +} diff --git a/PortabilityLayer/WorkerThread.h b/PortabilityLayer/WorkerThread.h new file mode 100644 index 0000000..c590f33 --- /dev/null +++ b/PortabilityLayer/WorkerThread.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace PortabilityLayer +{ + class HostThreadEvent; + + class WorkerThread + { + public: + typedef void(*Callback_t)(void *context); + + static WorkerThread *Create(); + virtual void Destroy() = 0; + + virtual void AsyncExecuteTask(Callback_t callback, void *context) = 0; + + protected: + WorkerThread(); + virtual ~WorkerThread(); + }; +}