job refactor

This commit is contained in:
jacob 2025-08-05 14:58:22 -05:00
parent 632f12dd63
commit 26bff4e741
11 changed files with 113 additions and 97 deletions

View File

@ -128,7 +128,7 @@ AppArgList ParseAppArgs(Arena *arena, String args_str)
//////////////////////////////// ////////////////////////////////
//~ @hookdef Entry point //~ @hookdef Entry point
void EntryPoint(void) void Startup(void)
{ {
__prof; __prof;
TempArena scratch = BeginScratchNoConflict(); TempArena scratch = BeginScratchNoConflict();

View File

@ -17,9 +17,7 @@ Arena *AllocArena(u64 reserve)
if (!base) if (!base)
{ {
/* Hard fail on memory reserve failure for now */ /* Hard fail on memory reserve failure for now */
/* FIXME: Enable this */ Panic(Lit("Failed to reserve memory"));
//Panic(Lit("Failed to reserve memory"));
(*(volatile int *)0) = 0;
} }
u64 reserved = reserve; u64 reserved = reserve;
AddGstat(GSTAT_MEMORY_RESERVED, reserve); AddGstat(GSTAT_MEMORY_RESERVED, reserve);
@ -29,9 +27,7 @@ Arena *AllocArena(u64 reserve)
if (!base) if (!base)
{ {
/* Hard fail on commit failure */ /* Hard fail on commit failure */
/* FIXME: Enable this */ Panic(Lit("Failed to commit initial memory block: System may be out of memory"));
//Panic(Lit("Failed to commit initial memory block: System may be out of memory"));
(*(volatile int *)0) = 0;
} }
Assert(((u64)base & 0xFFF) == 0); /* Base should be 4k aligned */ Assert(((u64)base & 0xFFF) == 0); /* Base should be 4k aligned */
@ -88,17 +84,13 @@ void *PushBytesNoZero(Arena *arena, u64 size, u64 align)
if (new_capacity > arena->reserved) if (new_capacity > arena->reserved)
{ {
/* Hard fail if we overflow reserved memory for now */ /* Hard fail if we overflow reserved memory for now */
/* FIXME: Enable this */ Panic(Lit("Failed to commit new memory block: Overflow of reserved memory"));
//Panic(Lit("Failed to commit new memory block: Overflow of reserved memory"));
(*(volatile int *)0) = 0;
} }
void *commit_address = base + arena->committed; void *commit_address = base + arena->committed;
if (!CommitMemory(commit_address, commit_bytes)) if (!CommitMemory(commit_address, commit_bytes))
{ {
/* Hard fail on memory allocation failure for now */ /* Hard fail on memory allocation failure for now */
/* FIXME: Enable this */ Panic(Lit("Failed to commit new memory block: System may be out of memory"));
//Panic(Lit("Failed to commit new memory block: System may be out of memory"));
(*(volatile int *)0) = 0;
} }
arena->committed += commit_bytes; arena->committed += commit_bytes;
AddGstat(GSTAT_MEMORY_COMMITTED, commit_bytes); AddGstat(GSTAT_MEMORY_COMMITTED, commit_bytes);

View File

@ -163,6 +163,7 @@ Inline ArenaCtx *ArenaCtxFromFiberId(i16 fiber_id)
SharedArenaCtx *shared = &shared_arena_ctx; SharedArenaCtx *shared = &shared_arena_ctx;
ArenaCtx *ctx = &shared->arena_contexts[fiber_id]; ArenaCtx *ctx = &shared->arena_contexts[fiber_id];
if (!ctx->scratch_arenas[0]) { if (!ctx->scratch_arenas[0]) {
__profn("Initialize fiber arena ctx");
for (i32 i = 0; i < (i32)countof(ctx->scratch_arenas); ++i) { for (i32 i = 0; i < (i32)countof(ctx->scratch_arenas); ++i) {
ctx->scratch_arenas[i] = AllocArena(Gibi(64)); ctx->scratch_arenas[i] = AllocArena(Gibi(64));
} }

View File

@ -12,6 +12,6 @@ void Exit(void);
void Panic(String msg); void Panic(String msg);
//////////////////////////////// ////////////////////////////////
//~ @hookdecl Application defined entry point //~ @hookdecl Application defined hooks
void EntryPoint(void); void Startup(void);

View File

@ -45,7 +45,7 @@ void FutexWait(volatile void *addr, void *cmp, u32 size, i64 timeout_ns);
void FutexWake(void *addr, i32 count); void FutexWake(void *addr, i32 count);
//////////////////////////////// ////////////////////////////////
//~ @hookdecl Job helpers //~ @hookdecl Job operations
#define EmptySig { i32 _; } #define EmptySig { i32 _; }
@ -60,6 +60,10 @@ Struct(GenericJobDesc)
JobPool pool; JobPool pool;
JobPriority priority; JobPriority priority;
Counter *counter; Counter *counter;
/* Internal */
Atomic32 num_completed;
GenericJobDesc *next_free;
}; };
Struct(JobDescParams) Struct(JobDescParams)

View File

@ -8,7 +8,7 @@ JobDef(W32_AppStartupJob, UNUSED sig, UNUSED id)
W32_SharedEntryCtx *g = &W32_shared_entry_ctx; W32_SharedEntryCtx *g = &W32_shared_entry_ctx;
TempArena scratch = BeginScratchNoConflict(); TempArena scratch = BeginScratchNoConflict();
{ {
EntryPoint(); Startup();
SetEvent(g->startup_end_event); SetEvent(g->startup_end_event);
} }
EndScratch(scratch); EndScratch(scratch);

View File

@ -1,7 +1,5 @@
W32_SharedJobCtx W32_shared_job_ctx = ZI; W32_SharedJobCtx W32_shared_job_ctx = ZI;
/* FIXME: Enable logs, panic, shutdown */
//////////////////////////////// ////////////////////////////////
//~ Win32 libs //~ Win32 libs
@ -40,6 +38,9 @@ void StartupBaseJobs(void)
g->num_fibers = 1; /* Fiber at index 0 always nil */ g->num_fibers = 1; /* Fiber at index 0 always nil */
g->fiber_names_arena = AllocArena(Gibi(64)); g->fiber_names_arena = AllocArena(Gibi(64));
/* Init job descs */
g->job_descs_arena = AllocArena(Gibi(64));
/* Convert main thread to fiber */ /* Convert main thread to fiber */
W32_AllocFiber(0); W32_AllocFiber(0);
@ -543,10 +544,8 @@ void W32_WakeLockedFibers(i32 num_fibers, W32_Fiber **fibers)
ZeroStruct(info); ZeroStruct(info);
info->count = 1; info->count = 1;
info->num_dispatched = fiber->job_id; info->num_dispatched = fiber->job_id;
info->func = fiber->job_func;
info->sig = fiber->job_sig;
info->counter = fiber->job_counter;
info->fiber_id = fiber->id; info->fiber_id = fiber->id;
info->desc = fiber->job_desc;
if (queue->first) if (queue->first)
{ {
info->next = queue->first; info->next = queue->first;
@ -798,12 +797,9 @@ W32_Fiber *W32_AllocFiber(W32_JobPool *pool)
fiber->next_addr_waiter = 0; fiber->next_addr_waiter = 0;
fiber->prev_time_waiter = 0; fiber->prev_time_waiter = 0;
fiber->next_time_waiter = 0; fiber->next_time_waiter = 0;
fiber->job_func = 0;
fiber->job_sig = 0;
fiber->job_id = 0; fiber->job_id = 0;
fiber->job_pool = 0; fiber->job_pool = 0;
fiber->job_priority = 0; fiber->job_priority = 0;
fiber->job_counter = 0;
fiber->yield_param = 0; fiber->yield_param = 0;
fiber->parent_id = 0; fiber->parent_id = 0;
return fiber; return fiber;
@ -857,33 +853,48 @@ void W32_YieldFiber(W32_Fiber *fiber, W32_Fiber *parent_fiber)
} }
} }
//- Fiber entry ////////////////////////////////
//~ Win32 fiber entry
void W32_FiberEntryPoint(void *id_ptr) void W32_FiberEntryPoint(void *id_ptr)
{ {
W32_SharedJobCtx *g = &W32_shared_job_ctx;
i16 id = (i32)(i64)id_ptr; i16 id = (i32)(i64)id_ptr;
volatile W32_Fiber *fiber = W32_FiberFromId(id); volatile W32_Fiber *fiber = W32_FiberFromId(id);
__prof_fiber_enter(fiber->name_cstr, PROF_THREAD_GROUP_FIBERS - Mebi(fiber->job_pool) + Kibi(1) + fiber->id); __prof_fiber_enter(fiber->name_cstr, PROF_THREAD_GROUP_FIBERS - Mebi(fiber->job_pool) + Kibi(1) + fiber->id);
for (;;) for (;;)
{ {
/* Run job */ //- Run job
{ {
W32_YieldParam *yield_param = fiber->yield_param; W32_YieldParam *yield_param = fiber->yield_param;
yield_param->kind = W32_YieldKind_None; yield_param->kind = W32_YieldKind_None;
{ {
MemoryBarrier(); MemoryBarrier();
GenericJobFunc *job_func = fiber->job_func; GenericJobDesc *job_desc = fiber->job_desc;
job_func(fiber->job_sig, fiber->job_id); GenericJobFunc *job_func = job_desc->func;
job_func(job_desc->sig, fiber->job_id);
MemoryBarrier(); MemoryBarrier();
} }
} }
/* Job completed, yield */ //- Job completed
{ {
/* Decrement job counter */ GenericJobDesc *job_desc = fiber->job_desc;
Counter *job_counter = fiber->job_counter; /* Check if we're the last completed dispatch in the job */
if (job_counter) if ((Atomic32FetchAdd(&job_desc->num_completed, 1) + 1) >= job_desc->count)
{ {
AddCounter(job_counter, -1); /* Decrement counter */
if (job_desc->counter)
{
AddCounter(job_desc->counter, -1);
}
/* Release job desc */
ResetArena(job_desc->arena);
LockTicketMutex(&g->job_descs_tm);
{
job_desc->next_free = g->first_free_job_desc;
g->first_free_job_desc = job_desc;
}
UnlockTicketMutex(&g->job_descs_tm);
} }
/* Yield to worker */ /* Yield to worker */
fiber->yield_param->kind = W32_YieldKind_Done; fiber->yield_param->kind = W32_YieldKind_Done;
@ -894,7 +905,7 @@ void W32_FiberEntryPoint(void *id_ptr)
} }
//////////////////////////////// ////////////////////////////////
//~ Win32 job worker //~ Win32 job worker entry
W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg) W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg)
{ {
@ -958,15 +969,13 @@ W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg)
while (!shutdown) while (!shutdown)
{ {
//- Pull job from queue //- Pull job from queue
GenericJobDesc *job_desc = 0;
JobPriority job_priority = 0; JobPriority job_priority = 0;
i16 job_fiber_id = 0; i16 job_fiber_id = 0;
i32 job_id = 0; i32 job_id = 0;
GenericJobFunc *job_func = 0;
void *job_sig = 0;
Counter *job_counter = 0;
{ {
//__profnc("Pull job", Rgb32F(0.75, 0.75, 0)); //__profnc("Pull job", Rgb32F(0.75, 0.75, 0));
for (JobPriority priority = 0; priority < (i32)countof(pool->job_queues) && !job_func; ++priority) for (JobPriority priority = 0; priority < (i32)countof(pool->job_queues) && !job_desc; ++priority)
{ {
W32_JobQueue *queue = &pool->job_queues[priority]; W32_JobQueue *queue = &pool->job_queues[priority];
if (queue) if (queue)
@ -974,7 +983,7 @@ W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg)
LockTicketMutex(&queue->lock); LockTicketMutex(&queue->lock);
{ {
W32_JobInfo *info = queue->first; W32_JobInfo *info = queue->first;
while (info && !job_func) while (info && !job_desc)
{ {
W32_JobInfo *next = info->next; W32_JobInfo *next = info->next;
b32 dequeue = 0; b32 dequeue = 0;
@ -986,9 +995,7 @@ W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg)
/* Pick job */ /* Pick job */
Atomic64FetchAdd(&pool->num_jobs_in_queue.v, -1); Atomic64FetchAdd(&pool->num_jobs_in_queue.v, -1);
job_priority = priority; job_priority = priority;
job_func = info->func; job_desc = info->desc;
job_sig = info->sig;
job_counter = info->counter;
if (job_id == (info->count - 1)) if (job_id == (info->count - 1))
{ {
/* We're picking up the last dispatch, so dequeue the job */ /* We're picking up the last dispatch, so dequeue the job */
@ -1003,9 +1010,7 @@ W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg)
job_fiber_id = info->fiber_id; job_fiber_id = info->fiber_id;
job_priority = priority; job_priority = priority;
job_id = info->num_dispatched; job_id = info->num_dispatched;
job_func = info->func; job_desc = info->desc;
job_sig = info->sig;
job_counter = info->counter;
dequeue = 1; dequeue = 1;
} }
if (dequeue) if (dequeue)
@ -1037,7 +1042,7 @@ W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg)
} }
//- Run fiber //- Run fiber
if (job_func) if (job_desc)
{ {
if (!job_fiber) if (!job_fiber)
{ {
@ -1049,12 +1054,10 @@ W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg)
__profvalue(job_fiber->id); __profvalue(job_fiber->id);
W32_YieldParam yield = ZI; W32_YieldParam yield = ZI;
job_fiber->parent_id = worker_fiber_id; job_fiber->parent_id = worker_fiber_id;
job_fiber->job_func = job_func; job_fiber->job_desc = job_desc;
job_fiber->job_sig = job_sig;
job_fiber->job_id = job_id; job_fiber->job_id = job_id;
job_fiber->job_pool = pool_kind; job_fiber->job_pool = pool_kind;
job_fiber->job_priority = job_priority; job_fiber->job_priority = job_priority;
job_fiber->job_counter = job_counter;
job_fiber->yield_param = &yield; job_fiber->yield_param = &yield;
b32 done = 0; b32 done = 0;
while (!done) while (!done)
@ -1276,7 +1279,7 @@ W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg)
} }
//////////////////////////////// ////////////////////////////////
//~ Win32 job scheduler //~ Win32 job scheduler entry
W32_ThreadDef(W32_JobSchedulerEntryFunc, UNUSED arg) W32_ThreadDef(W32_JobSchedulerEntryFunc, UNUSED arg)
{ {
@ -1398,13 +1401,29 @@ void FutexWake(void *addr, i32 count)
} }
//////////////////////////////// ////////////////////////////////
//~ @hoodef Job //~ @hoodef Job operations
GenericJobDesc *PushJobDesc_(u64 sig_size, u64 sig_align, GenericJobFunc *func, JobDescParams params) GenericJobDesc *PushJobDesc_(u64 sig_size, u64 sig_align, GenericJobFunc *func, JobDescParams params)
{ {
struct W32_SharedJobCtx *g = &W32_shared_job_ctx;
GenericJobDesc *result = 0; GenericJobDesc *result = 0;
/* FIXME: Pool arenas */ Arena *arena = 0;
Arena *arena = AllocArena(Mebi(1)); LockTicketMutex(&g->job_descs_tm);
{
if (g->first_free_job_desc)
{
result = g->first_free_job_desc;
g->first_free_job_desc = result->next_free;
arena = result->arena;
}
else
{
result = PushStructNoZero(g->job_descs_arena, GenericJobDesc);
arena = AllocArena(Mebi(4));
}
}
UnlockTicketMutex(&g->job_descs_tm);
ZeroStruct(result);
result = PushStruct(arena, GenericJobDesc); result = PushStruct(arena, GenericJobDesc);
result->arena = arena; result->arena = arena;
result->sig = PushBytes(arena, sig_size, sig_align); result->sig = PushBytes(arena, sig_size, sig_align);
@ -1424,13 +1443,11 @@ void RunJobEx(GenericJobDesc *desc)
Counter *counter = desc->counter; Counter *counter = desc->counter;
JobPool pool_kind = desc->pool; JobPool pool_kind = desc->pool;
JobPriority priority = desc->priority; JobPriority priority = desc->priority;
GenericJobFunc *func = desc->func;
void *sig = desc->sig;
if (count > 0) if (count > 0)
{ {
if (counter) if (counter)
{ {
AddCounter(counter, count); AddCounter(counter, 1);
} }
W32_Fiber *fiber = W32_FiberFromId(FiberId()); W32_Fiber *fiber = W32_FiberFromId(FiberId());
priority = ClampI32(priority, fiber->job_priority, JobPriority_Count - 1); /* A job cannot create a job with a higher priority than itself */ priority = ClampI32(priority, fiber->job_priority, JobPriority_Count - 1); /* A job cannot create a job with a higher priority than itself */
@ -1453,10 +1470,8 @@ void RunJobEx(GenericJobDesc *desc)
info = PushStructNoZero(queue->arena, W32_JobInfo); info = PushStructNoZero(queue->arena, W32_JobInfo);
} }
ZeroStruct(info); ZeroStruct(info);
info->desc = desc;
info->count = count; info->count = count;
info->func = func;
info->sig = sig;
info->counter = counter;
if (queue->last) if (queue->last)
{ {
queue->last->next = info; queue->last->next = info;

View File

@ -115,25 +115,17 @@ AlignedStruct(W32_Fiber, 64)
i16 next_time_waiter; /* 02 bytes */ i16 next_time_waiter; /* 02 bytes */
i16 prev_time_waiter; /* 02 bytes */ i16 prev_time_waiter; /* 02 bytes */
/* ---------------------------------------------------- */ /* ---------------------------------------------------- */
u8 _pad1[8]; /* 08 bytes (padding) */ GenericJobDesc *job_desc; /* 08 bytes */
/* ---------------------------------------------------- */
u8 _pad2[8]; /* 08 bytes (padding) */
/* ---------------------------------------------------- */ /* ---------------------------------------------------- */
/* -------------------- Cache line -------------------- */ /* -------------------- Cache line -------------------- */
/* ---------------------------------------------------- */ /* ---------------------------------------------------- */
GenericJobFunc *job_func; /* 08 bytes */
/* ---------------------------------------------------- */
void *job_sig; /* 08 bytes */
/* ---------------------------------------------------- */
i32 job_id; /* 04 bytes */ i32 job_id; /* 04 bytes */
i16 job_pool; /* 02 bytes */ i16 job_pool; /* 02 bytes */
i16 job_priority; /* 02 bytes */ i16 job_priority; /* 02 bytes */
/* ---------------------------------------------------- */ /* ---------------------------------------------------- */
struct Counter *job_counter; /* 08 bytes */
/* ---------------------------------------------------- */
W32_YieldParam *yield_param; /* 08 bytes */ W32_YieldParam *yield_param; /* 08 bytes */
/* ---------------------------------------------------- */ /* ---------------------------------------------------- */
u8 _pad3[24]; /* 24 bytes (padding) */ u8 _pad0[48]; /* 48 bytes (padding) */
}; };
StaticAssert(sizeof(W32_Fiber) == 128); /* Padding validation (increase if necessary) */ StaticAssert(sizeof(W32_Fiber) == 128); /* Padding validation (increase if necessary) */
@ -153,13 +145,10 @@ AlignedStruct(W32_WorkerCtx, 64)
//- Job info //- Job info
Struct(W32_JobInfo) Struct(W32_JobInfo)
{ {
i32 num_dispatched; GenericJobDesc *desc;
i32 count; i32 count;
GenericJobFunc *func; i32 num_dispatched;
void *sig;
struct Counter *counter;
i16 fiber_id; /* If the job is being resumed from a yield */ i16 fiber_id; /* If the job is being resumed from a yield */
W32_JobInfo *next; W32_JobInfo *next;
@ -236,6 +225,11 @@ Struct(W32_SharedJobCtx)
Arena *fiber_names_arena; Arena *fiber_names_arena;
W32_Fiber fibers[MaxFibers]; W32_Fiber fibers[MaxFibers];
//- Job descs
Arena *job_descs_arena;
TicketMutex job_descs_tm;
GenericJobDesc *first_free_job_desc;
//- Wait lists //- Wait lists
Atomic64Padded waiter_wake_gen; Atomic64Padded waiter_wake_gen;
TicketMutex wait_lists_arena_tm; TicketMutex wait_lists_arena_tm;
@ -284,10 +278,18 @@ void W32_ReleaseFiber(W32_JobPool *pool, W32_Fiber *fiber);
ForceInline W32_Fiber *W32_FiberFromId(i16 id); ForceInline W32_Fiber *W32_FiberFromId(i16 id);
ForceNoInline void W32_FiberResume(W32_Fiber *fiber); ForceNoInline void W32_FiberResume(W32_Fiber *fiber);
void W32_YieldFiber(W32_Fiber *fiber, W32_Fiber *parent_fiber); void W32_YieldFiber(W32_Fiber *fiber, W32_Fiber *parent_fiber);
////////////////////////////////
//~ Fiber entry
void W32_FiberEntryPoint(void *id_ptr); void W32_FiberEntryPoint(void *id_ptr);
//////////////////////////////// ////////////////////////////////
//~ Workers //~ Job worker entry
W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg); W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg);
////////////////////////////////
//~ Job scheduler entry
W32_ThreadDef(W32_JobSchedulerEntryFunc, _); W32_ThreadDef(W32_JobSchedulerEntryFunc, _);

View File

@ -326,6 +326,7 @@ void GPU_D12_InitObjects(void)
Counter counter = ZI; Counter counter = ZI;
RunJob(DX12_NUM_QUEUES, GPU_D12_AllocCommandQueueJob, JobPool_Inherit, JobPriority_Low, &counter, .descs_in = params, .cqs_out = g->command_queues); RunJob(DX12_NUM_QUEUES, GPU_D12_AllocCommandQueueJob, JobPool_Inherit, JobPriority_Low, &counter, .descs_in = params, .cqs_out = g->command_queues);
WaitOnCounter(&counter); WaitOnCounter(&counter);
DEBUGBREAKABLE;
} }
#if ProfilingIsEnabled #if ProfilingIsEnabled
{ {
@ -3283,6 +3284,8 @@ void GPU_PresentSwapchain(GPU_Swapchain *gp_swapchain, Vec2I32 backbuffer_resolu
} }
#if ProfilingGpu #if ProfilingGpu
{
GPU_D12_SharedState *g = &GPU_D12_shared_state;
{ {
__profframe(0); __profframe(0);
@ -3306,6 +3309,7 @@ void GPU_PresentSwapchain(GPU_Swapchain *gp_swapchain, Vec2I32 backbuffer_resolu
__prof_dx12_collect(cq->prof); __prof_dx12_collect(cq->prof);
} }
} }
}
#endif #endif
} }

View File

@ -6,16 +6,13 @@ RES_SharedState RES_shared_state = ZI;
void RES_StartupCore(void) void RES_StartupCore(void)
{ {
__prof; __prof;
RES_SharedState *g = &RES_shared_state;
g->arena = AllocArena(Gibi(64));
#if RESOURCES_EMBEDDED #if RESOURCES_EMBEDDED
String embedded_data = INC_GetResTar(); String embedded_data = INC_GetResTar();
if (embedded_data.len <= 0) if (embedded_data.len <= 0)
{ {
Panic(Lit("No embedded resources found")); Panic(Lit("No embedded resources found"));
} }
g->archive = TAR_ArchiveFromString(g->arena, embedded_data, Lit("")); g->archive = TAR_ArchiveFromString(GetPermArena(), embedded_data, Lit(""));
#else #else
/* Ensure we have the right working directory */ /* Ensure we have the right working directory */
if (!P_IsDir(Lit("res"))) if (!P_IsDir(Lit("res")))

View File

@ -22,9 +22,10 @@ Struct(RES_Resource)
Struct(RES_SharedState) Struct(RES_SharedState)
{ {
Arena *arena;
#if RESOURCES_EMBEDDED #if RESOURCES_EMBEDDED
TAR_Archive archive; TAR_Archive archive;
#else
i32 _;
#endif #endif
}; };