//////////////////////////////// //~ Job queue types /* Work pools contain their own worker threads with their own thread priority * affinity based on the intended context of the pool. */ typedef i32 JobPool; enum { JobPool_Inherit = -1, /* The floating pool contains a large number of lower priority worker * threads that have affinity over the entire CPU. Other pools should push * jobs that only block and do no work here so that they can yield on the * blocking job rather than blocking themselves. */ JobPool_Floating = 0, JobPool_Background = 1, JobPool_Audio = 2, JobPool_User = 3, JobPool_Sim = 4, JobPool_Count }; /* Job execution order within a pool is based on priority. */ typedef i32 JobPriority; enum { JobPriority_Inherit = -1, JobPriority_High = 0, JobPriority_Normal = 1, JobPriority_Low = 2, JobPriority_Count }; //////////////////////////////// //~ @hookdecl Startup void StartupBaseJobs(void); //////////////////////////////// //~ @hookdecl Futex /* Futex-like wait & wake */ void FutexWait(volatile void *addr, void *cmp, u32 size, i64 timeout_ns); void FutexWake(void *addr, i32 count); //////////////////////////////// //~ @hookdecl Job operations #define EmptySig { i32 _; } typedef void GenericJobFunc(void *, i32); Struct(GenericJobDesc) { Arena *arena; void *sig; GenericJobFunc *func; i32 count; JobPool pool; JobPriority priority; Counter *counter; /* Internal */ Atomic32 num_completed; GenericJobDesc *next_free; }; Struct(JobDescParams) { i32 count; JobPool pool; JobPriority priority; Counter *counter; }; #define JobDecl(job, sigdef) \ typedef struct job##_Sig sigdef job##_Sig; \ Struct(job##_Desc) { Arena *arena; job##_Sig *sig; GenericJobFunc *func; i32 count; JobPool pool; JobPriority priority; Counter *counter; }; \ void job(job##_Sig *, i32); \ inline void job##_Generic(void *sig, i32 id) { job((job##_Sig *)sig, id); } \ StaticAssert(1) #define PushJobDesc(job, ...) (job##_Desc *)PushJobDesc_(sizeof(job##_Sig), alignof(job##_Sig), job##_Generic, (JobDescParams) { .count = 1, .pool = JobPool_Inherit, .priority = JobPriority_Inherit, .counter = 0, __VA_ARGS__ }) GenericJobDesc *PushJobDesc_(u64 sig_size, u64 sig_align, GenericJobFunc *func, JobDescParams params); #define JobDef(job, sig_arg, id_arg) void job(job##_Sig *sig_arg, i32 id_arg) #define RunJob(_count, job, _pool, _priority, _counter, ...) do { \ job##_Desc *__job_desc = (job##_Desc *)PushJobDesc_(sizeof(job##_Sig), alignof(job##_Sig), job##_Generic, (JobDescParams) { .count = _count, .pool = _pool, .priority = _priority, .counter = _counter,}); \ *__job_desc->sig = (job##_Sig) { __VA_ARGS__ }; \ RunJobEx((GenericJobDesc *)__job_desc); \ } while (0) void RunJobEx(GenericJobDesc *desc); //////////////////////////////// //~ @hookdecl Helpers i64 TimeNs(void); u32 ThreadId(void); u32 GetLogicalProcessorCount(void); i64 GetCurrentSchedulerPeriodNs(void);