cache_coherent_ is what I'm going with for now based off of studying it further. I really really don't like the "atomic" as the verbiage phrase. It conveys nothing about what the execution engine is actually doing with the thread caches or the bus snoop.
221 lines
7.5 KiB
Odin
221 lines
7.5 KiB
Odin
package host
|
|
|
|
import "core:thread"
|
|
import "core:sync"
|
|
|
|
Path_Logs :: "../logs"
|
|
when ODIN_OS == .Windows
|
|
{
|
|
Path_Sectr_Module :: "sectr.dll"
|
|
Path_Sectr_Live_Module :: "sectr_live.dll"
|
|
Path_Sectr_Debug_Symbols :: "sectr.pdb"
|
|
Path_Sectr_Spall_Record :: "sectr.spall"
|
|
}
|
|
|
|
// Only static memory host has.
|
|
host_memory: ProcessMemory
|
|
|
|
@(thread_local)
|
|
thread_memory: ThreadMemory
|
|
|
|
load_client_api :: proc(version_id: int) -> (loaded_module: Client_API)
|
|
{
|
|
write_time, result := file_last_write_time_by_name("sectr.dll")
|
|
if result != OS_ERROR_NONE {
|
|
panic_contextless( "Could not resolve the last write time for sectr")
|
|
}
|
|
|
|
thread_sleep( Millisecond * 100 )
|
|
|
|
live_file := Path_Sectr_Live_Module
|
|
file_copy_sync( Path_Sectr_Module, live_file, allocator = context.temp_allocator )
|
|
|
|
lib, load_result := os_lib_load( live_file )
|
|
if ! load_result {
|
|
panic( "Failed to load the sectr module." )
|
|
}
|
|
|
|
startup := cast( type_of( host_memory.client_api.startup)) os_lib_get_proc(lib, "startup")
|
|
tick_lane_startup := cast( type_of( host_memory.client_api.tick_lane_startup)) os_lib_get_proc(lib, "tick_lane_startup")
|
|
hot_reload := cast( type_of( host_memory.client_api.hot_reload)) os_lib_get_proc(lib, "hot_reload")
|
|
tick_lane := cast( type_of( host_memory.client_api.tick_lane)) os_lib_get_proc(lib, "tick_lane")
|
|
clean_frame := cast( type_of( host_memory.client_api.clean_frame)) os_lib_get_proc(lib, "clean_frame")
|
|
if startup == nil do panic("Failed to load sectr.startup symbol" )
|
|
if tick_lane_startup == nil do panic("Failed to load sectr.tick_lane_startup symbol" )
|
|
if hot_reload == nil do panic("Failed to load sectr.hot_reload symbol" )
|
|
if tick_lane == nil do panic("Failed to load sectr.tick_lane symbol" )
|
|
if clean_frame == nil do panic("Failed to load sectr.clean_frmae symbol" )
|
|
|
|
loaded_module.lib = lib
|
|
loaded_module.write_time = write_time
|
|
loaded_module.lib_version = version_id
|
|
loaded_module.startup = startup
|
|
loaded_module.tick_lane_startup = tick_lane_startup
|
|
loaded_module.hot_reload = hot_reload
|
|
loaded_module.tick_lane = tick_lane
|
|
loaded_module.clean_frame = clean_frame
|
|
return
|
|
}
|
|
|
|
master_prepper_proc :: proc(thread: ^SysThread) {}
|
|
main :: proc()
|
|
{
|
|
// Setup host arenas
|
|
arena_init(& host_memory.host_persist, host_memory.host_persist_buf[:])
|
|
arena_init(& host_memory.host_scratch, host_memory.host_scratch_buf[:])
|
|
context.allocator = arena_allocator(& host_memory.host_persist)
|
|
context.temp_allocator = arena_allocator(& host_memory.host_scratch)
|
|
// Setup the profiler
|
|
{
|
|
buffer_backing := make([]u8, SPALL_BUFFER_DEFAULT_SIZE * 4)
|
|
host_memory.spall_profiler.ctx = spall_context_create(Path_Sectr_Spall_Record)
|
|
host_memory.spall_profiler.buffer = spall_buffer_create(buffer_backing)
|
|
}
|
|
// Setu the "Master Prepper" thread
|
|
thread_memory.id = .Master_Prepper
|
|
thread_id := thread_current_id()
|
|
{
|
|
using thread_memory
|
|
system_ctx = & host_memory.threads[WorkerID.Master_Prepper]
|
|
system_ctx.creation_allocator = {}
|
|
system_ctx.procedure = master_prepper_proc
|
|
when ODIN_OS == .Windows {
|
|
// system_ctx.win32_thread = w32_get_current_thread()
|
|
// system_ctx.win32_thread_id = w32_get_current_thread_id()
|
|
system_ctx.id = cast(int) system_ctx.win32_thread_id
|
|
}
|
|
free_all(context.temp_allocator)
|
|
}
|
|
// Setup the logger
|
|
{
|
|
fmt_backing := make([]byte, 32 * Kilo)
|
|
defer free_all(context.temp_allocator)
|
|
|
|
// Generating the logger's name, it will be used when the app is shutting down.
|
|
path_logger_finalized : string
|
|
{
|
|
startup_time := time_now()
|
|
year, month, day := time_date( startup_time)
|
|
hour, min, sec := time_clock_from_time( startup_time)
|
|
|
|
if ! os_is_directory( Path_Logs ) {
|
|
os_make_directory( Path_Logs )
|
|
}
|
|
timestamp := str_pfmt_buffer( fmt_backing, "%04d-%02d-%02d_%02d-%02d-%02d", year, month, day, hour, min, sec)
|
|
path_logger_finalized = str_pfmt_buffer( fmt_backing, "%s/sectr_%v.log", Path_Logs, timestamp)
|
|
}
|
|
logger_init( & host_memory.logger, "Sectr Host", str_pfmt_buffer( fmt_backing, "%s/sectr.log", Path_Logs ) )
|
|
context.logger = to_odin_logger( & host_memory.logger )
|
|
{
|
|
// Log System Context
|
|
builder := strbuilder_from_bytes( fmt_backing )
|
|
str_pfmt_builder( & builder, "Core Count: %v, ", os_core_count() )
|
|
str_pfmt_builder( & builder, "Page Size: %v", os_page_size() )
|
|
log_print( to_str(builder) )
|
|
}
|
|
}
|
|
context.logger = to_odin_logger( & host_memory.logger )
|
|
// Load the Enviornment API for the first-time
|
|
{
|
|
host_memory.client_api = load_client_api( 1 )
|
|
verify( host_memory.client_api.lib_version != 0, "Failed to initially load the sectr module" )
|
|
}
|
|
|
|
// Client API Startup
|
|
host_memory.host_api.sync_client_module = sync_client_api
|
|
host_memory.host_api.launch_tick_lane_thread = launch_tick_lane_thread
|
|
host_memory.client_api.startup(& host_memory, & thread_memory)
|
|
|
|
// Start the tick lanes
|
|
thread_wide_startup()
|
|
}
|
|
|
|
thread_wide_startup :: proc()
|
|
{
|
|
assert(thread_memory.id == .Master_Prepper)
|
|
if THREAD_TICK_LANES > 1 {
|
|
launch_tick_lane_thread(.Atomic_Accountant)
|
|
sync.barrier_init(& host_memory.client_api_sync_lock, THREAD_TICK_LANES)
|
|
}
|
|
host_tick_lane_startup(thread_memory.system_ctx)
|
|
}
|
|
|
|
@export
|
|
launch_tick_lane_thread :: proc(id : WorkerID) {
|
|
assert_contextless(thread_memory.id == .Master_Prepper)
|
|
// TODO(Ed): We need to make our own version of this that doesn't allocate memory.
|
|
lane_thread := thread.create(host_tick_lane_startup, .High)
|
|
lane_thread.user_index = int(id)
|
|
thread.start(lane_thread)
|
|
}
|
|
|
|
host_tick_lane_startup :: proc(lane_thread: ^SysThread) {
|
|
thread_memory.system_ctx = lane_thread
|
|
thread_memory.id = cast(WorkerID) lane_thread.user_index
|
|
host_memory.client_api.tick_lane_startup(& thread_memory)
|
|
|
|
host_tick_lane()
|
|
}
|
|
|
|
host_tick_lane :: proc()
|
|
{
|
|
delta_ns: Duration
|
|
|
|
host_tick := time_tick_now()
|
|
|
|
running : b64 = true
|
|
for ; running ;
|
|
{
|
|
profile("Host Tick")
|
|
sync_client_api()
|
|
|
|
running = host_memory.client_api.tick_lane( duration_seconds(delta_ns), delta_ns )
|
|
// host_memory.client_api.clean_frame()
|
|
|
|
delta_ns = time_tick_lap_time( & host_tick )
|
|
host_tick = time_tick_now()
|
|
}
|
|
}
|
|
|
|
@export
|
|
sync_client_api :: proc()
|
|
{
|
|
leader := sync.barrier_wait(& host_memory.client_api_sync_lock)
|
|
free_all(context.temp_allocator)
|
|
profile(#procedure)
|
|
if thread_memory.id == .Master_Prepper
|
|
{
|
|
write_time, result := file_last_write_time_by_name( Path_Sectr_Module );
|
|
if result == OS_ERROR_NONE && host_memory.client_api.write_time != write_time
|
|
{
|
|
cache_coherent_store(& host_memory.client_api_hot_reloaded, true)
|
|
|
|
version_id := host_memory.client_api.lib_version + 1
|
|
unload_client_api( & host_memory.client_api )
|
|
|
|
// Wait for pdb to unlock (linker may still be writting)
|
|
for ; file_is_locked( Path_Sectr_Debug_Symbols ) && file_is_locked( Path_Sectr_Live_Module ); {}
|
|
thread_sleep( Millisecond * 100 )
|
|
|
|
host_memory.client_api = load_client_api( version_id )
|
|
verify( host_memory.client_api.lib_version != 0, "Failed to hot-reload the sectr module" )
|
|
}
|
|
}
|
|
leader = sync.barrier_wait(& host_memory.client_api_sync_lock)
|
|
if cache_coherent_load(& host_memory.client_api_hot_reloaded)
|
|
{
|
|
host_memory.client_api.hot_reload(& host_memory, & thread_memory)
|
|
if thread_memory.id == .Master_Prepper {
|
|
cache_coherent_store(& host_memory.client_api_hot_reloaded, false)
|
|
}
|
|
}
|
|
}
|
|
|
|
unload_client_api :: proc( module : ^Client_API )
|
|
{
|
|
os_lib_unload( module.lib )
|
|
file_remove( Path_Sectr_Live_Module )
|
|
module^ = {}
|
|
log_print("Unloaded sectr API")
|
|
}
|