From b1d745aa1fd4cc77be03f5bcc3bd7479c6f3d04e Mon Sep 17 00:00:00 2001 From: PS Date: Sun, 24 Jan 2021 14:49:38 -0800 Subject: [PATCH] implemented an abstraction layer for threads so application code can run multithreaded code wihtout worrying about the platform --- src/app/blumen_lumen.cpp | 31 +++++++- src/app/blumen_lumen.h | 2 + src/app/foldhaus_app.cpp | 2 - src/app/foldhaus_platform.h | 2 + src/app/platform_win32/win32_foldhaus.cpp | 3 + .../win32_foldhaus_work_queue.h | 42 +++++++++++ src/gs_libs/gs_types.cpp | 74 +++++++++++++++++++ src/gs_libs/gs_types.h | 42 ++++++++++- 8 files changed, 189 insertions(+), 9 deletions(-) diff --git a/src/app/blumen_lumen.cpp b/src/app/blumen_lumen.cpp index ccc04c1..e58414b 100644 --- a/src/app/blumen_lumen.cpp +++ b/src/app/blumen_lumen.cpp @@ -5,9 +5,31 @@ // #ifndef BLUMEN_LUMEN_CPP -// TODO(pjs): -// - need to request opening a network port and getting messages from it pumped into CustomUpdate -// +internal void +BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData) +{ + packet_ringbuffer* MicPacketBuffer = (packet_ringbuffer*)UserData; + +#if 0 + platform_socket* ListenSocket = CreateSocketAndConnect(Ctx->SocketManager, "127.0.0.1", "20185"); + + while (true) + { + gs_data Data = SocketReceive(Ctx->SocketManager, &ListenSocket, Ctx->Transient); + if (Data.Size > 0) + { + OutputDebugStringA("Listened"); + MicPacketBuffer->Values[MicPacketBuffer->WriteHead++] = Data; + if (MicPacketBuffer->WriteHead >= PACKETS_MAX) + { + MicPacketBuffer->WriteHead = 0; + } + } + } + + CloseSocket(Ctx->SocketManager, ListenSocket); +#endif +} internal gs_data BlumenLumen_CustomInit(app_state* State, context Context) @@ -21,7 +43,8 @@ BlumenLumen_CustomInit(app_state* State, context Context) Result = PushSizeToData(&State->Permanent, sizeof(blumen_lumen_state)); blumen_lumen_state* BLState = (blumen_lumen_state*)Result.Memory; - BLState->JobReq.Memory = (u8*)&BLState->MicPacketBuffer; + BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicPacketBuffer); + #if 1 gs_const_string SculpturePath = ConstString("data/test_blumen.fold"); diff --git a/src/app/blumen_lumen.h b/src/app/blumen_lumen.h index 25220bb..74631d2 100644 --- a/src/app/blumen_lumen.h +++ b/src/app/blumen_lumen.h @@ -28,6 +28,8 @@ struct blumen_lumen_state { packet_ringbuffer MicPacketBuffer; temp_job_req JobReq; + + platform_thread_handle MicListenThread; }; diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 7bec2d8..12d5bfd 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -8,8 +8,6 @@ #include "foldhaus_platform.h" #include "foldhaus_app.h" -//////////////////////////////////////////////////////////////////////// - internal void ClearAndPushPatterns(animation_pattern_array* Patterns) { diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h index 459c1af..68762b6 100644 --- a/src/app/foldhaus_platform.h +++ b/src/app/foldhaus_platform.h @@ -206,6 +206,8 @@ struct context update_and_render* UpdateAndRender; cleanup_application* CleanupApplication; + platform_thread_manager* ThreadManager; + // Platform Services gs_work_queue* GeneralWorkQueue; diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index 9fec14f..0382f6d 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -493,6 +493,9 @@ WinMain ( Context.PlatformGetFontInfo = Win32GetFontInfo; Context.PlatformDrawFontCodepoint = Win32DrawFontCodepoint; + Context.ThreadManager = PushStruct(&PlatformPermanent, platform_thread_manager); + *Context.ThreadManager = CreatePlatformThreadManager(Win32CreateThread, Win32KillThread); + win32_dll_refresh DLLRefresh = InitializeDLLHotReloading(DLLName, WorkingDLLName, DLLLockFileName); if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true)) { return -1; } diff --git a/src/app/platform_win32/win32_foldhaus_work_queue.h b/src/app/platform_win32/win32_foldhaus_work_queue.h index 2388798..8fa0311 100644 --- a/src/app/platform_win32/win32_foldhaus_work_queue.h +++ b/src/app/platform_win32/win32_foldhaus_work_queue.h @@ -163,6 +163,48 @@ WorkerThreadProc (LPVOID InputThreadInfo) return 0; } +DWORD WINAPI +Win32ThreadProcWrapper(LPVOID ThreadInfo) +{ + platform_thread* Thread = (platform_thread*)ThreadInfo; + gs_thread_context Ctx = Win32CreateThreadContext(); + Thread->Proc(&Ctx, Thread->UserData); + + // TODO(pjs): Destroy Thread Context + // TODO(pjs): How do we notify the thread manager this thread belongs to that it is free? + // Probaby put a pointer to the thread manager in the platform_thread struct + // so we can update the tracking structure? + + return 0; +} + +CREATE_THREAD(Win32CreateThread) +{ + Thread->Proc = Proc; + Thread->UserData = UserData; + + // TODO(pjs): ugh, allocation out in the middle of nowhere + HANDLE* ThreadHandle = (HANDLE*)Win32Alloc(sizeof(HANDLE), 0); + *ThreadHandle = CreateThread(0, 0, Win32ThreadProcWrapper, (void*)Thread, 0, 0); + // TODO(pjs): Error checking on the created thread + + Thread->PlatformHandle = (u8*)ThreadHandle; + + return true; +} + +KILL_THREAD(Win32KillThread) +{ + HANDLE* ThreadHandle = (HANDLE*)Thread->PlatformHandle; + TerminateThread(ThreadHandle, 0); + + // TODO(pjs): see allocation out in the middle of nowhere in Win32CreateThread + Win32Free((void*)Thread->PlatformHandle, sizeof(HANDLE)); + + // TODO(pjs): Error checking + return true; +} + internal void Win32WorkQueue_Init(gs_memory_arena* Arena, u32 ThreadCount) { diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index fd40b0f..d1e6916 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -3271,6 +3271,80 @@ TimeHandlerGetSecondsElapsed(gs_time_handler TimeHandler, s64 StartCycles, s64 E return Result; } +////////////////////////// +// +// Thread Manager + +CREATE_THREAD(CreateThreadStub) +{ + return {}; +} + + +KILL_THREAD(KillThreadStub) +{ + return false; +} + +internal platform_thread_manager +CreatePlatformThreadManager(platform_create_thread* CreateThreadProc, + platform_kill_thread* KillThreadProc) +{ + platform_thread_manager Result = {}; + Result.CreateThreadProc = CreateThreadProc; + Result.KillThreadProc = KillThreadProc; + + if (!CreateThreadProc) + { + Result.CreateThreadProc = CreateThreadStub; + } + if (!KillThreadProc) + { + Result.KillThreadProc = KillThreadStub; + } + + return Result; +} + +internal platform_thread_handle +CreateThread(platform_thread_manager* Manager, thread_proc_* Proc, u8* Arg) +{ + platform_thread_handle Result = {}; + + for (u32 i = 1; i < THREADS_MAX; i++) + { + if (!Manager->ThreadsUsed[i]) + { + Result.Index = i; + break; + } + } + Assert(Result.Index != 0); + + Manager->ThreadsUsed[Result.Index] = true; + Manager->CreateThreadProc(&Manager->Threads[Result.Index], Proc, Arg); + + return Result; +} + +internal bool +KillThread(platform_thread_manager* Manager, platform_thread_handle Handle) +{ + Assert(Manager->ThreadsUsed[Handle.Index]); + + platform_thread* Thread = &Manager->Threads[Handle.Index]; + bool Result = Manager->KillThreadProc(Thread); + + if (Result) + { + Manager->ThreadsUsed[Handle.Index] = false; + Manager->Threads[Handle.Index] = {}; + } + + return Result; +} + + /////////////////////////// // // Hashes diff --git a/src/gs_libs/gs_types.h b/src/gs_libs/gs_types.h index c9cfdbe..8bd7ac8 100644 --- a/src/gs_libs/gs_types.h +++ b/src/gs_libs/gs_types.h @@ -1005,16 +1005,52 @@ struct gs_thread_context gs_memory_arena* Transient; }; -// Threads & Work Queue +// Threads + +typedef struct platform_thread_handle +{ + u32 Index; +} platform_thread_handle; + +typedef struct platform_thread_manager platform_thread_manager; + +#define THREAD_PROC_(name) void name(gs_thread_context* Ctx, u8* UserData) +typedef THREAD_PROC_(thread_proc_); + +typedef struct platform_thread +{ + u8* PlatformHandle; + thread_proc_* Proc; + u8* UserData; + // TODO(pjs): Some kind of platform thread handle +} platform_thread; + +#define CREATE_THREAD(name) bool name(platform_thread* Thread, thread_proc_* Proc, u8* UserData) +typedef CREATE_THREAD(platform_create_thread); + +#define KILL_THREAD(name) bool name(platform_thread* Thread) +typedef KILL_THREAD(platform_kill_thread); + +#define THREADS_MAX 32 +typedef struct platform_thread_manager +{ + b8 ThreadsUsed[THREADS_MAX]; + platform_thread Threads[THREADS_MAX]; + + platform_create_thread* CreateThreadProc; + platform_kill_thread* KillThreadProc; +} platform_thread_manager; + +// Work Queue typedef struct gs_work_queue gs_work_queue; -struct gs_worker_thread +typedef struct gs_worker_thread { gs_thread_context Context; gs_work_queue* Queue; b32 ShouldExit; -}; +} gs_worker_thread; #define THREAD_PROC(name) void name(gs_thread_context Context, gs_data Data) typedef THREAD_PROC(thread_proc);