fiber testing

This commit is contained in:
jacob 2025-07-05 13:45:10 -05:00
parent 53f38271e6
commit e2a0d38e70
3 changed files with 119 additions and 38 deletions

View File

@ -7,7 +7,7 @@
#if PROFILING
#define PROFILING_SYSTEM_TRACE 1
#define PROFILING_SYSTEM_TRACE 0
#define PROFILING_CAPTURE_FRAME_IMAGE 0
#define PROFILING_LOCKS 0
#define PROFILING_D3D 1

View File

@ -116,6 +116,39 @@ struct win32_window {
#define MAX_COUNTERS 4096
struct alignas(64) counter {
/* =================================================== */
i32 id; /* 4 bytes */
struct atomic_i32 value; /* 4 bytes */
/* =================================================== */
u8 _pad1[8]; /* 8 bytes (padding) */
/* =================================================== */
u8 _pad2[8]; /* 8 bytes (padding) */
/* =================================================== */
u8 _pad3[8]; /* 8 bytes (padding) */
/* =================================================== */
u8 _pad4[8]; /* 8 bytes (padding) */
/* =================================================== */
u8 _pad5[8]; /* 8 bytes (padding) */
/* =================================================== */
u8 _pad6[8]; /* 8 bytes (padding) */
/* =================================================== */
u8 _pad7[8]; /* 8 bytes (padding) */
};
STATIC_ASSERT(sizeof(struct counter) == 64); /* Assume counter fits in one cache line (increase if necessary) */
STATIC_ASSERT(alignof(struct counter) == 64); /* Avoid false sharing */
#define FIBER_NAME_PREFIX_CSTR "Fiber #"
#define FIBER_NAME_MAX_SIZE 64
@ -142,9 +175,12 @@ struct alignas(64) fiber {
void *job_sig; /* 8 bytes */
/* =================================================== */
i32 job_id; /* 4 bytes */
enum yield_kind yield_kind; /* 4 bytes */
i32 job_priority; /* 4 bytes */
/* =================================================== */
u8 _pad[16]; /* 8 bytes (padding) */
enum yield_kind yield_kind; /* 4 bytes */
u8 _pad0[4]; /* 4 bytes (padding) */
/* =================================================== */
u8 _pad1[8]; /* 8 bytes (padding) */
};
STATIC_ASSERT(sizeof(struct fiber) == 64); /* Assume fiber fits in one cache line (increase if necessary) */
STATIC_ASSERT(alignof(struct fiber) == 64); /* Avoid false sharing */
@ -153,12 +189,23 @@ struct alignas(64) fiber_ctx {
/* ==================================================== */
struct sys_scratch_ctx scratch_ctx; /* 16 bytes */
/* ==================================================== */
u8 _pad[40]; /* 40 bytes (padding) */
u8 _pad0[8]; /* 8 bytes (padding) */
/* ==================================================== */
u8 _pad1[8]; /* 8 bytes (padding) */
/* ==================================================== */
u8 _pad2[8]; /* 8 bytes (padding) */
/* ==================================================== */
u8 _pad3[8]; /* 8 bytes (padding) */
/* ==================================================== */
u8 _pad4[8]; /* 8 bytes (padding) */
};
STATIC_ASSERT(sizeof(struct fiber_ctx) == 64); /* Assume ctx fits in one cache line (increase if necessary) */
STATIC_ASSERT(alignof(struct fiber_ctx) == 64); /* Avoid false sharing */
struct alignas(64) runner_ctx {
i32 id;
HANDLE sleep_timer;
@ -174,16 +221,6 @@ struct job_info {
struct job_info *next;
};
struct alignas(64) job_queue {
struct atomic_i32 lock;
struct arena *arena;
struct job_info *first;
struct job_info *last;
struct job_info *first_free;
};
enum job_queue_kind {
JOB_QUEUE_KIND_HIGH_PRIORITY,
JOB_QUEUE_KIND_NORMAL_PRIORITY,
@ -192,7 +229,17 @@ enum job_queue_kind {
NUM_JOB_QUEUE_KINDS
};
struct alignas(64) job_queue {
enum job_queue_kind kind;
struct atomic_i32 lock;
struct arena *arena;
struct job_info *first;
struct job_info *last;
struct job_info *first_free;
};
/* ========================== *
* Global state
@ -247,11 +294,22 @@ GLOBAL struct {
/* Counters */
i32 num_counters;
struct atomic_i32 counters_lock; /* TODO: Prevent false sharing */
i32 first_free_counter_id;
struct counter counters[MAX_COUNTERS];
/* Fibers */
i32 num_fibers;
i32 first_free_fiber_id;
struct arena *fiber_names_arena;
alignas(64) struct atomic_i32 fibers_lock;
struct atomic_i32 fibers_lock; /* TODO: Prevent false sharing */
struct fiber fibers[SYS_MAX_FIBERS];
struct fiber_ctx fiber_contexts[SYS_MAX_FIBERS];
@ -259,17 +317,23 @@ GLOBAL struct {
struct job_queue job_queues[NUM_JOB_QUEUE_KINDS];
/* Runners */
struct atomic_i32 runners_shutdown;
struct atomic_i32 runners_shutdown; /* TODO: Prevent false sharing */
i32 num_runner_threads;
struct arena *runner_threads_arena;
struct sys_thread **runner_threads;
struct runner_ctx *runner_contexts;
struct atomic_i32 runner_wake_gen;
} G = ZI, DEBUG_ALIAS(G, G_sys_win32);
/* ========================== *
* Counters
* ========================== */
/* ========================== *
* Fibers
* ========================== */
@ -346,6 +410,7 @@ INTERNAL struct fiber *fiber_alloc(enum fiber_kind kind)
fiber->job_func = 0;
fiber->job_sig = 0;
fiber->job_id = 0;
fiber->job_priority = 0;
fiber->yield_kind = 0;
fiber->parent_id = 0;
return fiber;
@ -405,9 +470,10 @@ void sys_yield(void)
void sys_run(i32 count, sys_job_func *func, enum sys_job_priority priority)
{
//priority = min_i32(priority, current_fiber_job_priority); /* Jobs can't create higher priority jobs */
if (count >= 1 && func && priority >= 0 && priority < NUM_SYS_JOB_PRIORITIES) {
STATIC_ASSERT((i32)NUM_SYS_JOB_PRIORITIES == (i32)NUM_JOB_QUEUE_KINDS); /* Enums must have 1:1 mapping */
if (count > 0) {
struct fiber *fiber = fiber_from_id(sys_current_fiber_id());
priority = clamp_i32(priority, fiber->job_priority, SYS_JOB_PRIORITY_BACKGROUND); /* A job cannot create a job with a higher priority than itself */
STATIC_ASSERT((i32)NUM_SYS_JOB_PRIORITIES == (i32)NUM_JOB_QUEUE_KINDS); /* Priority & queue kind enums must have 1:1 mapping */
enum job_queue_kind queue_kind = (enum job_queue_kind)priority;
struct job_queue *queue = &G.job_queues[queue_kind];
while (atomic_i32_fetch_test_set(&queue->lock, 0, 1) != 0) ix_pause();
@ -417,8 +483,9 @@ void sys_run(i32 count, sys_job_func *func, enum sys_job_priority priority)
info = queue->first_free;
queue->first_free = info->next;
} else {
info = arena_push(queue->arena, struct job_info);
info = arena_push_no_zero(queue->arena, struct job_info);
}
MEMZERO_STRUCT(info);
info->count = count;
info->func = func;
if (queue->last) {
@ -429,9 +496,6 @@ void sys_run(i32 count, sys_job_func *func, enum sys_job_priority priority)
queue->last = info;
}
atomic_i32_fetch_set(&queue->lock, 0);
} else {
/* Invalid job parameters */
sys_panic(LIT("Invalid job parameters"));
}
}
@ -451,7 +515,6 @@ INTERNAL SYS_JOB_DEF(bla_job, job)
Sleep(20);
}
/* ========================== *
* Job fiber func
* ========================== */
@ -497,8 +560,11 @@ INTERNAL SYS_THREAD_DEF(runner_entry, runner_ctx_arg)
queues[i] = &G.job_queues[i];
}
struct fiber *job_fiber = NULL;
while (!atomic_i32_fetch(&G.runners_shutdown)) {
/* Pull job from queue */
enum sys_job_priority job_priority = 0;
i32 job_id = 0;
sys_job_func *job_func = 0;
void *job_sig = 0;
@ -512,6 +578,8 @@ INTERNAL SYS_THREAD_DEF(runner_entry, runner_ctx_arg)
job_id = info->num_dispatched++;
if (job_id < info->count) {
/* Pick job */
STATIC_ASSERT((i32)NUM_SYS_JOB_PRIORITIES == (i32)NUM_JOB_QUEUE_KINDS); /* Priority & queue kind enums must have 1:1 mapping */
job_priority = (enum sys_job_priority)queue->kind;
job_func = info->func;
job_sig = info->sig;
if (job_id == (info->count - 1)) {
@ -533,15 +601,18 @@ INTERNAL SYS_THREAD_DEF(runner_entry, runner_ctx_arg)
/* Execute fiber */
if (job_func) {
__profscope(Execute fiber);
struct fiber *fiber = fiber_alloc(FIBER_KIND_JOB_RUNNER);
fiber->job_func = job_func;
fiber->job_sig = job_sig;
fiber->job_id = job_id;
fiber->parent_id = runner_fiber_id;
if (!job_fiber) {
job_fiber = fiber_alloc(FIBER_KIND_JOB_RUNNER);
}
job_fiber->job_func = job_func;
job_fiber->job_sig = job_sig;
job_fiber->job_id = job_id;
job_fiber->job_priority = job_priority;
job_fiber->parent_id = runner_fiber_id;
b32 done = false;
while (!done) {
SwitchToFiber(fiber->addr);
enum yield_kind yield_kind = fiber->yield_kind;
SwitchToFiber(job_fiber->addr);
enum yield_kind yield_kind = job_fiber->yield_kind;
switch (yield_kind) {
default:
{
@ -557,7 +628,8 @@ INTERNAL SYS_THREAD_DEF(runner_entry, runner_ctx_arg)
case YIELD_KIND_DONE:
{
fiber_release(fiber, fiber->id);
/* TODO: remove this */
(UNUSED)fiber_release;
done = true;
} break;
}
@ -574,6 +646,7 @@ INTERNAL SYS_THREAD_DEF(test_entry, _)
/* Init job queues */
for (u32 i = 0; i < countof(G.job_queues); ++i) {
struct job_queue *queue = &G.job_queues[i];
queue->kind = (enum job_queue_kind)i;
queue->arena = arena_alloc(GIGABYTE(64));
}
@ -2826,6 +2899,9 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
__profthread("Main thread", PROF_THREAD_GROUP_MAIN);
/* Init counters */
G.num_counters = 1; /* Counter at index 0 always nil */
/* Init fibers */
G.num_fibers = 1; /* Fiber at index 0 always nil */
G.fiber_names_arena = arena_alloc(GIGABYTE(64));

View File

@ -192,8 +192,8 @@ GLOBAL READONLY enum user_bind_kind g_binds[SYS_BTN_COUNT] = {
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(user_shutdown);
INTERNAL LOG_EVENT_CALLBACK_FUNC_DEF(debug_console_log_callback, log);
INTERNAL JOB_DEF(user_job, _);
INTERNAL JOB_DEF(local_sim_job , _);
INTERNAL SYS_JOB_DEF(user_job, _);
INTERNAL SYS_JOB_DEF(local_sim_job , _);
INTERNAL SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(window_event_callback, event);
struct user_startup_receipt user_startup(struct gp_startup_receipt *gp_sr,
@ -259,8 +259,13 @@ struct user_startup_receipt user_startup(struct gp_startup_receipt *gp_sr,
sys_window_register_event_callback(G.window, &window_event_callback);
/* Start jobs */
#if 0
job_dispatch_pinned(APP_DEDICATED_WORKER_ID_SIM, local_sim_job, NULL);
job_dispatch_pinned(APP_DEDICATED_WORKER_ID_USER, user_job, NULL);
#else
sys_run(1, local_sim_job, SYS_JOB_PRIORITY_HIGH);
sys_run(1, user_job, SYS_JOB_PRIORITY_HIGH);
#endif
app_register_exit_callback(&user_shutdown);
return (struct user_startup_receipt) { 0 };
@ -2092,7 +2097,7 @@ INTERNAL void user_update(void)
* User thread
* ========================== */
INTERNAL JOB_DEF(user_job, _)
INTERNAL SYS_JOB_DEF(user_job, _)
{
(UNUSED)_;
i64 last_frame_ns = 0;
@ -2181,7 +2186,7 @@ struct sim_decode_queue {
};
INTERNAL JOB_DEF(local_sim_job, _)
INTERNAL SYS_JOB_DEF(local_sim_job, _)
{
(UNUSED)_;