fibers test wip

This commit is contained in:
jacob 2025-07-02 14:56:49 -05:00
parent b017a9a477
commit fe40690871
3 changed files with 305 additions and 301 deletions

View File

@ -83,7 +83,7 @@
#define FIBERS_TEST 0
#define FIBERS_TEST 1

View File

@ -2339,6 +2339,310 @@ b32 sys_run_command(struct string cmd)
return success;
}
#if 0
/* ========================== *
* Testing
* ========================== */
#define FIBER_STACK_SIZE MEGABYTE(4)
#define TJOB_DEF(name, arg) void name(void *arg)
typedef TJOB_DEF(tjob_func, arg);
enum fiber_yield_reason {
FIBER_YIELD_REASON_NONE,
FIBER_YIELD_REASON_SLEEP,
FIBER_YIELD_REASON_DONE
};
struct fiber {
const char *name;
void *addr;
struct tjob *current_job;
enum fiber_yield_reason yield_reason;
};
struct tjob {
tjob_func *func;
void *arg;
struct tjob *next;
};
#define NUM_RUNNERS 6
GLOBAL struct {
struct atomic_i32 fibers_lock;
struct arena *fibers_arena;
void *runner_fiber_addr;
struct fiber f1;
struct arena *arena;
b32 shutdown;
struct atomic_i32 lock;
struct tjob *first_free_job;
struct tjob *first_job;
struct tjob *last_job;
} g_test;
INTERNAL void atomic_lock(void)
{
while (atomic_i32_fetch_test_set(&g_test.lock, 0, 1) != 0) {
ix_pause();
}
}
INTERNAL void atomic_unlock(void)
{
atomic_i32_fetch_set(&g_test.lock, 0);
}
INTERNAL void push_job(tjob_func *func, void *arg)
{
atomic_lock();
{
struct tjob *job = NULL;
if (g_test.first_free_job) {
job = g_test.first_free_job;
g_test.first_free_job = job->next;
} else {
job = arena_push_no_zero(g_test.arena, struct tjob);
}
*job = (struct tjob) { .func = func, .arg = arg };
if (g_test.last_job) {
g_test.last_job->next = job;
} else {
g_test.first_job = job;
}
g_test.last_job = job;
}
atomic_unlock();
}
INTERNAL void yield(struct fiber *fiber, enum fiber_yield_reason reason)
{
fiber->yield_reason = reason;
__prof_fiber_leave;
SwitchToFiber(g_test.runner_fiber_addr);
__prof_fiber_enter(fiber->name);
}
INTERNAL TJOB_DEF(test_job, arg)
{
__prof;
struct fiber *fiber = arg;
(UNUSED)fiber;
(UNUSED)yield;
#if 1
Sleep(50);
yield(fiber, FIBER_YIELD_REASON_SLEEP);
Sleep(50);
#else
Sleep(50);
#endif
}
INTERNAL void fiber_proc(void *vfiber)
{
DEBUGBREAKABLE;
struct fiber *fiber = vfiber;
(UNUSED)fiber;
while (true) {
__prof_fiber_enter(fiber->name);
{
fiber->yield_reason = FIBER_YIELD_REASON_NONE;
fiber->current_job->func(fiber->current_job->arg);
fiber->yield_reason = FIBER_YIELD_REASON_DONE;
}
__prof_fiber_leave;
SwitchToFiber(g_test.runner_fiber_addr);
}
}
struct runner_thread_param {
i32 id;
};
INTERNAL DWORD WINAPI runner_thread_proc(LPVOID tparam)
{
__prof;
struct runner_thread_param *param = tparam;
struct arena *arena = arena_alloc(GIGABYTE(64));
/* Set thread name */
{
struct string id_str = string_from_int(arena, param->id, 10, 1);
struct string name = string_format(arena, LIT("Runner thread %F"), FMT_STR(id_str));
wchar_t *name_wstr = wstr_from_string(arena, name);
SetThreadDescription(GetCurrentThread(), name_wstr);
}
g_test.runner_fiber_addr = ConvertThreadToFiber(NULL);
g_test.f1.name = "Fiber 1";
g_test.f1.addr = CreateFiber(FIBER_STACK_SIZE, fiber_proc, &g_test.f1);
b32 shutdown = false;
while (!shutdown) {
Sleep(100);
struct arena_temp temp = arena_temp_begin(arena);
atomic_lock();
shutdown = g_test.shutdown;
if (!shutdown && g_test.first_job) {
/* Pull job */
struct tjob local_job = ZI;
{
struct tjob *job = g_test.first_job;
local_job = *job;
struct tjob *next = job->next;
g_test.first_job = next;
if (!next) {
g_test.last_job = NULL;
}
}
/* Run job */
atomic_unlock();
{
__profscope(Run job);
struct fiber *fiber = &g_test.f1;
fiber->current_job = &local_job;
fiber->yield_reason = FIBER_YIELD_REASON_NONE;
b32 done = false;
while (!done) {
SwitchToFiber(fiber->addr);
enum fiber_yield_reason yield_reason = fiber->yield_reason;
switch (yield_reason) {
default: break;
case FIBER_YIELD_REASON_SLEEP:
{
__profscope(Sleep);
Sleep(100);
} break;
case FIBER_YIELD_REASON_DONE:
{
done = true;
} break;
}
}
}
atomic_lock();
}
atomic_unlock();
arena_temp_end(temp);
}
arena_release(arena);
return 0;
}
INTERNAL void test_main(struct string cmdline)
{
__prof;
(UNUSED)cmdline;
struct arena *arena = arena_alloc(GIGABYTE(64));
{
g_test.arena = arena_alloc(GIGABYTE(64));
Sleep(1000);
for (u32 i = 0; i < 50; ++i) {
push_job(test_job, &g_test.f1);
}
u32 num_runners = NUM_RUNNERS;
HANDLE *runner_thread_handles = arena_push_array(arena, HANDLE, num_runners);
struct runner_thread_param *tparams = arena_push_array(arena, struct runner_thread_param, num_runners);
for (u32 i = 0; i < num_runners; ++i) {
struct runner_thread_param *tparam = &tparams[i];
tparam->id = i;
runner_thread_handles[i] = CreateThread(NULL, MEGABYTE(1), runner_thread_proc, tparam, 0, NULL);
//SetThreadDescription(GetCurrentThread(), t->thread_name_wstr);
}
struct sys_thread *runner_thread = sys_thread_alloc(runner_thread_entry_point, NULL, LIT("Runner thread"));
Sleep(5000);
{
atomic_lock();
g_test.shutdown = true;
atomic_unlock();
}
for (u32 i = 0; i < num_runners; ++i) {
HANDLE handle = runner_thread_handles[i];
DWORD wait_res = WaitForSingleObject(handle, INFINITE);
if (wait_res == WAIT_OBJECT_0) {
CloseHandle(handle);
}
}
}
arena_release(arena);
}
#endif
/* ========================== *
* Entry point
* ========================== */

