From 475b8d21fb832e540b6e1046fcbeffdf7543e40a Mon Sep 17 00:00:00 2001 From: elasota Date: Sun, 1 Nov 2020 20:59:52 -0500 Subject: [PATCH] Improve load screen responsiveness --- Aerofoil/GpSystemServices_Win32.cpp | 51 ++++ Aerofoil/GpSystemServices_Win32.h | 8 + .../app/jni/main/GpSystemServices_Android.cpp | 56 ++++- .../app/jni/main/GpSystemServices_Android.h | 2 + ApplicationResourcePatches/PICT/1302.bmp | Bin 0 -> 18486 bytes ApplicationResourcePatches/manifest.json | 1 + GpApp/Main.cpp | 219 ++++++++++++++---- PortabilityLayer/Android.mk | 1 + PortabilityLayer/HostSystemServices.h | 8 + PortabilityLayer/PortabilityLayer.vcxproj | 2 + .../PortabilityLayer.vcxproj.filters | 6 + PortabilityLayer/WorkerThread.cpp | 152 ++++++++++++ PortabilityLayer/WorkerThread.h | 23 ++ 13 files changed, 485 insertions(+), 44 deletions(-) create mode 100644 ApplicationResourcePatches/PICT/1302.bmp create mode 100644 PortabilityLayer/WorkerThread.cpp create mode 100644 PortabilityLayer/WorkerThread.h 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 0000000000000000000000000000000000000000..3ebb4c1b717140da0ef57c3f141f07b6422375fb GIT binary patch literal 18486 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 z +#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(); + }; +}