implemented an abstraction layer for threads so application code can run multithreaded code wihtout worrying about the platform
This commit is contained in:
parent
9d1809b5e2
commit
b1d745aa1f
|
@ -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");
|
||||
|
|
|
@ -28,6 +28,8 @@ struct blumen_lumen_state
|
|||
{
|
||||
packet_ringbuffer MicPacketBuffer;
|
||||
temp_job_req JobReq;
|
||||
|
||||
platform_thread_handle MicListenThread;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#include "foldhaus_platform.h"
|
||||
#include "foldhaus_app.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal void
|
||||
ClearAndPushPatterns(animation_pattern_array* Patterns)
|
||||
{
|
||||
|
|
|
@ -206,6 +206,8 @@ struct context
|
|||
update_and_render* UpdateAndRender;
|
||||
cleanup_application* CleanupApplication;
|
||||
|
||||
platform_thread_manager* ThreadManager;
|
||||
|
||||
// Platform Services
|
||||
gs_work_queue* GeneralWorkQueue;
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue