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
|
#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");
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue