diff --git a/src/raddbg/raddbg.c b/src/raddbg/raddbg.c index b23b5993..ac1017a7 100644 --- a/src/raddbg/raddbg.c +++ b/src/raddbg/raddbg.c @@ -347,6 +347,7 @@ entry_point(int argc, char **argv) //- rjf: initialize basic dependencies os_init(argc, argv); + ts_init(); //- rjf: parse command line arguments CmdLine cmdln = cmd_line_from_string_list(scratch.arena, os_get_command_line_arguments()); diff --git a/src/raddbg/raddbg_main.cpp b/src/raddbg/raddbg_main.cpp index 6bb32ceb..b8db5c55 100644 --- a/src/raddbg/raddbg_main.cpp +++ b/src/raddbg/raddbg_main.cpp @@ -13,6 +13,7 @@ //- rjf: [h] #include "base/base_inc.h" #include "os/os_inc.h" +#include "task_system/task_system.h" #include "raddbgi_make_local/raddbgi_make_local.h" #include "mdesk/mdesk.h" #include "hash_store/hash_store.h" @@ -50,6 +51,7 @@ //- rjf: [c] #include "base/base_inc.c" #include "os/os_inc.c" +#include "task_system/task_system.c" #include "raddbgi_make_local/raddbgi_make_local.c" #include "mdesk/mdesk.c" #include "hash_store/hash_store.c" diff --git a/src/raddbgi_from_pdb/raddbgi_from_pdb.c b/src/raddbgi_from_pdb/raddbgi_from_pdb.c index 73d77f26..29bd8833 100644 --- a/src/raddbgi_from_pdb/raddbgi_from_pdb.c +++ b/src/raddbgi_from_pdb/raddbgi_from_pdb.c @@ -540,30 +540,42 @@ p2r_location_over_lvar_addr_range(Arena *arena, RDIM_ScopeChunkList *scopes, RDI } //////////////////////////////// -//~ rjf: Initial PDB Parsing Pass Threads +//~ rjf: Initial Parsing & Preparation Pass Tasks -internal void -p2r_tpi_hash_parse_thread__entry_point(void *p) +internal void * +p2r_exe_hash_task__entry_point(Arena *arena, void *p) { - ThreadName("[p2r] tpi hash parse thread"); - P2R_TPIHashParseTask *task = (P2R_TPIHashParseTask *)p; - task->out = pdb_tpi_hash_from_data(task->out_arena, task->in.strtbl, task->in.tpi, task->in.hash_data, task->in.aux_data); + P2R_EXEHashIn *in = (P2R_EXEHashIn *)p; + U64 *out = push_array(arena, U64, 1); + ProfScope("hash exe") *out = rdi_hash(in->exe_data.str, in->exe_data.size); + return out; } -internal void -p2r_tpi_leaf_parse_thread__entry_point(void *p) +internal void * +p2r_tpi_hash_parse_task__entry_point(Arena *arena, void *p) { - ThreadName("[p2r] tpi leaf parse thread"); - P2R_TPILeafParseTask *task = (P2R_TPILeafParseTask *)p; - task->out = cv_leaf_from_data(task->out_arena, task->in.leaf_data, task->in.itype_first); + P2R_TPIHashParseIn *in = (P2R_TPIHashParseIn *)p; + void *out = 0; + ProfScope("parse tpi hash") out = pdb_tpi_hash_from_data(arena, in->strtbl, in->tpi, in->hash_data, in->aux_data); + return out; } -internal void -p2r_exe_hash_thread__entry_point(void *p) +internal void * +p2r_tpi_leaf_parse_task__entry_point(Arena *arena, void *p) { - ThreadName("[p2r] exe hash thread"); - P2R_EXEHashTask *task = (P2R_EXEHashTask *)p; - ProfScope("hash exe") task->out = rdi_hash(task->in.exe_data.str, task->in.exe_data.size); + P2R_TPILeafParseIn *in = (P2R_TPILeafParseIn *)p; + void *out = 0; + ProfScope("parse tpi leaf") out = cv_leaf_from_data(arena, in->leaf_data, in->itype_first); + return out; +} + +internal void * +p2r_symbol_stream_parse_task__entry_point(Arena *arena, void *p) +{ + P2R_SymbolStreamParseIn *in = (P2R_SymbolStreamParseIn *)p; + void *out = 0; + ProfScope("parse symbol stream") out = cv_sym_from_data(arena, in->data, 4); + return out; } //////////////////////////////// @@ -684,7 +696,7 @@ p2r_itype_fwd_map_fill(P2R_ITypeFwdMapFillIn *in) //- rjf: if the forwarded itype is nonzero & in TPI range -> save to map if(itype_fwd != 0 && itype_fwd < in->tpi_leaf->itype_opl) { - in->itype_fwd_map[itype-in->itype_first] = itype_fwd; + in->itype_fwd_map[itype] = itype_fwd; } } } @@ -1557,79 +1569,46 @@ p2r_convert(Arena *arena, P2R_ConvertIn *in) CV_LeafParsed *ipi_leaf = 0; ProfScope("do independent parsing & preparation passess") { - //- rjf: kick off exe hash - OS_Handle exe_hash_thread = {0}; - P2R_EXEHashTask exe_hash_task = {0}; + //- rjf: form task inputs + P2R_EXEHashIn exe_hash_in = {in->input_exe_data}; + P2R_TPIHashParseIn tpi_hash_in = {0}; { - exe_hash_task.in.exe_data = in->input_exe_data; - exe_hash_thread = os_launch_thread(p2r_exe_hash_thread__entry_point, &exe_hash_task, 0); + tpi_hash_in.strtbl = strtbl; + tpi_hash_in.tpi = tpi; + tpi_hash_in.hash_data = msf_data_from_stream(msf, tpi->hash_sn); + tpi_hash_in.aux_data = msf_data_from_stream(msf, tpi->hash_sn_aux); + } + P2R_TPILeafParseIn tpi_leaf_in = {0}; + { + tpi_leaf_in.leaf_data = pdb_leaf_data_from_tpi(tpi); + tpi_leaf_in.itype_first = tpi->itype_first; + } + P2R_TPIHashParseIn ipi_hash_in = {0}; + { + ipi_hash_in.strtbl = strtbl; + ipi_hash_in.tpi = ipi; + ipi_hash_in.hash_data = msf_data_from_stream(msf, ipi->hash_sn); + ipi_hash_in.aux_data = msf_data_from_stream(msf, ipi->hash_sn_aux); + } + P2R_TPILeafParseIn ipi_leaf_in = {0}; + { + ipi_leaf_in.leaf_data = pdb_leaf_data_from_tpi(ipi); + ipi_leaf_in.itype_first = ipi->itype_first; } - //- rjf: kick off tpi hash parse - OS_Handle tpi_hash_thread = {0}; - P2R_TPIHashParseTask tpi_hash_task = {0}; - if(tpi != 0) - { - tpi_hash_task.in.strtbl = strtbl; - tpi_hash_task.in.tpi = tpi; - tpi_hash_task.in.hash_data = msf_data_from_stream(msf, tpi->hash_sn); - tpi_hash_task.in.aux_data = msf_data_from_stream(msf, tpi->hash_sn_aux); - tpi_hash_task.out_arena = arena_alloc(); - tpi_hash_thread = os_launch_thread(p2r_tpi_hash_parse_thread__entry_point, &tpi_hash_task, 0); - } + //- rjf: kick off tasks + TS_Ticket exe_hash_ticket = ts_kickoff(p2r_exe_hash_task__entry_point, &exe_hash_in); + TS_Ticket tpi_hash_ticket = ts_kickoff(p2r_tpi_hash_parse_task__entry_point, &tpi_hash_in); + TS_Ticket tpi_leaf_ticket = ts_kickoff(p2r_tpi_leaf_parse_task__entry_point, &tpi_leaf_in); + TS_Ticket ipi_hash_ticket = ts_kickoff(p2r_tpi_hash_parse_task__entry_point, &ipi_hash_in); + TS_Ticket ipi_leaf_ticket = ts_kickoff(p2r_tpi_leaf_parse_task__entry_point, &ipi_leaf_in); - //- rjf: kick off tpi leaf parse - OS_Handle tpi_leaf_thread = {0}; - P2R_TPILeafParseTask tpi_leaf_task = {0}; - if(tpi != 0) - { - tpi_leaf_task.in.leaf_data = pdb_leaf_data_from_tpi(tpi); - tpi_leaf_task.in.itype_first = tpi->itype_first; - tpi_leaf_task.out_arena = arena_alloc(); - tpi_leaf_thread = os_launch_thread(p2r_tpi_leaf_parse_thread__entry_point, &tpi_leaf_task, 0); - } - - //- rjf: kick off ipi hash parse - OS_Handle ipi_hash_thread = {0}; - P2R_TPIHashParseTask ipi_hash_task = {0}; - if(ipi != 0) - { - ipi_hash_task.in.strtbl = strtbl; - ipi_hash_task.in.tpi = ipi; - ipi_hash_task.in.hash_data = msf_data_from_stream(msf, ipi->hash_sn); - ipi_hash_task.in.aux_data = msf_data_from_stream(msf, ipi->hash_sn_aux); - ipi_hash_task.out_arena = arena_alloc(); - ipi_hash_thread = os_launch_thread(p2r_tpi_hash_parse_thread__entry_point, &ipi_hash_task, 0); - } - - //- rjf: kick off ipi leaf parse - OS_Handle ipi_leaf_thread = {0}; - P2R_TPILeafParseTask ipi_leaf_task = {0}; - if(ipi != 0) - { - ipi_leaf_task.in.leaf_data = pdb_leaf_data_from_tpi(ipi); - ipi_leaf_task.in.itype_first = ipi->itype_first; - ipi_leaf_task.out_arena = arena_alloc(); - ipi_leaf_thread = os_launch_thread(p2r_tpi_leaf_parse_thread__entry_point, &ipi_leaf_task, 0); - } - - //- rjf: join all independent task threads - os_thread_wait(exe_hash_thread, max_U64); - os_thread_wait(tpi_hash_thread, max_U64); - os_thread_wait(tpi_leaf_thread, max_U64); - os_thread_wait(ipi_hash_thread, max_U64); - os_thread_wait(ipi_leaf_thread, max_U64); - - //- rjf: fill/absorb exports from completed tasks - exe_hash = exe_hash_task.out; - tpi_hash = tpi_hash_task.out; - tpi_leaf = tpi_leaf_task.out; - ipi_hash = ipi_hash_task.out; - ipi_leaf = ipi_leaf_task.out; - arena_absorb(arena, tpi_hash_task.out_arena); - arena_absorb(arena, tpi_leaf_task.out_arena); - arena_absorb(arena, ipi_hash_task.out_arena); - arena_absorb(arena, ipi_leaf_task.out_arena); + //- rjf: join tasks + exe_hash = *ts_join_struct(exe_hash_ticket, max_U64, U64); + tpi_hash = ts_join_struct(tpi_hash_ticket, max_U64, PDB_TpiHashParsed); + tpi_leaf = ts_join_struct(tpi_leaf_ticket, max_U64, CV_LeafParsed); + ipi_hash = ts_join_struct(ipi_hash_ticket, max_U64, PDB_TpiHashParsed); + ipi_leaf = ts_join_struct(ipi_leaf_ticket, max_U64, CV_LeafParsed); } ////////////////////////////////////////////////////////////// @@ -1915,6 +1894,7 @@ p2r_convert(Arena *arena, P2R_ConvertIn *in) task->fill_in.tpi_leaf = tpi_leaf; task->fill_in.itype_first = task_idx*task_size_itypes; task->fill_in.itype_opl = task->fill_in.itype_first + task_size_itypes; + task->fill_in.itype_opl = ClampTop(task->fill_in.itype_opl, itype_opl); task->fill_in.itype_fwd_map = itype_fwd_map; } } diff --git a/src/raddbgi_from_pdb/raddbgi_from_pdb.h b/src/raddbgi_from_pdb/raddbgi_from_pdb.h index 8749d49f..b3b88f00 100644 --- a/src/raddbgi_from_pdb/raddbgi_from_pdb.h +++ b/src/raddbgi_from_pdb/raddbgi_from_pdb.h @@ -1,8 +1,8 @@ // Copyright (c) 2024 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -#ifndef RDI_FROM_PDB_H -#define RDI_FROM_PDB_H +#ifndef RADDBGI_FROM_PDB_H +#define RADDBGI_FROM_PDB_H //////////////////////////////// //~ rjf: Conversion Inputs/Outputs @@ -107,6 +107,22 @@ struct P2R_EXEHashTask U64 out; }; +//- rjf: symbol stream parsing + +typedef struct P2R_SymbolStreamParseIn P2R_SymbolStreamParseIn; +struct P2R_SymbolStreamParseIn +{ + String8 data; +}; + +typedef struct P2R_SymbolStreamParseTask P2R_SymbolStreamParseTask; +struct P2R_SymbolStreamParseTask +{ + P2R_SymbolStreamParseIn in; + Arena *out_arena; + CV_SymParsed *sym; +}; + //////////////////////////////// //~ rjf: Conversion Data Structure Types @@ -233,11 +249,12 @@ internal RDI_RegisterCode p2r_reg_code_from_arch_encoded_fp_reg(RDI_Arch arch, C internal void p2r_location_over_lvar_addr_range(Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_LocationSet *locset, RDIM_Location *location, CV_LvarAddrRange *range, COFF_SectionHeader *section, CV_LvarAddrGap *gaps, U64 gap_count); //////////////////////////////// -//~ rjf: Initial Parsing & Preparation Pass Threads +//~ rjf: Initial Parsing & Preparation Pass Tasks -internal void p2r_tpi_hash_parse_thread__entry_point(void *p); -internal void p2r_tpi_leaf_parse_thread__entry_point(void *p); -internal void p2r_exe_hash_thread__entry_point(void *p); +internal void *p2r_exe_hash_task__entry_point(Arena *arena, void *p); +internal void *p2r_tpi_hash_parse_task__entry_point(Arena *arena, void *p); +internal void *p2r_tpi_leaf_parse_task__entry_point(Arena *arena, void *p); +internal void *p2r_symbol_stream_parse_task__entry_point(Arena *arena, void *p); //////////////////////////////// //~ rjf: Type Forward Resolution Map Build Path & Thread @@ -256,4 +273,4 @@ internal void p2r_symbol_stream_convert_task_thread__entry_point(void *p); internal P2R_ConvertOut *p2r_convert(Arena *arena, P2R_ConvertIn *in); -#endif // RDI_FROM_PDB_H +#endif // RADDBGI_FROM_PDB_H diff --git a/src/raddbgi_from_pdb/raddbgi_from_pdb_main.c b/src/raddbgi_from_pdb/raddbgi_from_pdb_main.c index a67f8920..9273294a 100644 --- a/src/raddbgi_from_pdb/raddbgi_from_pdb_main.c +++ b/src/raddbgi_from_pdb/raddbgi_from_pdb_main.c @@ -8,6 +8,7 @@ //- rjf: [h] #include "base/base_inc.h" #include "os/os_inc.h" +#include "task_system/task_system.h" #include "raddbgi_make_local/raddbgi_make_local.h" #include "coff/coff.h" #include "codeview/codeview.h" @@ -20,6 +21,7 @@ //- rjf: [c] #include "base/base_inc.c" #include "os/os_inc.c" +#include "task_system/task_system.c" #include "raddbgi_make_local/raddbgi_make_local.c" #include "coff/coff.c" #include "codeview/codeview.c" @@ -48,6 +50,7 @@ main(int argc, char **argv) //- rjf: initialize dependencies os_init(argc, argv); + ts_init(); //- rjf: initialize state, parse command line Arena *arena = arena_alloc(); diff --git a/src/task_system/task_system.c b/src/task_system/task_system.c new file mode 100644 index 00000000..2281591e --- /dev/null +++ b/src/task_system/task_system.c @@ -0,0 +1,168 @@ +//////////////////////////////// +//~ rjf: Top-Level Layer Initialization + +internal void +ts_init(void) +{ + Arena *arena = arena_alloc(); + ts_shared = push_array(arena, TS_Shared, 1); + ts_shared->arena = arena; + ts_shared->artifact_slots_count = 1024; + ts_shared->artifact_stripes_count = 64; + ts_shared->artifact_slots = push_array(arena, TS_TaskArtifactSlot, ts_shared->artifact_slots_count); + ts_shared->artifact_stripes = push_array(arena, TS_TaskArtifactStripe, ts_shared->artifact_stripes_count); + for(U64 idx = 0; idx < ts_shared->artifact_stripes_count; idx += 1) + { + ts_shared->artifact_stripes[idx].arena = arena_alloc(); + ts_shared->artifact_stripes[idx].cv = os_condition_variable_alloc(); + ts_shared->artifact_stripes[idx].rw_mutex = os_rw_mutex_alloc(); + } + ts_shared->u2t_ring_size = KB(256); + ts_shared->u2t_ring_base = push_array_no_zero(arena, U8, ts_shared->u2t_ring_size); + ts_shared->u2t_ring_mutex = os_mutex_alloc(); + ts_shared->u2t_ring_cv = os_condition_variable_alloc(); + ts_shared->task_threads_count = os_logical_core_count()-1; + ts_shared->task_threads = push_array(arena, TS_TaskThread, ts_shared->task_threads_count); + for(U64 idx = 0; idx < ts_shared->task_threads_count; idx += 1) + { + ts_shared->task_threads[idx].arena = arena_alloc(); + ts_shared->task_threads[idx].thread = os_launch_thread(ts_task_thread__entry_point, (void *)idx, 0); + } +} + +//////////////////////////////// +//~ rjf: High-Level Task Kickoff / Joining + +internal TS_Ticket +ts_kickoff(TS_TaskFunctionType *entry_point, void *p) +{ + // rjf: obtain number & slot/stripefor next artifact + U64 artifact_num = ins_atomic_u64_inc_eval(&ts_shared->artifact_num_gen); + U64 slot_idx = artifact_num%ts_shared->artifact_slots_count; + U64 stripe_idx = slot_idx%ts_shared->artifact_stripes_count; + TS_TaskArtifactSlot *slot = &ts_shared->artifact_slots[slot_idx]; + TS_TaskArtifactStripe *stripe = &ts_shared->artifact_stripes[stripe_idx]; + + // rjf: allocate artifact + TS_TaskArtifact *artifact = 0; + OS_MutexScopeW(stripe->rw_mutex) + { + artifact = stripe->free_artifact; + if(artifact != 0) + { + SLLStackPop(stripe->free_artifact); + } + else + { + artifact = push_array_no_zero(stripe->arena, TS_TaskArtifact, 1); + } + artifact->num = artifact_num; + artifact->task_is_done = 0; + artifact->result = 0; + } + + // rjf: form ticket out of artifact info + TS_Ticket ticket = {artifact_num, (U64)artifact}; + + // rjf: push task info to task ring buffer + OS_MutexScope(ts_shared->u2t_ring_mutex) for(;;) + { + U64 unconsumed_size = ts_shared->u2t_ring_write_pos - ts_shared->u2t_ring_read_pos; + U64 available_size = ts_shared->u2t_ring_size-unconsumed_size; + if(available_size >= sizeof(entry_point) + sizeof(p) + sizeof(ticket)) + { + ts_shared->u2t_ring_write_pos += ring_write_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_write_pos, &entry_point); + ts_shared->u2t_ring_write_pos += ring_write_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_write_pos, &p); + ts_shared->u2t_ring_write_pos += ring_write_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_write_pos, &ticket); + break; + } + os_condition_variable_wait(ts_shared->u2t_ring_cv, ts_shared->u2t_ring_mutex, max_U64); + } + os_condition_variable_broadcast(ts_shared->u2t_ring_cv); + + return ticket; +} + +internal void * +ts_join(TS_Ticket ticket, U64 endt_us) +{ + void *result = 0; + U64 artifact_num = ticket.u64[0]; + U64 slot_idx = artifact_num%ts_shared->artifact_slots_count; + U64 stripe_idx = slot_idx%ts_shared->artifact_stripes_count; + TS_TaskArtifactSlot *slot = &ts_shared->artifact_slots[slot_idx]; + TS_TaskArtifactStripe *stripe = &ts_shared->artifact_stripes[stripe_idx]; + TS_TaskArtifact *artifact = (TS_TaskArtifact *)ticket.u64[1]; + OS_MutexScopeR(stripe->rw_mutex) for(;;) + { + B64 task_is_done = artifact->task_is_done; + if(task_is_done) + { + OS_MutexScopeRWPromote(stripe->rw_mutex) + { + result = artifact->result; + SLLStackPush(stripe->free_artifact, artifact); + } + break; + } + else + { + os_condition_variable_wait_rw_r(stripe->cv, stripe->rw_mutex, endt_us); + } + } + return result; +} + +//////////////////////////////// +//~ rjf: Task Threads + +internal void +ts_u2t_dequeue_task(TS_TaskFunctionType **entry_point_out, void **p_out, TS_Ticket *ticket_out) +{ + OS_MutexScope(ts_shared->u2t_ring_mutex) for(;;) + { + U64 unconsumed_size = ts_shared->u2t_ring_write_pos - ts_shared->u2t_ring_read_pos; + if(unconsumed_size >= sizeof(*entry_point_out) + sizeof(*p_out) + sizeof(*ticket_out)) + { + ts_shared->u2t_ring_read_pos += ring_read_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_read_pos, entry_point_out); + ts_shared->u2t_ring_read_pos += ring_read_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_read_pos, p_out); + ts_shared->u2t_ring_read_pos += ring_read_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_read_pos, ticket_out); + break; + } + os_condition_variable_wait(ts_shared->u2t_ring_cv, ts_shared->u2t_ring_mutex, max_U64); + } + os_condition_variable_broadcast(ts_shared->u2t_ring_cv); +} + +internal void +ts_task_thread__entry_point(void *p) +{ + U64 thread_idx = (U64)p; + ThreadName("[ts] task thread #%I64u", thread_idx+1); + TS_TaskThread *thread = &ts_shared->task_threads[thread_idx]; + for(;;) + { + //- rjf: grab next task + TS_TaskFunctionType *task_function = 0; + void *task_params = 0; + TS_Ticket task_ticket = {0}; + ts_u2t_dequeue_task(&task_function, &task_params, &task_ticket); + + //- rjf: run task + void *task_result = task_function(thread->arena, task_params); + + //- rjf: store into artifact + U64 artifact_num = task_ticket.u64[0]; + U64 slot_idx = artifact_num%ts_shared->artifact_slots_count; + U64 stripe_idx = slot_idx%ts_shared->artifact_stripes_count; + TS_TaskArtifactSlot *slot = &ts_shared->artifact_slots[slot_idx]; + TS_TaskArtifactStripe *stripe = &ts_shared->artifact_stripes[stripe_idx]; + TS_TaskArtifact *artifact = (TS_TaskArtifact *)task_ticket.u64[1]; + OS_MutexScopeW(stripe->rw_mutex) + { + artifact->task_is_done = 1; + artifact->result = task_result; + } + os_condition_variable_broadcast(stripe->cv); + } +} diff --git a/src/task_system/task_system.h b/src/task_system/task_system.h new file mode 100644 index 00000000..afa58758 --- /dev/null +++ b/src/task_system/task_system.h @@ -0,0 +1,113 @@ +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef TASK_SYSTEM_H +#define TASK_SYSTEM_H + +//////////////////////////////// +//~ rjf: Task "Ticket" Type +// +// "Tickets" are opaque handles, used to refer to submitted tasks. +// + +typedef struct TS_Ticket TS_Ticket; +struct TS_Ticket +{ + U64 u64[2]; +}; + +//////////////////////////////// +//~ rjf: Task Request Types + +typedef void *TS_TaskFunctionType(Arena *arena, void *user_data); + +//////////////////////////////// +//~ rjf: Task Artifact Cache Types + +typedef struct TS_TaskArtifact TS_TaskArtifact; +struct TS_TaskArtifact +{ + TS_TaskArtifact *next; + U64 num; + B64 task_is_done; + void *result; +}; + +typedef struct TS_TaskArtifactSlot TS_TaskArtifactSlot; +struct TS_TaskArtifactSlot +{ + TS_TaskArtifact *first; + TS_TaskArtifact *last; +}; + +typedef struct TS_TaskArtifactStripe TS_TaskArtifactStripe; +struct TS_TaskArtifactStripe +{ + Arena *arena; + OS_Handle cv; + OS_Handle rw_mutex; + TS_TaskArtifact *free_artifact; +}; + +//////////////////////////////// +//~ rjf: Per-Thread State + +typedef struct TS_TaskThread TS_TaskThread; +struct TS_TaskThread +{ + Arena *arena; + OS_Handle thread; +}; + +//////////////////////////////// +//~ rjf: Main Shared State + +typedef struct TS_Shared TS_Shared; +struct TS_Shared +{ + Arena *arena; + + // rjf: task artifact cache + U64 artifact_num_gen; + U64 artifact_slots_count; + U64 artifact_stripes_count; + TS_TaskArtifactSlot *artifact_slots; + TS_TaskArtifactStripe *artifact_stripes; + + // rjf: task ring buffer + U64 u2t_ring_size; + U8 *u2t_ring_base; + U64 u2t_ring_write_pos; + U64 u2t_ring_read_pos; + OS_Handle u2t_ring_mutex; + OS_Handle u2t_ring_cv; + + // rjf: task threads + TS_TaskThread *task_threads; + U64 task_threads_count; +}; + +//////////////////////////////// +//~ rjf: Globals + +global TS_Shared *ts_shared = 0; + +//////////////////////////////// +//~ rjf: Top-Level Layer Initialization + +internal void ts_init(void); + +//////////////////////////////// +//~ rjf: High-Level Task Kickoff / Joining + +internal TS_Ticket ts_kickoff(TS_TaskFunctionType *entry_point, void *p); +internal void *ts_join(TS_Ticket ticket, U64 endt_us); +#define ts_join_struct(ticket, endt_us, type) (type *)ts_join((ticket), (endt_us)) + +//////////////////////////////// +//~ rjf: Task Threads + +internal void ts_u2t_dequeue_task(TS_TaskFunctionType **entry_point_out, void **p_out, TS_Ticket *ticket_out); +internal void ts_task_thread__entry_point(void *p); + +#endif // TASK_SYSTEM_H