aggregate base layer state

This commit is contained in:
jacob 2025-12-12 13:40:22 -06:00
parent ac8a4cf6c2
commit 476d154beb
28 changed files with 393 additions and 344 deletions

View File

@ -748,6 +748,7 @@
i64 TimeNs(void); i64 TimeNs(void);
void TrueRand(String buffer); void TrueRand(String buffer);
CpuTopologyInfo GetCpuTopologyInfo(void); CpuTopologyInfo GetCpuTopologyInfo(void);
void SleepSeconds(f64 seconds);
#endif #endif
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -1,5 +1,3 @@
ThreadLocal ThreadArenasCtx t_arena_ctx = ZI;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Arena management //~ Arena management
@ -20,7 +18,7 @@ Arena *AcquireArena(u64 reserve)
Panic(Lit("Failed to reserve memory")); Panic(Lit("Failed to reserve memory"));
} }
u64 reserved = reserve; u64 reserved = reserve;
AddGstat(GSTAT_MEMORY_RESERVED, reserve); AddGstat(MemoryReserved, reserve);
/* Commit initial block */ /* Commit initial block */
base = CommitMemory(base, ArenaBlockSize); base = CommitMemory(base, ArenaBlockSize);
@ -34,8 +32,8 @@ Arena *AcquireArena(u64 reserve)
StaticAssert(sizeof(Arena) <= ArenaHeaderSize); /* Arena struct must fit in header */ StaticAssert(sizeof(Arena) <= ArenaHeaderSize); /* Arena struct must fit in header */
AsanPoison(base + sizeof(Arena), ArenaBlockSize - sizeof(Arena)); AsanPoison(base + sizeof(Arena), ArenaBlockSize - sizeof(Arena));
AddGstat(GSTAT_MEMORY_COMMITTED, ArenaBlockSize); AddGstat(MemoryCommitted, ArenaBlockSize);
AddGstat(GSTAT_NUM_ARENAS, 1); AddGstat(NumArenas, 1);
/* Create & return arena header at beginning of block */ /* Create & return arena header at beginning of block */
Arena *arena = (Arena *)base; Arena *arena = (Arena *)base;
@ -48,9 +46,9 @@ Arena *AcquireArena(u64 reserve)
void ReleaseArena(Arena *arena) void ReleaseArena(Arena *arena)
{ {
AsanUnpoison(arena, arena->committed + ArenaHeaderSize); AsanUnpoison(arena, arena->committed + ArenaHeaderSize);
AddGstat(GSTAT_MEMORY_COMMITTED, -(i64)(arena->committed - ArenaHeaderSize)); AddGstat(MemoryCommitted, -(i64)(arena->committed - ArenaHeaderSize));
AddGstat(GSTAT_MEMORY_RESERVED, -(i64)(arena->reserved)); AddGstat(MemoryReserved, -(i64)(arena->reserved));
AddGstat(GSTAT_NUM_ARENAS, -1); AddGstat(NumArenas, -1);
ReleaseMemory(arena); ReleaseMemory(arena);
} }
@ -117,7 +115,7 @@ void *PushBytesNoZero(Arena *arena, u64 size, u64 align)
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"));
} }
arena->committed += commit_bytes; arena->committed += commit_bytes;
AddGstat(GSTAT_MEMORY_COMMITTED, commit_bytes); AddGstat(MemoryCommitted, commit_bytes);
AsanPoison(commit_address, commit_bytes); AsanPoison(commit_address, commit_bytes);
} }
@ -200,15 +198,15 @@ void EndTempArena(TempArena temp)
TempArena BeginScratch(Arena *potential_conflict) TempArena BeginScratch(Arena *potential_conflict)
{ {
/* This function is currently hard-coded for 2 thread-local scratch arenas */ /* This function is currently hard-coded for 2 thread-local scratch arenas */
StaticAssert(ScratchArenasPerCtx == 2); StaticAssert(countof(Base_tl.arenas.scratch) == 2);
/* Use `BeginScratchNoConflict` if no conflicts are present */ /* Use `BeginScratchNoConflict` if no conflicts are present */
Assert(potential_conflict != 0); Assert(potential_conflict != 0);
Arena *scratch_arena = t_arena_ctx.scratch_arenas[0]; Arena *scratch_arena = Base_tl.arenas.scratch[0];
if (scratch_arena == potential_conflict) if (scratch_arena == potential_conflict)
{ {
scratch_arena = t_arena_ctx.scratch_arenas[1]; scratch_arena = Base_tl.arenas.scratch[1];
} }
TempArena temp = BeginTempArena(scratch_arena); TempArena temp = BeginTempArena(scratch_arena);
return temp; return temp;
@ -216,12 +214,11 @@ TempArena BeginScratch(Arena *potential_conflict)
TempArena BeginScratchNoConflict_(void) TempArena BeginScratchNoConflict_(void)
{ {
Arena *scratch_arena = t_arena_ctx.scratch_arenas[0]; Arena *scratch_arena = Base_tl.arenas.scratch[0];
TempArena temp = BeginTempArena(scratch_arena); TempArena temp = BeginTempArena(scratch_arena);
return temp; return temp;
} }
void EndScratch(TempArena scratch_temp) void EndScratch(TempArena scratch_temp)
{ {
EndTempArena(scratch_temp); EndTempArena(scratch_temp);

View File

@ -4,19 +4,6 @@
#define ArenaHeaderSize 256 #define ArenaHeaderSize 256
#define ArenaBlockSize 16384 #define ArenaBlockSize 16384
////////////////////////////////////////////////////////////
//~ State types
#define ScratchArenasPerCtx 2
Struct(ThreadArenasCtx)
{
Arena *perm_arena;
Arena *scratch_arenas[ScratchArenasPerCtx];
};
extern ThreadLocal ThreadArenasCtx t_arena_ctx;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Arena management //~ Arena management
@ -65,7 +52,7 @@ void *ArenaFirst_(Arena *arena, u64 align);
TempArena BeginTempArena(Arena *arena); TempArena BeginTempArena(Arena *arena);
void EndTempArena(TempArena temp); void EndTempArena(TempArena temp);
#define PermArena() (t_arena_ctx.perm_arena) #define PermArena() (Base_tl.arenas.perm)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Scratch arena helpers //~ Scratch arena helpers
@ -74,12 +61,12 @@ TempArena BeginScratch(Arena *potential_conflict);
TempArena BeginScratchNoConflict_(void); TempArena BeginScratchNoConflict_(void);
void EndScratch(TempArena scratch_temp); void EndScratch(TempArena scratch_temp);
/* This macro declares an unused "arena" variable that will error if an existing "arena" /* This macro declares an unused "arena" variable that will produce a shadowing
* variable is present (due to shadowing). This is for catching obvious cases of * warning variable is present (due to shadowing). This is for catching obvious
* `BeginScratchNoConflict` getting called when an `arena` variable already * cases of `BeginScratchNoConflict` getting called when an `arena` variable
* exists in the caller's scope. */ * already exists in the caller's scope. */
#define BeginScratchNoConflict() \ #define BeginScratchNoConflict() \
BeginScratchNoConflict_(); \ BeginScratchNoConflict_(); \
do { \ do { \
u8 arena = 0; \ u8 arena = 0; \
} while (0) } while (0)

View File

@ -4,19 +4,75 @@
void BootstrapAsync(void) void BootstrapAsync(void)
{ {
/* TODO: Dynamic lane counts */ /* TODO: Dynamic lane counts */
DispatchWave(Lit("Async"), 4, AsyncEntryPoint, 0); DispatchWave(Lit("Async"), 4, AsyncWorkerEntryPoint, 0);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Async ops //~ Async ops
void OnAsyncTick(AsyncFunc *func) void OnAsyncTick(AsyncTickCallbackFunc *func)
{ {
Arena *perm = PermArena();
AsyncTickCallbackNode *n = PushStruct(perm, AsyncTickCallbackNode);
n->callback.func = func;
Lock lock = LockE(&Base.async.mutex);
{
SllQueuePush(Base.async.first_callback_node, Base.async.last_callback_node, n);
Base.async.callback_nodes_count += 1;
}
Unlock(&lock);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Async worker //~ Async worker
void AsyncEntryPoint(WaveLaneCtx *lane) void AsyncWorkerEntryPoint(WaveLaneCtx *lane)
{ {
/* Tick forever */
for (;;)
{
TempArena scratch = BeginScratchNoConflict();
AsyncWorkerState *w = &Base.async.worker;
/* FIXME: Remove this */
SleepSeconds(0.100);
//////////////////////////////
//- Grab async functions
if (lane->idx == 0)
{
Lock lock = LockE(&Base.async.mutex);
{
w->callbacks_count = Base.async.callback_nodes_count;
w->callbacks = PushStructsNoZero(scratch.arena, AsyncTickCallback, w->callbacks_count);
u64 callback_idx = 0;
for (AsyncTickCallbackNode *n = Base.async.first_callback_node; n; n = n->next)
{
w->callbacks[callback_idx] = n->callback;
++callback_idx;
}
}
Unlock(&lock);
}
WaveSync(lane);
//////////////////////////////
//- Run tick funcs
for (u64 callback_idx = 0; callback_idx < w->callbacks_count; ++callback_idx)
{
AsyncTickCallback *callback = &w->callbacks[callback_idx];
callback->func(lane);
}
//////////////////////////////
//- End tick
WaveSync(lane);
EndScratch(scratch);
}
} }

View File

@ -1,7 +1,24 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Async types //~ Async types
typedef void AsyncFunc(WaveLaneCtx *lane); typedef void AsyncTickCallbackFunc(WaveLaneCtx *lane);
Struct(AsyncTickCallback)
{
AsyncTickCallbackFunc *func;
};
Struct(AsyncTickCallbackNode)
{
AsyncTickCallbackNode *next;
AsyncTickCallback callback;
};
Struct(AsyncWorkerState)
{
u64 callbacks_count;
AsyncTickCallback *callbacks;
};
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Bootstrap //~ Bootstrap
@ -11,9 +28,9 @@ void BootstrapAsync(void);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Async ops //~ Async ops
void OnAsyncTick(AsyncFunc *func); void OnAsyncTick(AsyncTickCallbackFunc *func);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Async worker //~ Async worker
void AsyncEntryPoint(WaveLaneCtx *lane); void AsyncWorkerEntryPoint(WaveLaneCtx *lane);

View File

@ -1,11 +1,8 @@
SharedCmdlineState shared_cmdline_state = ZI;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Bootstrap //~ Bootstrap
void BootstrapCmdline(void) void BootstrapCmdline(void)
{ {
SharedCmdlineState *g = &shared_cmdline_state;
TempArena scratch = BeginScratchNoConflict(); TempArena scratch = BeginScratchNoConflict();
{ {
StringList raw = GetRawCommandline(); StringList raw = GetRawCommandline();
@ -54,19 +51,19 @@ void BootstrapCmdline(void)
} }
Arena *perm = PermArena(); Arena *perm = PermArena();
g->positional_args_count = tmp_positionals_count; Base.cmdline.positional_args_count = tmp_positionals_count;
g->positional_args = PushStructsNoZero(perm, String, tmp_positionals_count); Base.cmdline.positional_args = PushStructsNoZero(perm, String, tmp_positionals_count);
CopyStructs(g->positional_args, tmp_positionals, tmp_positionals_count); CopyStructs(Base.cmdline.positional_args, tmp_positionals, tmp_positionals_count);
for (u64 i = 0; i < tmp_args_count; ++i) for (u64 i = 0; i < tmp_args_count; ++i)
{ {
CommandlineArg arg = tmp_args[i]; CommandlineArg arg = tmp_args[i];
CommandlineArgNode *n = PushStruct(perm, CommandlineArgNode); CommandlineArgNode *n = PushStruct(perm, CommandlineArgNode);
u64 hash = HashFnv64(Fnv64Basis, arg.name); u64 hash = HashFnv64(Fnv64Basis, arg.name);
u64 bin_idx = hash % CommandlineArgBinsCount; u64 bin_idx = hash % countof(Base.cmdline.arg_bins);
n->arg = arg; n->arg = arg;
n->hash = hash; n->hash = hash;
n->next = g->arg_bins[bin_idx]; n->next = Base.cmdline.arg_bins[bin_idx];
g->arg_bins[bin_idx] = n; Base.cmdline.arg_bins[bin_idx] = n;
} }
} }
EndScratch(scratch); EndScratch(scratch);
@ -77,21 +74,19 @@ void BootstrapCmdline(void)
String StringFromCommandlineIdx(i32 idx) String StringFromCommandlineIdx(i32 idx)
{ {
SharedCmdlineState *g = &shared_cmdline_state;
String result = ZI; String result = ZI;
if (idx < g->positional_args_count) if (idx < Base.cmdline.positional_args_count)
{ {
result = g->positional_args[idx]; result = Base.cmdline.positional_args[idx];
} }
return result; return result;
} }
CommandlineArg CommandlineArgFromName(String name) CommandlineArg CommandlineArgFromName(String name)
{ {
SharedCmdlineState *g = &shared_cmdline_state;
CommandlineArg result = ZI; CommandlineArg result = ZI;
u64 hash = HashFnv64(Fnv64Basis, name); u64 hash = HashFnv64(Fnv64Basis, name);
for (CommandlineArgNode *n = g->arg_bins[hash % CommandlineArgBinsCount]; n; n = n->next) for (CommandlineArgNode *n = Base.cmdline.arg_bins[hash % countof(Base.cmdline.arg_bins)]; n; n = n->next)
{ {
if (n->hash == hash && MatchString(n->arg.name, name)) if (n->hash == hash && MatchString(n->arg.name, name))
{ {

View File

@ -18,18 +18,6 @@ Struct(CommandlineArgNode)
CommandlineArg arg; CommandlineArg arg;
}; };
////////////////////////////////////////////////////////////
//~ State types
#define CommandlineArgBinsCount 1024
Struct(SharedCmdlineState)
{
String *positional_args;
u64 positional_args_count;
CommandlineArgNode *arg_bins[CommandlineArgBinsCount];
} extern shared_cmdline_state;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Bootstrap //~ Bootstrap

View File

@ -1,3 +0,0 @@
#if GstatIsEnabled
SharedGstatCtx _shared_gstat_ctx = ZI;
#endif

View File

@ -2,21 +2,9 @@
#if GstatIsEnabled #if GstatIsEnabled
Struct(SharedGstatCtx) #define SetGstat(name, value) Atomic64Set(&Base.gstat.name.v, (value))
{ #define AddGstat(name, value) Atomic64FetchAdd(&Base.gstat.name.v, (value))
Atomic64Padded GSTAT_SOCK_BYTES_SENT; #define GetGstat(name) Atomic64Fetch(&Base.gstat.name.v)
Atomic64Padded GSTAT_SOCK_BYTES_RECEIVED;
Atomic64Padded GSTAT_MEMORY_COMMITTED;
Atomic64Padded GSTAT_MEMORY_RESERVED;
Atomic64Padded GSTAT_NUM_ARENAS;
Atomic64Padded GSTAT_DEBUG_STEPS;
};
extern SharedGstatCtx _shared_gstat_ctx;
#define SetGstat(name, value) Atomic64Set(&_shared_gstat_ctx.name.v, (value))
#define AddGstat(name, value) Atomic64FetchAdd(&_shared_gstat_ctx.name.v, (value))
#define GetGstat(name) Atomic64Fetch(&_shared_gstat_ctx.name.v)
#else #else

View File

@ -26,6 +26,7 @@
# include "base_resource.h" # include "base_resource.h"
# include "base_controller.h" # include "base_controller.h"
# include "base_async.h" # include "base_async.h"
# include "base_state.h"
#elif IsLanguageG #elif IsLanguageG
# include "base_shader.gh" # include "base_shader.gh"
#endif #endif
@ -40,7 +41,6 @@
# include "base_string.c" # include "base_string.c"
# include "base_cmdline.c" # include "base_cmdline.c"
# include "base_uni.c" # include "base_uni.c"
# include "base_gstat.c"
# include "base_buddy.c" # include "base_buddy.c"
# include "base_math.c" # include "base_math.c"
# include "base_rand.c" # include "base_rand.c"
@ -48,6 +48,7 @@
# include "base_resource.c" # include "base_resource.c"
# include "base_controller.c" # include "base_controller.c"
# include "base_async.c" # include "base_async.c"
# include "base_state.c"
#endif #endif
//- Include base_win32 //- Include base_win32

View File

@ -129,11 +129,6 @@ Global Readonly LogLevelSettings log_settings[LogLevel_Count] = {
# define LogDebugF(...) # define LogDebugF(...)
#endif #endif
////////////////////////////////////////////////////////////
//~ @hookdecl Bootstrap
void BootstrapLogs(String logfile_path);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ @hookdecl Log //~ @hookdecl Log

View File

@ -1,11 +1,8 @@
SharedResourceState shared_resource_state = ZI;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Bootstrap //~ Bootstrap
void BootstrapResources(u64 archive_strings_count, String *archive_strings) void BootstrapResources(u64 archive_strings_count, String *archive_strings)
{ {
SharedResourceState *g = &shared_resource_state;
Arena *perm = PermArena(); Arena *perm = PermArena();
for (u64 archive_string_index = 0; archive_string_index < archive_strings_count; ++archive_string_index) for (u64 archive_string_index = 0; archive_string_index < archive_strings_count; ++archive_string_index)
{ {
@ -32,11 +29,11 @@ void BootstrapResources(u64 archive_strings_count, String *archive_strings)
entry->data = STRING(data_len, archive.text + data_start); entry->data = STRING(data_len, archive.text + data_start);
entry->hash = HashFnv64(Fnv64Basis, entry->name); entry->hash = HashFnv64(Fnv64Basis, entry->name);
ResourceEntryBin *bin = &g->bins[entry->hash % NumResourceEntryBins]; ResourceEntryBin *bin = &Base.resource.bins[entry->hash % countof(Base.resource.bins)];
SllQueuePushN(bin->first, bin->last, entry, next_in_bin); SllQueuePushN(bin->first, bin->last, entry, next_in_bin);
SllQueuePushN(g->first_entry, g->last_entry, entry, next); SllQueuePushN(Base.resource.first_entry, Base.resource.last_entry, entry, next);
} }
g->entries_count += num_entries; Base.resource.entries_count += num_entries;
} }
} }
} }
@ -60,8 +57,7 @@ ResourceKey ResourceKeyFromStore(ResourceStore *store, String name)
ResourceEntry *ResourceEntryFromHash(u64 hash) ResourceEntry *ResourceEntryFromHash(u64 hash)
{ {
ResourceEntry *result = 0; ResourceEntry *result = 0;
SharedResourceState *g = &shared_resource_state; ResourceEntryBin *bin = &Base.resource.bins[hash % countof(Base.resource.bins)];
ResourceEntryBin *bin = &g->bins[hash % NumResourceEntryBins];
for (ResourceEntry *e = bin->first; e; e = e->next_in_bin) for (ResourceEntry *e = bin->first; e; e = e->next_in_bin)
{ {
if (e->hash == hash) if (e->hash == hash)

View File

@ -17,19 +17,6 @@ Struct(ResourceEntryBin)
ResourceEntry *last; ResourceEntry *last;
}; };
////////////////////////////////////////////////////////////
//~ State types
#define NumResourceEntryBins 4096
Struct(SharedResourceState)
{
ResourceEntry *first_entry;
ResourceEntry *last_entry;
u64 entries_count;
ResourceEntryBin bins[NumResourceEntryBins];
} extern shared_resource_state;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Bootstrap //~ Bootstrap

2
src/base/base_state.c Normal file
View File

@ -0,0 +1,2 @@
BaseState Base = ZI;
ThreadLocal BaseThreadLocalState Base_tl = ZI;

59
src/base/base_state.h Normal file
View File

@ -0,0 +1,59 @@
////////////////////////////////////////////////////////////
//~ Global state
Struct(BaseState)
{
/* Command line */
struct
{
String *positional_args;
u64 positional_args_count;
CommandlineArgNode *arg_bins[1024];
} cmdline;
/* Resources */
struct
{
u64 entries_count;
ResourceEntry *first_entry;
ResourceEntry *last_entry;
ResourceEntryBin bins[4096];
} resource;
/* Stats */
struct
{
Atomic64Padded SockBytesSent;
Atomic64Padded SockBytesReceived;
Atomic64Padded MemoryCommitted;
Atomic64Padded MemoryReserved;
Atomic64Padded NumArenas;
Atomic64Padded DebugSteps;
} gstat;
/* Async */
struct
{
Mutex mutex;
u64 callback_nodes_count;
AsyncTickCallbackNode *first_callback_node;
AsyncTickCallbackNode *last_callback_node;
AsyncWorkerState worker;
} async;
};
extern BaseState Base;
////////////////////////////////////////////////////////////
//~ Thread-local state
Struct(BaseThreadLocalState)
{
struct
{
Arena *perm;
Arena *scratch[2];
} arenas;
};
extern ThreadLocal BaseThreadLocalState Base_tl;

View File

@ -12,7 +12,7 @@ void WaveSyncEx(WaveLaneCtx *lane, u64 spin_count)
if (blocked_count == lanes_count) if (blocked_count == lanes_count)
{ {
Atomic32Set(&wave->sync_count.v, 0); Atomic32Set(&wave->sync_count.v, 0);
Atomic64FetchAdd(&wave->sync_gen.v, sync_gen + 1); Atomic64Set(&wave->sync_gen.v, sync_gen + 1);
FutexWakeNeq(&wave->sync_gen.v); FutexWakeNeq(&wave->sync_gen.v);
} }
else else

View File

@ -1,4 +1,4 @@
W32_SharedState W32_shared_state = ZI; W32_State W32 = ZI;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Win32 embedded data //~ Win32 embedded data
@ -40,7 +40,7 @@ BOOL W32_FindEmbeddedRcData(HMODULE module, LPCWSTR type, LPWSTR wstr_entry_name
StringList GetRawCommandline(void) StringList GetRawCommandline(void)
{ {
return W32_shared_state.raw_command_line; return W32.raw_command_line;
} }
void Echo(String msg) void Echo(String msg)
@ -92,10 +92,9 @@ b32 IsRunningInDebugger(void)
i64 TimeNs(void) i64 TimeNs(void)
{ {
struct W32_SharedState *g = &W32_shared_state;
LARGE_INTEGER qpc; LARGE_INTEGER qpc;
QueryPerformanceCounter(&qpc); QueryPerformanceCounter(&qpc);
i64 result = (qpc.QuadPart - g->timer_start_qpc) * g->ns_per_qpc; i64 result = (qpc.QuadPart - W32.timer_start_qpc) * W32.ns_per_qpc;
return result; return result;
} }
@ -165,6 +164,11 @@ CpuTopologyInfo GetCpuTopologyInfo(void)
return res; return res;
} }
void SleepSeconds(f64 seconds)
{
Sleep(seconds / 1000.0);
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ @hookimpl Swap //~ @hookimpl Swap
@ -231,20 +235,18 @@ void WriteSwappedState(String name, String data)
void OnExit(ExitFunc *func) void OnExit(ExitFunc *func)
{ {
W32_SharedState *g = &W32_shared_state; i32 index = Atomic32FetchAdd(&W32.num_exit_funcs, 1);
i32 index = Atomic32FetchAdd(&g->num_exit_funcs, 1); if (index >= countof(W32.exit_funcs))
if (index >= W32_MaxOnExitFuncs)
{ {
Panic(Lit("Maximum on exit functions registered")); Panic(Lit("Maximum on exit functions registered"));
} }
g->exit_funcs[index] = func; W32.exit_funcs[index] = func;
} }
void SignalExit(i32 code) void SignalExit(i32 code)
{ {
W32_SharedState *g = &W32_shared_state; Atomic32Set(&W32.exit_code, code);
Atomic32Set(&g->exit_code, code); SetEvent(W32.exit_event);
SetEvent(g->exit_event);
} }
void ExitNow(i32 code) void ExitNow(i32 code)
@ -252,34 +254,168 @@ void ExitNow(i32 code)
ExitProcess(code); ExitProcess(code);
} }
////////////////////////////////////////////////////////////
//~ Log
void W32_BootstrapLogs(String logfile_path)
{
W32.logs_arena = AcquireArena(Gibi(64));
W32.log_msgs_arena = AcquireArena(Gibi(64));
W32.readable_log_events = ArenaNext(W32.logs_arena, LogEvent);
if (logfile_path.len > 0)
{
TempArena scratch = BeginScratchNoConflict();
{
wchar_t *path_wstr = WstrFromString(scratch.arena, logfile_path);
W32.logfile = CreateFileW(path_wstr,
FILE_APPEND_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
0);
}
EndScratch(scratch);
}
Atomic32Set(&W32.logs_initialized, 1);
}
void W32_Log(i32 level, String msg)
{
TempArena scratch = BeginScratchNoConflict();
if (Atomic32Fetch(&W32.logs_initialized))
{
LogLevelSettings settings = log_settings[level];
if (level < 0 || level >= LogLevel_Count)
{
Panic(Lit("Invalid log level"));
}
DateTime datetime = LocalDateTime();
i64 now_ns = TimeNs();
i32 thread_id = GetCurrentThreadId();
//- Log message to file
/* TODO: Log asynchronously */
{
String shorthand = settings.shorthand;
String msg_formatted = StringF(
scratch.arena,
"[%F:%F:%F.%F] <%F> [%F] %F\n",
/* Time */
FmtUint(datetime.hour, .z = 2),
FmtUint(datetime.minute, .z = 2),
FmtUint(datetime.second, .z = 2),
FmtUint(datetime.milliseconds, .z = 3),
/* Thread id */
FmtUint(thread_id, .z = 5),
/* Level */
FmtString(shorthand),
/* Message */
FmtString(msg)
);
WriteFile(W32.logfile, msg_formatted.text, msg_formatted.len, 0, 0);
}
//- Log message to queue
/* TODO: Log asynchronously */
LockTicketMutex(&W32.logs_tm);
{
/* Get staged data */
LogEvent *ev = PushStruct(W32.logs_arena, LogEvent);
ev->msg = PushString(W32.log_msgs_arena, msg);
ev->datetime = datetime;
ev->time_ns = now_ns;
ev->level = level;
ev->thread_id = thread_id;
ev->id = W32.logs_count++;
ev->level_id = W32.log_level_counts[level]++;
Atomic64Set(&W32.readable_logs_count, W32.logs_count);
}
UnlockTicketMutex(&W32.logs_tm);
}
EndScratch(scratch);
}
////////////////////////////////////////////////////////////
//~ @hookimpl Log
/* Panic log function is separate to enforce zero side effects other than
* immediately writing to log file. */
void LogPanic(String msg)
{
if (Atomic32Fetch(&W32.logs_initialized))
{
String beg = Lit("******** PANICKING ********\n");
String end = Lit("\n***************************\n");
WriteFile(W32.logfile, beg.text, beg.len, 0, 0);
WriteFile(W32.logfile, msg.text, msg.len, 0, 0);
WriteFile(W32.logfile, end.text, end.len, 0, 0);
}
}
void Log_(i32 level, String msg)
{
W32_Log(level, msg);
}
void LogF_(i32 level, String fmt, ...)
{
if (Atomic32Fetch(&W32.logs_initialized))
{
TempArena scratch = BeginScratchNoConflict();
va_list args;
va_start(args, fmt);
{
String msg = FormatString(scratch.arena, fmt, FmtArgsFromVaList(scratch.arena, args));
W32_Log(level, msg);
}
va_end(args);
EndScratch(scratch);
}
}
LogEventsArray GetLogEvents(void)
{
LogEventsArray result = ZI;
result.count = Atomic64Fetch(&W32.readable_logs_count);
if (result.count > 0)
{
result.logs = W32.readable_log_events;
}
return result;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Main //~ Main
i32 W32_Main(void) i32 W32_Main(void)
{ {
W32_SharedState *g = &W32_shared_state;
/* Init time */ /* Init time */
{ {
LARGE_INTEGER qpf; LARGE_INTEGER qpf;
QueryPerformanceFrequency(&qpf); QueryPerformanceFrequency(&qpf);
g->ns_per_qpc = 1000000000 / qpf.QuadPart; W32.ns_per_qpc = 1000000000 / qpf.QuadPart;
} }
{ {
LARGE_INTEGER qpc; LARGE_INTEGER qpc;
QueryPerformanceCounter(&qpc); QueryPerformanceCounter(&qpc);
g->timer_start_qpc = qpc.QuadPart; W32.timer_start_qpc = qpc.QuadPart;
} }
/* Setup events */ /* Setup events */
g->panic_event = CreateEventW(0, 1, 0, 0); W32.panic_event = CreateEventW(0, 1, 0, 0);
g->exit_event = CreateEventW(0, 1, 0, 0); W32.exit_event = CreateEventW(0, 1, 0, 0);
g->main_thread_id = GetCurrentThreadId(); W32.main_thread_id = GetCurrentThreadId();
SetThreadDescription(GetCurrentThread(), L"Main thread"); SetThreadDescription(GetCurrentThread(), L"Main thread");
/* Query system info */ /* Query system info */
GetSystemInfo(&g->info); GetSystemInfo(&W32.info);
/* Init main thread */ /* Init main thread */
W32_InitCurrentThread(Lit("Main")); W32_InitCurrentThread(Lit("Main"));
@ -299,7 +435,7 @@ i32 W32_Main(void)
PushStringToList(perm, &args_list, arg); PushStringToList(perm, &args_list, arg);
} }
} }
g->raw_command_line = args_list; W32.raw_command_line = args_list;
} }
////////////////////////////// //////////////////////////////
@ -310,7 +446,7 @@ i32 W32_Main(void)
/* Bootstrap log system */ /* Bootstrap log system */
/* FIXME: Remove hardcoded log path */ /* FIXME: Remove hardcoded log path */
BootstrapLogs(Lit("log.log")); W32_BootstrapLogs(Lit("log.log"));
LogInfoF("Main thread ID: %F", FmtUint(ThreadId())); LogInfoF("Main thread ID: %F", FmtUint(ThreadId()));
/* Bootstrap resource system */ /* Bootstrap resource system */
@ -324,7 +460,7 @@ i32 W32_Main(void)
BootstrapAsync(); BootstrapAsync();
/* Bootstrap layers */ /* Bootstrap layers */
if (!Atomic32Fetch(&g->panicking)) if (!Atomic32Fetch(&W32.panicking))
{ {
BootstrapLayers(); BootstrapLayers();
} }
@ -333,11 +469,11 @@ i32 W32_Main(void)
//- Wait for exit signal //- Wait for exit signal
/* Wait for exit start or panic */ /* Wait for exit start or panic */
if (!Atomic32Fetch(&g->panicking)) if (!Atomic32Fetch(&W32.panicking))
{ {
HANDLE handles[] = { HANDLE handles[] = {
g->exit_event, W32.exit_event,
g->panic_event, W32.panic_event,
}; };
DWORD wake = WaitForMultipleObjects(countof(handles), handles, 0, INFINITE); DWORD wake = WaitForMultipleObjects(countof(handles), handles, 0, INFINITE);
} }
@ -346,24 +482,24 @@ i32 W32_Main(void)
//- Shutdown //- Shutdown
/* Run exit callbacks */ /* Run exit callbacks */
if (!Atomic32Fetch(&g->panicking)) if (!Atomic32Fetch(&W32.panicking))
{ {
i32 num_funcs = Atomic32Fetch(&g->num_exit_funcs); i32 num_funcs = Atomic32Fetch(&W32.num_exit_funcs);
for (i32 idx = num_funcs - 1; idx >= 0; --idx) for (i32 idx = num_funcs - 1; idx >= 0; --idx)
{ {
ExitFunc *func = g->exit_funcs[idx]; ExitFunc *func = W32.exit_funcs[idx];
func(); func();
} }
} }
/* Exit */ /* Exit */
if (Atomic32Fetch(&g->panicking)) if (Atomic32Fetch(&W32.panicking))
{ {
WaitForSingleObject(g->panic_event, INFINITE); WaitForSingleObject(W32.panic_event, INFINITE);
MessageBoxExW(0, g->panic_wstr, L"Fatal error", MB_ICONSTOP | MB_SETFOREGROUND | MB_TOPMOST, 0); MessageBoxExW(0, W32.panic_wstr, L"Fatal error", MB_ICONSTOP | MB_SETFOREGROUND | MB_TOPMOST, 0);
Atomic32FetchTestSet(&g->exit_code, 0, 1); Atomic32FetchTestSet(&W32.exit_code, 0, 1);
} }
return Atomic32Fetch(&g->exit_code); return Atomic32Fetch(&W32.exit_code);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -56,9 +56,7 @@ Struct(W32_FindEmbeddedDataCtx)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ State types //~ State types
#define W32_MaxOnExitFuncs 4096 Struct(W32_State)
Struct(W32_SharedState)
{ {
SYSTEM_INFO info; SYSTEM_INFO info;
u32 main_thread_id; u32 main_thread_id;
@ -71,15 +69,33 @@ Struct(W32_SharedState)
StringList raw_command_line; StringList raw_command_line;
//- Application control flow //- Application control flow
Atomic32 panicking; Atomic32 panicking;
wchar_t panic_wstr[4096]; wchar_t panic_wstr[4096];
HANDLE panic_event; HANDLE panic_event;
HANDLE exit_event; HANDLE exit_event;
//- Exit funcs //- Exit funcs
Atomic32 num_exit_funcs; Atomic32 num_exit_funcs;
ExitFunc *exit_funcs[W32_MaxOnExitFuncs]; ExitFunc *exit_funcs[4096];
} extern W32_shared_state;
//- Logs
HANDLE logfile;
Atomic32 logs_initialized;
TicketMutex logs_tm;
Arena *log_msgs_arena;
Arena *logs_arena;
u64 logs_count;
u64 log_level_counts[LogLevel_Count];
LogEvent *readable_log_events;
Atomic64 readable_logs_count;
};
extern W32_State W32;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Embedded data initialization //~ Embedded data initialization
@ -87,6 +103,12 @@ Struct(W32_SharedState)
#define W32_EmbeddedDataPrefix EMBEDDED_RESOURCE_DATA__ #define W32_EmbeddedDataPrefix EMBEDDED_RESOURCE_DATA__
BOOL W32_FindEmbeddedRcData(HMODULE module, LPCWSTR type, LPWSTR wstr_entry_name, LONG_PTR udata); BOOL W32_FindEmbeddedRcData(HMODULE module, LPCWSTR type, LPWSTR wstr_entry_name, LONG_PTR udata);
////////////////////////////////////////////////////////////
//~ Log
void W32_BootstrapLogs(String logfile_path);
void W32_Log(i32 level, String msg);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Main //~ Main

View File

@ -1,7 +1,6 @@
//- Api //- Api
#include "base_win32.h" #include "base_win32.h"
#include "base_win32_wave.h" #include "base_win32_wave.h"
#include "base_win32_log.h"
//- Impl //- Impl
#include "base_win32.c" #include "base_win32.c"
@ -9,4 +8,3 @@
#include "base_win32_memory.c" #include "base_win32_memory.c"
#include "base_win32_wave.c" #include "base_win32_wave.c"
#include "base_win32_time.c" #include "base_win32_time.c"
#include "base_win32_log.c"

View File

@ -1,145 +0,0 @@
W32_SharedLogState W32_shared_log_state = ZI;
////////////////////////////////////////////////////////////
//~ @hookimpl Bootstrap
void BootstrapLogs(String logfile_path)
{
W32_SharedLogState *g = &W32_shared_log_state;
g->logs_arena = AcquireArena(Gibi(64));
g->log_msgs_arena = AcquireArena(Gibi(64));
g->readable_log_events = ArenaNext(g->logs_arena, LogEvent);
if (logfile_path.len > 0)
{
TempArena scratch = BeginScratchNoConflict();
{
wchar_t *path_wstr = WstrFromString(scratch.arena, logfile_path);
g->logfile = CreateFileW(path_wstr,
FILE_APPEND_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
0);
}
EndScratch(scratch);
}
Atomic32Set(&g->initialized, 1);
}
////////////////////////////////////////////////////////////
//~ Log
void W32_Log(i32 level, String msg)
{
W32_SharedLogState *g = &W32_shared_log_state;
TempArena scratch = BeginScratchNoConflict();
if (Atomic32Fetch(&g->initialized))
{
LogLevelSettings settings = log_settings[level];
if (level < 0 || level >= LogLevel_Count)
{
Panic(Lit("Invalid log level"));
}
DateTime datetime = LocalDateTime();
i64 now_ns = TimeNs();
i32 thread_id = GetCurrentThreadId();
//- Log message to file
/* TODO: Log asynchronously */
{
String shorthand = settings.shorthand;
String msg_formatted = StringF(
scratch.arena,
"[%F:%F:%F.%F] <%F> [%F] %F\n",
/* Time */
FmtUint(datetime.hour, .z = 2),
FmtUint(datetime.minute, .z = 2),
FmtUint(datetime.second, .z = 2),
FmtUint(datetime.milliseconds, .z = 3),
/* Thread id */
FmtUint(thread_id, .z = 5),
/* Level */
FmtString(shorthand),
/* Message */
FmtString(msg)
);
WriteFile(g->logfile, msg_formatted.text, msg_formatted.len, 0, 0);
}
//- Log message to queue
/* TODO: Log asynchronously */
LockTicketMutex(&g->logs_tm);
{
/* Get staged data */
LogEvent *ev = PushStruct(g->logs_arena, LogEvent);
ev->msg = PushString(g->log_msgs_arena, msg);
ev->datetime = datetime;
ev->time_ns = now_ns;
ev->level = level;
ev->thread_id = thread_id;
ev->id = g->logs_count++;
ev->level_id = g->log_level_counts[level]++;
Atomic64Set(&g->readable_logs_count, g->logs_count);
}
UnlockTicketMutex(&g->logs_tm);
}
EndScratch(scratch);
}
////////////////////////////////////////////////////////////
//~ @hookimpl Log
/* Panic log function is separate to enforce zero side effects other than
* immediately writing to log file. */
void LogPanic(String msg)
{
W32_SharedLogState *g = &W32_shared_log_state;
if (Atomic32Fetch(&g->initialized))
{
String beg = Lit("******** PANICKING ********\n");
String end = Lit("\n***************************\n");
WriteFile(g->logfile, beg.text, beg.len, 0, 0);
WriteFile(g->logfile, msg.text, msg.len, 0, 0);
WriteFile(g->logfile, end.text, end.len, 0, 0);
}
}
void Log_(i32 level, String msg)
{
W32_Log(level, msg);
}
void LogF_(i32 level, String fmt, ...)
{
W32_SharedLogState *g = &W32_shared_log_state;
if (Atomic32Fetch(&g->initialized))
{
TempArena scratch = BeginScratchNoConflict();
va_list args;
va_start(args, fmt);
{
String msg = FormatString(scratch.arena, fmt, FmtArgsFromVaList(scratch.arena, args));
W32_Log(level, msg);
}
va_end(args);
EndScratch(scratch);
}
}
LogEventsArray GetLogEvents(void)
{
W32_SharedLogState *g = &W32_shared_log_state;
LogEventsArray result = ZI;
result.count = Atomic64Fetch(&g->readable_logs_count);
if (result.count > 0)
{
result.logs = g->readable_log_events;
}
return result;
}

View File

@ -1,21 +0,0 @@
////////////////////////////////////////////////////////////
//~ State types
Struct(W32_SharedLogState)
{
HANDLE logfile;
Atomic32 initialized;
TicketMutex logs_tm;
Arena *log_msgs_arena;
Arena *logs_arena;
u64 logs_count;
u64 log_level_counts[LogLevel_Count];
LogEvent *readable_log_events;
Atomic64 readable_logs_count;
} extern W32_shared_log_state;
////////////////////////////////////////////////////////////
//~ Log
void W32_Log(i32 level, String msg);

View File

@ -5,16 +5,15 @@ void W32_InitCurrentThread(String name)
{ {
/* Init thread arenas */ /* Init thread arenas */
{ {
ThreadArenasCtx *tctx = &t_arena_ctx; Base_tl.arenas.perm = AcquireArena(Gibi(64));
tctx->perm_arena = AcquireArena(Gibi(64)); for (i32 i = 0; i < (i32)countof(Base_tl.arenas.scratch); ++i)
for (i32 i = 0; i < (i32)countof(tctx->scratch_arenas); ++i)
{ {
tctx->scratch_arenas[i] = AcquireArena(Gibi(64)); Base_tl.arenas.scratch[i] = AcquireArena(Gibi(64));
} }
} }
Arena *perm = PermArena(); Arena *perm = PermArena();
/* Set thread name */
wchar_t *thread_name_wstr = WstrFromString(perm, name); wchar_t *thread_name_wstr = WstrFromString(perm, name);
SetThreadDescription(GetCurrentThread(), thread_name_wstr); SetThreadDescription(GetCurrentThread(), thread_name_wstr);

View File

@ -138,15 +138,15 @@ Struct(CLD_EpaData)
#if COLLIDER_DEBUG #if COLLIDER_DEBUG
void CLD_DebugBreakable(void); void CLD_DebugBreakable(void);
#define CLD_DBGSTEP \ #define CLD_DBGSTEP \
dbg_step++; \ dbg_step++; \
if (dbg_step >= GetGstat(GSTAT_DEBUG_STEPS)) \ if (dbg_step >= GetGstat(DebugSteps)) \
{ \ { \
goto abort; \ goto abort; \
} \ } \
else if (dbg_step >= GetGstat(GSTAT_DEBUG_STEPS) - 1) \ else if (dbg_step >= GetGstat(DebugSteps) - 1) \
{ \ { \
CLD_DebugBreakable(); \ CLD_DebugBreakable(); \
} (void)0 } (void)0
#else #else
#define CLD_DBGSTEP #define CLD_DBGSTEP

View File

@ -3,7 +3,7 @@ GC_State GC = ZI;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Bootstrap //~ Bootstrap
void GC_BootStrap(void) void GC_Bootstrap(void)
{ {
OnAsyncTick(GC_AsyncTick); OnAsyncTick(GC_AsyncTick);
} }

View File

@ -11,6 +11,8 @@
@IncludeC glyph_cache.h @IncludeC glyph_cache.h
@Bootstrap GC_Bootstrap
////////////////////////////// //////////////////////////////
//- Impl //- Impl

View File

@ -2848,8 +2848,6 @@ void G_D12_CollectionWorkerEntryPoint(WaveLaneCtx *lane)
{ {
for (;;) for (;;)
{ {
P_SleepSeconds(0.100);
/* Copy print-buffers to readback */ /* Copy print-buffers to readback */
for (G_QueueKind queue_kind = 0; queue_kind < G_NumQueues; ++queue_kind) for (G_QueueKind queue_kind = 0; queue_kind < G_NumQueues; ++queue_kind)
{ {

View File

@ -137,6 +137,5 @@ i64 P_GetCurrentTimerPeriodNs(void);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ @hookdecl Sleep //~ @hookdecl Sleep
void P_SleepSeconds(f64 seconds);
void P_SleepPrecise(i64 sleep_time_ns); void P_SleepPrecise(i64 sleep_time_ns);
void P_SleepFrame(i64 last_frame_time_ns, i64 target_dt_ns); void P_SleepFrame(i64 last_frame_time_ns, i64 target_dt_ns);

View File

@ -838,7 +838,7 @@ P_SockReadResult P_ReadSock(Arena *arena, P_Sock *sock)
result.address = P_W32_PlatformAddressFromWin32Address(ws_addr); result.address = P_W32_PlatformAddressFromWin32Address(ws_addr);
if (size >= 0) if (size >= 0)
{ {
AddGstat(GSTAT_SOCK_BYTES_RECEIVED, size); AddGstat(SockBytesReceived, size);
result.data.text = read_buff.text; result.data.text = read_buff.text;
result.data.len = size; result.data.len = size;
result.valid = 1; result.valid = 1;
@ -867,7 +867,7 @@ void P_WriteSock(P_Sock *sock, P_Address address, String data)
i32 size = sendto(ws->sock, (char *)data.text, data.len, 0, &ws_addr.sa, ws_addr.size); i32 size = sendto(ws->sock, (char *)data.text, data.len, 0, &ws_addr.sa, ws_addr.size);
if (size > 0) if (size > 0)
{ {
AddGstat(GSTAT_SOCK_BYTES_SENT, size); AddGstat(SockBytesSent, size);
} }
#if IsRtcEnabled #if IsRtcEnabled
if (size != (i32)data.len) if (size != (i32)data.len)
@ -978,11 +978,6 @@ i64 P_GetCurrentTimerPeriodNs(void)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ @hookimpl Sleep //~ @hookimpl Sleep
void P_SleepSeconds(f64 seconds)
{
Sleep(seconds / 1000.0);
}
void P_SleepPrecise(i64 sleep_time_ns) void P_SleepPrecise(i64 sleep_time_ns)
{ {
i64 now_ns = TimeNs(); i64 now_ns = TimeNs();