View File

@ -2338,301 +2338,6 @@ b32 sys_run_command(struct string cmd)
}
return success;
}
/* ========================== *
* Testing
* ========================== */
#define FIBER_STACK_SIZE MEGABYTE(4)
#define TJOB_DEF(name, arg) void name(void *arg)
typedef TJOB_DEF(tjob_func, arg);
enum fiber_yield_reason {
FIBER_YIELD_REASON_NONE,
FIBER_YIELD_REASON_SLEEP,
FIBER_YIELD_REASON_DONE
};
struct fiber {
const char *name;
void *addr;
struct tjob *current_job;
enum fiber_yield_reason yield_reason;
};
struct tjob {
tjob_func *func;
void *arg;
struct tjob *next;
};
GLOBAL struct {
void *runner_fiber_addr;
struct fiber f1;
struct arena *arena;
b32 shutdown;
struct atomic_i32 lock;
struct tjob *first_free_job;
struct tjob *first_job;
struct tjob *last_job;
} g_test;
INTERNAL void atomic_lock(void)
{
while (atomic_i32_fetch_test_set(&g_test.lock, 0, 1) != 0) {
ix_pause();
}
}
INTERNAL void atomic_unlock(void)
{
atomic_i32_fetch_set(&g_test.lock, 0);
}
INTERNAL void push_job(tjob_func *func, void *arg)
{
atomic_lock();
{
struct tjob *job = NULL;
if (g_test.first_free_job) {
job = g_test.first_free_job;
g_test.first_free_job = job->next;
} else {
job = arena_push_no_zero(g_test.arena, struct tjob);
}
*job = (struct tjob) { .func = func, .arg = arg };
if (g_test.last_job) {
g_test.last_job->next = job;
} else {
g_test.first_job = job;
}
g_test.last_job = job;
}
atomic_unlock();
}
INTERNAL void yield(struct fiber *fiber, enum fiber_yield_reason reason)
{
fiber->yield_reason = reason;
__prof_fiber_leave;
SwitchToFiber(g_test.runner_fiber_addr);
__prof_fiber_enter(fiber->name);
}
INTERNAL TJOB_DEF(test_job, arg)
{
__prof;
struct fiber *fiber = arg;
(UNUSED)fiber;
(UNUSED)yield;
#if 1
Sleep(50);
yield(fiber, FIBER_YIELD_REASON_SLEEP);
Sleep(50);
#else
Sleep(50);
#endif
}
INTERNAL void fiber_proc(void *vfiber)
{
DEBUGBREAKABLE;
struct fiber *fiber = vfiber;
(UNUSED)fiber;
while (true) {
__prof_fiber_enter(fiber->name);
{
fiber->yield_reason = FIBER_YIELD_REASON_NONE;
fiber->current_job->func(fiber->current_job->arg);
fiber->yield_reason = FIBER_YIELD_REASON_DONE;
}
__prof_fiber_leave;
SwitchToFiber(g_test.runner_fiber_addr);
}
}
struct runner_thread_param {
i32 id;
};
INTERNAL DWORD WINAPI runner_thread_proc(LPVOID tparam)
{
__prof;
struct runner_thread_param *param = tparam;
struct arena *arena = arena_alloc(GIGABYTE(64));
/* Set thread name */
{
struct string id_str = string_from_int(arena, param->id, 10, 1);
struct string name = string_format(arena, LIT("Runner thread %F"), FMT_STR(id_str));
wchar_t *name_wstr = wstr_from_string(arena, name);
SetThreadDescription(GetCurrentThread(), name_wstr);
}
g_test.runner_fiber_addr = ConvertThreadToFiber(NULL);
g_test.f1.name = "Fiber 1";
g_test.f1.addr = CreateFiber(FIBER_STACK_SIZE, fiber_proc, &g_test.f1);
b32 shutdown = false;
while (!shutdown) {
Sleep(100);
struct arena_temp temp = arena_temp_begin(arena);
atomic_lock();
shutdown = g_test.shutdown;
if (!shutdown && g_test.first_job) {
/* Pull job */
struct tjob local_job = ZI;
{
struct tjob *job = g_test.first_job;
local_job = *job;
struct tjob *next = job->next;
g_test.first_job = next;
if (!next) {
g_test.last_job = NULL;
}
}
/* Run job */
atomic_unlock();
{
__profscope(Run job);
struct fiber *fiber = &g_test.f1;
fiber->current_job = &local_job;
fiber->yield_reason = FIBER_YIELD_REASON_NONE;
b32 done = false;
while (!done) {
SwitchToFiber(fiber->addr);
enum fiber_yield_reason yield_reason = fiber->yield_reason;
switch (yield_reason) {
default: break;
case FIBER_YIELD_REASON_SLEEP:
{
__profscope(Sleep);
Sleep(100);
} break;
case FIBER_YIELD_REASON_DONE:
{
done = true;
} break;
}
}
}
atomic_lock();
}
atomic_unlock();
arena_temp_end(temp);
}
arena_release(arena);
}
INTERNAL void test_main(struct string cmdline)
{
__prof;
(UNUSED)cmdline;
struct arena *arena = arena_alloc(GIGABYTE(64));
{
g_test.arena = arena_alloc(GIGABYTE(64));
Sleep(1000);
for (u32 i = 0; i < 50; ++i) {
push_job(test_job, &g_test.f1);
}
u32 num_runners = NUM_RUNNERS;
HANDLE *runner_thread_handles = arena_push_array(arena, HANDLE, num_runners);
struct runner_thread_param *tparams = arena_push_array(arena, struct runner_thread_param, num_runners);
for (u32 i = 0; i < num_runners; ++i) {
struct runner_thread_param *tparam = &tparams[i];
tparam->id = i;
runner_thread_handles[i] = CreateThread(NULL, MEGABYTE(1), runner_thread_proc, tparam, 0, NULL);
//SetThreadDescription(GetCurrentThread(), t->thread_name_wstr);
}
struct sys_thread *runner_thread = sys_thread_alloc(runner_thread_entry_point, NULL, LIT("Runner thread"));
Sleep(5000);
{
atomic_lock();
g_test.shutdown = true;
atomic_unlock();
}
for (u32 i = 0; i < num_runners; ++i) {
HANDLE handle = runner_thread_handles[i];
DWORD wait_res = WaitForSingleObject(handle, INFINITE);
if (wait_res == WAIT_OBJECT_0) {
CloseHandle(handle);
}
}
}
arena_release(arena);
}
/* ========================== *
* Entry point
@ -2643,12 +2348,7 @@ INTERNAL SYS_THREAD_DEF(win32_app_thread_entry_point, arg)
(UNUSED)arg;
struct arena_temp scratch = scratch_begin_no_conflict();
struct string cmdline_args = string_from_wstr(scratch.arena, G.cmdline_args_wstr, ARRAY_COUNT(G.cmdline_args_wstr));
#if 0
(UNUSED)test_main;
app_entry_point(cmdline_args);
#else
test_main(cmdline_args);
#endif
scratch_end(scratch);
}