implemented an abstraction layer for threads so application code can run multithreaded code wihtout worrying about the platform

This commit is contained in:
PS 2021-01-24 14:49:38 -08:00
parent 9d1809b5e2
commit b1d745aa1f
8 changed files with 189 additions and 9 deletions

View File

@ -5,9 +5,31 @@
// //
#ifndef BLUMEN_LUMEN_CPP #ifndef BLUMEN_LUMEN_CPP
// TODO(pjs): internal void
// - need to request opening a network port and getting messages from it pumped into CustomUpdate 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 internal gs_data
BlumenLumen_CustomInit(app_state* State, context Context) 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)); Result = PushSizeToData(&State->Permanent, sizeof(blumen_lumen_state));
blumen_lumen_state* BLState = (blumen_lumen_state*)Result.Memory; 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 #if 1
gs_const_string SculpturePath = ConstString("data/test_blumen.fold"); gs_const_string SculpturePath = ConstString("data/test_blumen.fold");

View File

@ -28,6 +28,8 @@ struct blumen_lumen_state
{ {
packet_ringbuffer MicPacketBuffer; packet_ringbuffer MicPacketBuffer;
temp_job_req JobReq; temp_job_req JobReq;
platform_thread_handle MicListenThread;
}; };

View File

@ -8,8 +8,6 @@
#include "foldhaus_platform.h" #include "foldhaus_platform.h"
#include "foldhaus_app.h" #include "foldhaus_app.h"
////////////////////////////////////////////////////////////////////////
internal void internal void
ClearAndPushPatterns(animation_pattern_array* Patterns) ClearAndPushPatterns(animation_pattern_array* Patterns)
{ {

View File

@ -206,6 +206,8 @@ struct context
update_and_render* UpdateAndRender; update_and_render* UpdateAndRender;
cleanup_application* CleanupApplication; cleanup_application* CleanupApplication;
platform_thread_manager* ThreadManager;
// Platform Services // Platform Services
gs_work_queue* GeneralWorkQueue; gs_work_queue* GeneralWorkQueue;

View File

@ -493,6 +493,9 @@ WinMain (
Context.PlatformGetFontInfo = Win32GetFontInfo; Context.PlatformGetFontInfo = Win32GetFontInfo;
Context.PlatformDrawFontCodepoint = Win32DrawFontCodepoint; Context.PlatformDrawFontCodepoint = Win32DrawFontCodepoint;
Context.ThreadManager = PushStruct(&PlatformPermanent, platform_thread_manager);
*Context.ThreadManager = CreatePlatformThreadManager(Win32CreateThread, Win32KillThread);
win32_dll_refresh DLLRefresh = InitializeDLLHotReloading(DLLName, WorkingDLLName, DLLLockFileName); win32_dll_refresh DLLRefresh = InitializeDLLHotReloading(DLLName, WorkingDLLName, DLLLockFileName);
if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true)) { return -1; } if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true)) { return -1; }

View File

@ -163,6 +163,48 @@ WorkerThreadProc (LPVOID InputThreadInfo)
return 0; 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 internal void
Win32WorkQueue_Init(gs_memory_arena* Arena, u32 ThreadCount) Win32WorkQueue_Init(gs_memory_arena* Arena, u32 ThreadCount)
{ {

View File

@ -3271,6 +3271,80 @@ TimeHandlerGetSecondsElapsed(gs_time_handler TimeHandler, s64 StartCycles, s64 E
return Result; 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 // Hashes

View File

@ -1005,16 +1005,52 @@ struct gs_thread_context
gs_memory_arena* Transient; 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; typedef struct gs_work_queue gs_work_queue;
struct gs_worker_thread typedef struct gs_worker_thread
{ {
gs_thread_context Context; gs_thread_context Context;
gs_work_queue* Queue; gs_work_queue* Queue;
b32 ShouldExit; b32 ShouldExit;
}; } gs_worker_thread;
#define THREAD_PROC(name) void name(gs_thread_context Context, gs_data Data) #define THREAD_PROC(name) void name(gs_thread_context Context, gs_data Data)
typedef THREAD_PROC(thread_proc); typedef THREAD_PROC(thread_proc);