hot reload works with tick lanes and job worker loops!

This commit is contained in:
2025-10-15 00:44:14 -04:00
parent ed6a79fd78
commit 9f75d080a7
6 changed files with 109 additions and 62 deletions

View File

@@ -18,6 +18,7 @@ ModuleAPI :: struct {
startup: type_of( startup ),
tick_lane_startup: type_of( tick_lane_startup),
job_worker_startup: type_of( job_worker_startup),
hot_reload: type_of( hot_reload ),
tick_lane: type_of( tick_lane ),
clean_frame: type_of( clean_frame),
@@ -47,21 +48,46 @@ Threads will eventually return to their tick_lane upon completion.
@export
hot_reload :: proc(host_mem: ^ProcessMemory, thread_mem: ^ThreadMemory)
{
profile(#procedure)
thread = thread_mem
if thread.id == .Master_Prepper {
grime_set_profiler_module_context(& memory.spall_context)
sync_store(& memory, host_mem, .Release)
// Critical reference synchronization
{
thread = thread_mem
if thread.id == .Master_Prepper {
sync_store(& memory, host_mem, .Release)
grime_set_profiler_module_context(& memory.spall_context)
}
else {
for ; memory == nil; {
sync_load(& memory, .Acquire)
}
for ; thread == nil; {
thread = thread_mem
}
}
grime_set_profiler_thread_buffer(& thread.spall_buffer)
}
profile(#procedure)
// Do hot-reload stuff...
{
}
// Critical reference synchronization
{
leader := barrier_wait(& memory.lane_job_sync)
if thread.id == .Master_Prepper {
sync_store(& memory.client_api_hot_reloaded, false, .Release)
}
else {
for ; memory.client_api_hot_reloaded == true; {
sync_load(& memory.client_api_hot_reloaded, .Acquire)
}
}
leader = barrier_wait(& memory.lane_job_sync)
}
grime_set_profiler_thread_buffer(& thread.spall_buffer)
}
/*
Called by host_tick_lane_startup
Used for lane specific startup operations
The lane tick cannot be handled it, its call must be done by the host module.
(We need threads to not be within a client callstack in the even of a hot-reload)
*/
@export
tick_lane_startup :: proc(thread_mem: ^ThreadMemory)
@@ -73,8 +99,19 @@ tick_lane_startup :: proc(thread_mem: ^ThreadMemory)
profile(#procedure)
}
/*
@export
job_worker_startup :: proc(thread_mem: ^ThreadMemory)
{
if thread_mem.id != .Master_Prepper {
thread = thread_mem
grime_set_profiler_thread_buffer(& thread.spall_buffer)
}
profile(#procedure)
}
/*
Host handles the loop.
(We need threads to be outside of client callstack in the event of a hot-reload)
*/
@export
tick_lane :: proc(host_delta_time_ms: f64, host_delta_ns: Duration) -> (should_close: b64 = false)

View File

@@ -17,39 +17,42 @@ ProcessMemory :: struct {
// Host
host_persist_buf: [32 * Mega]byte,
host_scratch_buf: [64 * Mega]byte,
host_persist: Odin_Arena,
host_scratch: Odin_Arena,
host_api: Host_API,
host_persist: Odin_Arena, // Host Persistent (Non-Wipeable), for bad third-party static object allocation
host_scratch: Odin_Arena, // Host Temporary Wipable
host_api: Host_API, // Client -> Host Interface
// Textual Logging
logger: Logger,
logger: Logger,
path_logger_finalized: string,
// Profiling
spall_context: Spall_Context,
// TODO(Ed): Try out Superluminal's API!
// Multi-threading
threads: [MAX_THREADS](^SysThread),
job_system: JobSystemContext,
tick_lanes: int,
lane_sync: sync.Barrier,
job_hot_reload_sync: sync.Barrier, // Used to sync jobs with main thread during hot-reload junction.
tick_running: b64,
threads: [MAX_THREADS](^SysThread), // All threads are tracked here.
job_system: JobSystemContext, // State tracking for job system.
tick_running: b64, // When disabled will lead to shutdown of the process.
tick_lanes: int, // Runtime tracker of live tick lane threads
lane_sync: sync.Barrier, // Used to sync tick lanes during wide junctions.
job_hot_reload_sync: sync.Barrier, // Used to sync jobs with main thread during hot-reload junction.
lane_job_sync: sync.Barrier, // Used to sync tick lanes and job workers during hot-reload.
// Client Module
client_api_hot_reloaded: b64,
client_api: ModuleAPI,
client_memory: State,
client_api_hot_reloaded: b64, // Used to signal to threads when hot-reload paths should be taken.
client_api: ModuleAPI, // Host -> Client Interface
client_memory: State,
}
Host_API :: struct {
request_virtual_memory: #type proc(),
request_virtual_mapped_io: #type proc(),
request_virtual_memory: #type proc(), // All dynamic allocations will utilize vmem interfaces
request_virtual_mapped_io: #type proc(), // TODO(Ed): Figure out usage constraints of this.
}
ThreadMemory :: struct {
using _: ThreadWorkerContext,
// Per-thread profiling
spall_buffer_backing: [SPALL_BUFFER_DEFAULT_SIZE * 2]byte,
spall_buffer: Spall_Buffer,
}