app exit / shutdown callbacks
This commit is contained in:
parent
d646d3434c
commit
511364ee05
65
src/app.c
65
src/app.c
@ -21,10 +21,21 @@
|
|||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include "math.h"
|
#include "math.h"
|
||||||
|
|
||||||
|
struct exit_callback {
|
||||||
|
app_exit_callback_func *func;
|
||||||
|
struct exit_callback *next;
|
||||||
|
struct sys_thread thread;
|
||||||
|
};
|
||||||
|
|
||||||
GLOBAL struct {
|
GLOBAL struct {
|
||||||
struct arena arena;
|
struct arena arena;
|
||||||
struct string write_path;
|
struct string write_path;
|
||||||
struct sync_flag quit_sf;
|
struct sync_flag exit_sf;
|
||||||
|
|
||||||
|
/* Exit callbacks */
|
||||||
|
struct sys_mutex exit_callbacks_mutex;
|
||||||
|
struct arena exit_callbacks_arena;
|
||||||
|
struct exit_callback *exit_callbacks_head;
|
||||||
} G = { 0 }, DEBUG_ALIAS(G, G_app);
|
} G = { 0 }, DEBUG_ALIAS(G, G_app);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -85,13 +96,37 @@ INTERNAL struct sys_window_settings default_window_settings(struct sys_window *w
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Exit callbacks
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(exit_callback_thread_entry_point, vcallback)
|
||||||
|
{
|
||||||
|
struct exit_callback *callback = (struct exit_callback *)vcallback;
|
||||||
|
callback->func();
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_register_exit_callback(app_exit_callback_func *func)
|
||||||
|
{
|
||||||
|
sys_mutex_lock(&G.exit_callbacks_mutex);
|
||||||
|
{
|
||||||
|
struct exit_callback *callback = arena_push_zero(&G.exit_callbacks_arena, struct exit_callback);
|
||||||
|
callback->func = func;
|
||||||
|
callback->next = G.exit_callbacks_head;
|
||||||
|
G.exit_callbacks_head = callback;
|
||||||
|
}
|
||||||
|
sys_mutex_unlock(&G.exit_callbacks_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Entry point
|
* Entry point
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
void app_entry_point(void)
|
void app_entry_point(void)
|
||||||
{
|
{
|
||||||
G.quit_sf = sync_flag_alloc();
|
G.exit_sf = sync_flag_alloc();
|
||||||
|
G.exit_callbacks_mutex = sys_mutex_alloc();
|
||||||
|
G.exit_callbacks_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
|
||||||
u32 worker_count = 4;
|
u32 worker_count = 4;
|
||||||
{
|
{
|
||||||
@ -189,17 +224,25 @@ void app_entry_point(void)
|
|||||||
/* Show window */
|
/* Show window */
|
||||||
sys_window_show(&window);
|
sys_window_show(&window);
|
||||||
|
|
||||||
/* Wait for app_quit() */
|
/* Wait for app_exit() */
|
||||||
sync_flag_wait(&G.quit_sf);
|
sync_flag_wait(&G.exit_sf);
|
||||||
|
|
||||||
/* Shutdown threaded systems */
|
/* Call exit callbacks */
|
||||||
/* FIXME: Only wait on threads for a certain period of time before
|
/* FIXME: Only wait on threads for a certain period of time before
|
||||||
* forcing process exit (to prevent process hanging in the background
|
* forcing process exit (to prevent process hanging in the background
|
||||||
* when a thread gets stuck) */
|
* when a thread gets stuck) */
|
||||||
playback_shutdown();
|
sys_mutex_lock(&G.exit_callbacks_mutex);
|
||||||
user_shutdown();
|
{
|
||||||
game_shutdown();
|
/* Start callback threads */
|
||||||
work_shutdown();
|
for (struct exit_callback *callback = G.exit_callbacks_head; callback; callback = callback->next) {
|
||||||
|
callback->thread = sys_thread_init(&exit_callback_thread_entry_point, callback, STR("[P4] Exit callback thread"));
|
||||||
|
}
|
||||||
|
/* Wait on callback threads */
|
||||||
|
for (struct exit_callback *callback = G.exit_callbacks_head; callback; callback = callback->next) {
|
||||||
|
sys_thread_join(&callback->thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sys_mutex_unlock(&G.exit_callbacks_mutex);
|
||||||
|
|
||||||
/* Write window settings to file */
|
/* Write window settings to file */
|
||||||
{
|
{
|
||||||
@ -224,7 +267,7 @@ void app_entry_point(void)
|
|||||||
logf_info("Program exited normally");
|
logf_info("Program exited normally");
|
||||||
}
|
}
|
||||||
|
|
||||||
void app_quit(void)
|
void app_exit(void)
|
||||||
{
|
{
|
||||||
sync_flag_set(&G.quit_sf);
|
sync_flag_set(&G.exit_sf);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
#ifndef APP_H
|
#ifndef APP_H
|
||||||
#define APP_H
|
#define APP_H
|
||||||
|
|
||||||
struct string app_write_path_cat(struct arena *arena, struct string filename);
|
#define APP_EXIT_CALLBACK_FUNC_DEF(name) void name(void)
|
||||||
|
typedef APP_EXIT_CALLBACK_FUNC_DEF(app_exit_callback_func);
|
||||||
|
|
||||||
|
struct string app_write_path_cat(struct arena *arena, struct string filename);
|
||||||
|
/* Register a function that will be called when the application exits */
|
||||||
|
void app_register_exit_callback(app_exit_callback_func *func);
|
||||||
void app_entry_point(void);
|
void app_entry_point(void);
|
||||||
void app_quit(void);
|
void app_exit(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
81
src/game.c
81
src/game.c
@ -8,9 +8,10 @@
|
|||||||
#include "math.h"
|
#include "math.h"
|
||||||
#include "scratch.h"
|
#include "scratch.h"
|
||||||
#include "atomic.h"
|
#include "atomic.h"
|
||||||
|
#include "app.h"
|
||||||
|
|
||||||
GLOBAL struct {
|
GLOBAL struct {
|
||||||
b32 shutdown;
|
struct atomic_i32 game_thread_shutdown;
|
||||||
struct sys_thread game_thread;
|
struct sys_thread game_thread;
|
||||||
|
|
||||||
struct world world;
|
struct world world;
|
||||||
@ -25,6 +26,46 @@ GLOBAL struct {
|
|||||||
struct atomic_u64 published_tick_id;
|
struct atomic_u64 published_tick_id;
|
||||||
} G = { 0 }, DEBUG_ALIAS(G, G_game);
|
} G = { 0 }, DEBUG_ALIAS(G, G_game);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Startup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(game_shutdown);
|
||||||
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(game_thread_entry_point, arg);
|
||||||
|
|
||||||
|
struct game_startup_receipt game_startup(struct mixer_startup_receipt *mixer_sr,
|
||||||
|
struct sheet_startup_receipt *sheet_sr,
|
||||||
|
struct sound_startup_receipt *sound_sr)
|
||||||
|
{
|
||||||
|
(UNUSED)mixer_sr;
|
||||||
|
(UNUSED)sheet_sr;
|
||||||
|
(UNUSED)sound_sr;
|
||||||
|
|
||||||
|
/* Initialize game cmd storage */
|
||||||
|
G.game_cmds_mutex = sys_mutex_alloc();
|
||||||
|
G.game_cmds_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
|
||||||
|
/* Initialize world */
|
||||||
|
world_alloc(&G.world);
|
||||||
|
|
||||||
|
/* Initialize tick transmission */
|
||||||
|
world_alloc(&G.published_tick);
|
||||||
|
G.published_tick_mutex = sys_mutex_alloc();
|
||||||
|
|
||||||
|
G.world.timescale = GAME_TIMESCALE;
|
||||||
|
G.game_thread = sys_thread_init(&game_thread_entry_point, NULL, STR("[P2] Game thread"));
|
||||||
|
app_register_exit_callback(&game_shutdown);
|
||||||
|
|
||||||
|
return (struct game_startup_receipt) { 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(game_shutdown)
|
||||||
|
{
|
||||||
|
__prof;
|
||||||
|
atomic_i32_eval_exchange(&G.game_thread_shutdown, true);
|
||||||
|
sys_thread_join(&G.game_thread);
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Game cmd
|
* Game cmd
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -542,15 +583,15 @@ INTERNAL void game_update(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Startup
|
* Game thread
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL SYS_THREAD_FUNC_DEF(game_thread_entry_point, arg)
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(game_thread_entry_point, arg)
|
||||||
{
|
{
|
||||||
(UNUSED)arg;
|
(UNUSED)arg;
|
||||||
sys_timestamp_t last_frame_ts = 0;
|
sys_timestamp_t last_frame_ts = 0;
|
||||||
f64 target_dt = GAME_FPS > (0) ? (1.0 / GAME_FPS) : 0;
|
f64 target_dt = GAME_FPS > (0) ? (1.0 / GAME_FPS) : 0;
|
||||||
while (!G.shutdown) {
|
while (!atomic_i32_eval(&G.game_thread_shutdown)) {
|
||||||
__profscope(game_update_w_sleep);
|
__profscope(game_update_w_sleep);
|
||||||
sleep_frame(last_frame_ts, target_dt);
|
sleep_frame(last_frame_ts, target_dt);
|
||||||
last_frame_ts = sys_timestamp();
|
last_frame_ts = sys_timestamp();
|
||||||
@ -558,38 +599,6 @@ INTERNAL SYS_THREAD_FUNC_DEF(game_thread_entry_point, arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct game_startup_receipt game_startup(struct mixer_startup_receipt *mixer_sr,
|
|
||||||
struct sheet_startup_receipt *sheet_sr,
|
|
||||||
struct sound_startup_receipt *sound_sr)
|
|
||||||
{
|
|
||||||
(UNUSED)mixer_sr;
|
|
||||||
(UNUSED)sheet_sr;
|
|
||||||
(UNUSED)sound_sr;
|
|
||||||
|
|
||||||
/* Initialize game cmd storage */
|
|
||||||
G.game_cmds_mutex = sys_mutex_alloc();
|
|
||||||
G.game_cmds_arena = arena_alloc(GIGABYTE(64));
|
|
||||||
|
|
||||||
/* Initialize world */
|
|
||||||
world_alloc(&G.world);
|
|
||||||
|
|
||||||
/* Initialize tick transmission */
|
|
||||||
world_alloc(&G.published_tick);
|
|
||||||
G.published_tick_mutex = sys_mutex_alloc();
|
|
||||||
|
|
||||||
|
|
||||||
G.world.timescale = GAME_TIMESCALE;
|
|
||||||
G.game_thread = sys_thread_init(&game_thread_entry_point, NULL, STR("[P2] Game thread"));
|
|
||||||
|
|
||||||
return (struct game_startup_receipt) { 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
void game_shutdown(void)
|
|
||||||
{
|
|
||||||
G.shutdown = true;
|
|
||||||
sys_thread_join(&G.game_thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Interface
|
* Interface
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|||||||
@ -34,7 +34,6 @@ struct game_startup_receipt { i32 _; };
|
|||||||
struct game_startup_receipt game_startup(struct mixer_startup_receipt *mixer_sr,
|
struct game_startup_receipt game_startup(struct mixer_startup_receipt *mixer_sr,
|
||||||
struct sheet_startup_receipt *sheet_sr,
|
struct sheet_startup_receipt *sheet_sr,
|
||||||
struct sound_startup_receipt *sound_sr);
|
struct sound_startup_receipt *sound_sr);
|
||||||
void game_shutdown(void);
|
|
||||||
|
|
||||||
void game_get_latest_tick(struct world *dest);
|
void game_get_latest_tick(struct world *dest);
|
||||||
u64 game_get_latest_tick_id(void);
|
u64 game_get_latest_tick_id(void);
|
||||||
|
|||||||
@ -7,6 +7,5 @@ struct mixer_startup_receipt;
|
|||||||
|
|
||||||
struct playback_startup_receipt { i32 _; };
|
struct playback_startup_receipt { i32 _; };
|
||||||
struct playback_startup_receipt playback_startup(struct mixer_startup_receipt *mixer_sr);
|
struct playback_startup_receipt playback_startup(struct mixer_startup_receipt *mixer_sr);
|
||||||
void playback_shutdown(void);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -9,6 +9,8 @@
|
|||||||
#include "scratch.h"
|
#include "scratch.h"
|
||||||
#include "sys.h"
|
#include "sys.h"
|
||||||
#include "mixer.h"
|
#include "mixer.h"
|
||||||
|
#include "atomic.h"
|
||||||
|
#include "app.h"
|
||||||
|
|
||||||
#define COBJMACROS
|
#define COBJMACROS
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
@ -34,7 +36,7 @@ struct wasapi_buffer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
GLOBAL struct {
|
GLOBAL struct {
|
||||||
b32 shutdown;
|
struct atomic_i32 playback_thread_shutdown;
|
||||||
struct sys_thread playback_thread;
|
struct sys_thread playback_thread;
|
||||||
IAudioClient *client;
|
IAudioClient *client;
|
||||||
HANDLE event;
|
HANDLE event;
|
||||||
@ -45,7 +47,33 @@ GLOBAL struct {
|
|||||||
} G = { 0 }, DEBUG_ALIAS(G, G_playback_wasapi);
|
} G = { 0 }, DEBUG_ALIAS(G, G_playback_wasapi);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Initialize
|
* Startup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
INTERNAL void wasapi_initialize(void);
|
||||||
|
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(playback_shutdown);
|
||||||
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(playback_thread_entry_point, arg);
|
||||||
|
|
||||||
|
struct playback_startup_receipt playback_startup(struct mixer_startup_receipt *mixer_sr)
|
||||||
|
{
|
||||||
|
(UNUSED)mixer_sr;
|
||||||
|
|
||||||
|
wasapi_initialize();
|
||||||
|
G.playback_thread = sys_thread_init(&playback_thread_entry_point, NULL, STR("[P3] Audio thread"));
|
||||||
|
app_register_exit_callback(&playback_shutdown);
|
||||||
|
|
||||||
|
return (struct playback_startup_receipt) { 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(playback_shutdown)
|
||||||
|
{
|
||||||
|
__prof;
|
||||||
|
atomic_i32_eval_exchange(&G.playback_thread_shutdown, true);
|
||||||
|
sys_thread_join(&G.playback_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Wasapi initialization
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL void wasapi_initialize(void)
|
INTERNAL void wasapi_initialize(void)
|
||||||
@ -143,7 +171,7 @@ INTERNAL void wasapi_initialize(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Update
|
* Playback thread update
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL struct wasapi_buffer wasapi_update_begin(void)
|
INTERNAL struct wasapi_buffer wasapi_update_begin(void)
|
||||||
@ -202,16 +230,16 @@ INTERNAL void wasapi_update_end(struct wasapi_buffer *wspbuf, struct mixed_pcm_f
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Startup
|
* Playback thread entry
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL SYS_THREAD_FUNC_DEF(playback_thread_entry_point, arg)
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(playback_thread_entry_point, arg)
|
||||||
{
|
{
|
||||||
(UNUSED)arg;
|
(UNUSED)arg;
|
||||||
|
|
||||||
/* FIXME: If playback fails at any point and mixer stops advancing, we
|
/* FIXME: If playback fails at any point and mixer stops advancing, we
|
||||||
* need to halt mixer to prevent memory leak when sounds are played. */
|
* need to halt mixer to prevent memory leak when sounds are played. */
|
||||||
while (!G.shutdown) {
|
while (!atomic_i32_eval(&G.playback_thread_shutdown)) {
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
|
|
||||||
struct wasapi_buffer wspbuf = wasapi_update_begin();
|
struct wasapi_buffer wspbuf = wasapi_update_begin();
|
||||||
@ -221,19 +249,3 @@ INTERNAL SYS_THREAD_FUNC_DEF(playback_thread_entry_point, arg)
|
|||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct playback_startup_receipt playback_startup(struct mixer_startup_receipt *mixer_sr)
|
|
||||||
{
|
|
||||||
(UNUSED)mixer_sr;
|
|
||||||
|
|
||||||
wasapi_initialize();
|
|
||||||
G.playback_thread = sys_thread_init(&playback_thread_entry_point, NULL, STR("[P3] Audio thread"));
|
|
||||||
|
|
||||||
return (struct playback_startup_receipt) { 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
void playback_shutdown(void)
|
|
||||||
{
|
|
||||||
G.shutdown = true;
|
|
||||||
sys_thread_join(&G.playback_thread);
|
|
||||||
}
|
|
||||||
|
|||||||
21
src/sys.h
21
src/sys.h
@ -248,8 +248,8 @@ enum sys_window_flags {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#define SYS_WINDOW_EVENT_CALLBACK_DEF(name, arg_name) void name(struct sys_event arg_name)
|
#define SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(name, arg_name) void name(struct sys_event arg_name)
|
||||||
typedef SYS_WINDOW_EVENT_CALLBACK_DEF(sys_window_event_callback, event);
|
typedef SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(sys_window_event_callback_func, event);
|
||||||
|
|
||||||
/* sys_window_update_settings should be used when altering settings values */
|
/* sys_window_update_settings should be used when altering settings values */
|
||||||
struct sys_window_settings {
|
struct sys_window_settings {
|
||||||
@ -273,8 +273,8 @@ struct sys_window {
|
|||||||
struct sys_window sys_window_alloc(void);
|
struct sys_window sys_window_alloc(void);
|
||||||
void sys_window_release(struct sys_window *sys_window);
|
void sys_window_release(struct sys_window *sys_window);
|
||||||
|
|
||||||
void sys_window_register_event_callback(struct sys_window *sys_window, sys_window_event_callback *func);
|
void sys_window_register_event_callback(struct sys_window *sys_window, sys_window_event_callback_func *func);
|
||||||
void sys_window_unregister_event_callback(struct sys_window *sys_window, sys_window_event_callback *func);
|
void sys_window_unregister_event_callback(struct sys_window *sys_window, sys_window_event_callback_func *func);
|
||||||
|
|
||||||
void sys_window_update_settings(struct sys_window *sys_window, struct sys_window_settings *settings);
|
void sys_window_update_settings(struct sys_window *sys_window, struct sys_window_settings *settings);
|
||||||
struct sys_window_settings sys_window_get_settings(struct sys_window *sys_window);
|
struct sys_window_settings sys_window_get_settings(struct sys_window *sys_window);
|
||||||
@ -378,6 +378,7 @@ struct sys_semaphore {
|
|||||||
struct sys_semaphore sys_semaphore_alloc(u32 max_count);
|
struct sys_semaphore sys_semaphore_alloc(u32 max_count);
|
||||||
void sys_semaphore_release(struct sys_semaphore *semaphore);
|
void sys_semaphore_release(struct sys_semaphore *semaphore);
|
||||||
void sys_semaphore_wait(struct sys_semaphore *semaphore);
|
void sys_semaphore_wait(struct sys_semaphore *semaphore);
|
||||||
|
void sys_semaphore_wait_timed(struct sys_semaphore *semaphore, f64 seconds);
|
||||||
void sys_semaphore_signal(struct sys_semaphore *semaphore, u32 count);
|
void sys_semaphore_signal(struct sys_semaphore *semaphore, u32 count);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -392,17 +393,17 @@ struct thread_local_store *sys_thread_get_thread_local_store(void);
|
|||||||
|
|
||||||
#define SYS_THREAD_STACK_SIZE MEGABYTE(4)
|
#define SYS_THREAD_STACK_SIZE MEGABYTE(4)
|
||||||
|
|
||||||
#define SYS_THREAD_FUNC_DEF(name, arg_name) void name(void *arg_name)
|
#define SYS_THREAD_ENTRY_POINT_FUNC_DEF(name, arg_name) void name(void *arg_name)
|
||||||
typedef SYS_THREAD_FUNC_DEF(sys_thread_func, data);
|
typedef SYS_THREAD_ENTRY_POINT_FUNC_DEF(sys_thread_entry_point_func, data);
|
||||||
|
|
||||||
struct sys_thread {
|
struct sys_thread {
|
||||||
u64 handle;
|
u64 handle[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Creates and starts running the thread in the supplied `thread_func` */
|
/* Creates a new thread running in the supplied `entry_point` */
|
||||||
struct sys_thread sys_thread_init(
|
struct sys_thread sys_thread_init(
|
||||||
sys_thread_func *thread_func,
|
sys_thread_entry_point_func *entry_point,
|
||||||
void *thread_data, /* Passed to thread_func */
|
void *thread_data, /* Passed as arg to `entry_point` */
|
||||||
struct string thread_name
|
struct string thread_name
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
187
src/sys_win32.c
187
src/sys_win32.c
@ -31,12 +31,16 @@
|
|||||||
#define SYS_WINDOW_EVENT_LISTENERS_MAX 512
|
#define SYS_WINDOW_EVENT_LISTENERS_MAX 512
|
||||||
#define WINDOW_CLASS_NAME L"power_play_window_class"
|
#define WINDOW_CLASS_NAME L"power_play_window_class"
|
||||||
|
|
||||||
struct win32_thread_params {
|
struct win32_thread {
|
||||||
sys_thread_func *thread_func;
|
u64 gen;
|
||||||
|
sys_thread_entry_point_func *entry_point;
|
||||||
void *thread_data;
|
void *thread_data;
|
||||||
char thread_name_cstr[256];
|
char thread_name_cstr[256];
|
||||||
|
|
||||||
struct win32_thread_params *next_free;
|
struct win32_thread *next;
|
||||||
|
struct win32_thread *prev;
|
||||||
|
|
||||||
|
HANDLE handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct win32_condition_variable {
|
struct win32_condition_variable {
|
||||||
@ -81,7 +85,7 @@ struct win32_window {
|
|||||||
struct sys_thread event_thread;
|
struct sys_thread event_thread;
|
||||||
|
|
||||||
struct sys_mutex event_callbacks_mutex;
|
struct sys_mutex event_callbacks_mutex;
|
||||||
sys_window_event_callback *event_callbacks[SYS_WINDOW_EVENT_LISTENERS_MAX];
|
sys_window_event_callback_func *event_callbacks[SYS_WINDOW_EVENT_LISTENERS_MAX];
|
||||||
u64 event_callbacks_count;
|
u64 event_callbacks_count;
|
||||||
|
|
||||||
struct win32_window *next_free;
|
struct win32_window *next_free;
|
||||||
@ -107,9 +111,11 @@ GLOBAL struct {
|
|||||||
struct win32_condition_variable *first_free_condition_variable;
|
struct win32_condition_variable *first_free_condition_variable;
|
||||||
|
|
||||||
/* Thread params */
|
/* Thread params */
|
||||||
struct sys_mutex thread_params_mutex;
|
struct sys_rw_mutex threads_rw_mutex;
|
||||||
struct arena thread_params_arena;
|
struct arena threads_arena;
|
||||||
struct win32_thread_params *first_free_thread_params;
|
struct win32_thread *threads_first;
|
||||||
|
struct win32_thread *threads_last;
|
||||||
|
struct win32_thread *threads_first_free;
|
||||||
|
|
||||||
/* Windows */
|
/* Windows */
|
||||||
WNDCLASSEXW window_class;
|
WNDCLASSEXW window_class;
|
||||||
@ -1080,7 +1086,7 @@ void sys_window_release(struct sys_window *sys_window)
|
|||||||
win32_window_release(window);
|
win32_window_release(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sys_window_register_event_callback(struct sys_window *sys_window, sys_window_event_callback *func)
|
void sys_window_register_event_callback(struct sys_window *sys_window, sys_window_event_callback_func *func)
|
||||||
{
|
{
|
||||||
struct win32_window *window = (struct win32_window *)sys_window->handle;
|
struct win32_window *window = (struct win32_window *)sys_window->handle;
|
||||||
|
|
||||||
@ -1095,14 +1101,14 @@ void sys_window_register_event_callback(struct sys_window *sys_window, sys_windo
|
|||||||
sys_mutex_unlock(&window->event_callbacks_mutex);
|
sys_mutex_unlock(&window->event_callbacks_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sys_window_unregister_event_callback(struct sys_window *sys_window, sys_window_event_callback *func)
|
void sys_window_unregister_event_callback(struct sys_window *sys_window, sys_window_event_callback_func *func)
|
||||||
{
|
{
|
||||||
struct win32_window *window = (struct win32_window *)sys_window->handle;
|
struct win32_window *window = (struct win32_window *)sys_window->handle;
|
||||||
|
|
||||||
sys_mutex_lock(&window->event_callbacks_mutex);
|
sys_mutex_lock(&window->event_callbacks_mutex);
|
||||||
{
|
{
|
||||||
u64 count = window->event_callbacks_count;
|
u64 count = window->event_callbacks_count;
|
||||||
sys_window_event_callback *last = count > 0 ? window->event_callbacks[count - 1] : NULL;
|
sys_window_event_callback_func *last = count > 0 ? window->event_callbacks[count - 1] : NULL;
|
||||||
|
|
||||||
for (u64 i = 0; i < window->event_callbacks_count; ++i) {
|
for (u64 i = 0; i < window->event_callbacks_count; ++i) {
|
||||||
if (window->event_callbacks[i] == func) {
|
if (window->event_callbacks[i] == func) {
|
||||||
@ -1442,6 +1448,13 @@ void sys_semaphore_wait(struct sys_semaphore *semaphore)
|
|||||||
WaitForSingleObjectEx((HANDLE)semaphore->handle, INFINITE, FALSE);
|
WaitForSingleObjectEx((HANDLE)semaphore->handle, INFINITE, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sys_semaphore_wait_timed(struct sys_semaphore *semaphore, f64 seconds)
|
||||||
|
{
|
||||||
|
__prof;
|
||||||
|
u32 ms = max_u32(1, math_round((f32)(seconds * 1000.0)));
|
||||||
|
WaitForSingleObjectEx((HANDLE)semaphore->handle, ms, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
void sys_semaphore_signal(struct sys_semaphore *semaphore, u32 count)
|
void sys_semaphore_signal(struct sys_semaphore *semaphore, u32 count)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
@ -1494,37 +1507,67 @@ struct thread_local_store *sys_thread_get_thread_local_store(void)
|
|||||||
* Threads
|
* Threads
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL struct win32_thread_params *thread_params_alloc(void)
|
INTERNAL struct win32_thread *win32_thread_alloc_assume_locked(void)
|
||||||
{
|
{
|
||||||
struct win32_thread_params *tp = NULL;
|
struct win32_thread *t = NULL;
|
||||||
sys_mutex_lock(&G.thread_params_mutex);
|
if (G.threads_first_free) {
|
||||||
{
|
t = G.threads_first_free;
|
||||||
if (G.first_free_thread_params) {
|
G.threads_first_free = t->next;
|
||||||
tp = G.first_free_thread_params;
|
MEMZERO_STRUCT(t);
|
||||||
G.first_free_thread_params = tp->next_free;
|
|
||||||
} else {
|
} else {
|
||||||
tp = arena_push(&G.thread_params_arena, struct win32_thread_params);
|
t = arena_push_zero(&G.threads_arena, struct win32_thread);
|
||||||
|
t->gen = 1;
|
||||||
}
|
}
|
||||||
|
if (G.threads_last) {
|
||||||
|
G.threads_last->next = t;
|
||||||
}
|
}
|
||||||
sys_mutex_unlock(&G.thread_params_mutex);
|
t->prev = G.threads_last;
|
||||||
MEMZERO_STRUCT(tp);
|
return t;
|
||||||
return tp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERNAL void thread_params_release(struct win32_thread_params *tp)
|
INTERNAL void win32_thread_release_assume_locked(struct win32_thread *t)
|
||||||
{
|
{
|
||||||
sys_mutex_lock(&G.thread_params_mutex);
|
if (t->prev) {
|
||||||
{
|
t->prev->next = t->next;
|
||||||
tp->next_free = G.first_free_thread_params;
|
|
||||||
G.first_free_thread_params = tp;
|
|
||||||
}
|
}
|
||||||
sys_mutex_unlock(&G.thread_params_mutex);
|
if (t->next) {
|
||||||
|
t->next->prev = t->prev;
|
||||||
|
}
|
||||||
|
if (G.threads_first == t) {
|
||||||
|
G.threads_first = t->next;
|
||||||
|
}
|
||||||
|
if (G.threads_last == t) {
|
||||||
|
G.threads_last = t->prev;
|
||||||
|
}
|
||||||
|
u64 new_gen = t->gen + 1;
|
||||||
|
*t = (struct win32_thread) {
|
||||||
|
.next = G.threads_first_free,
|
||||||
|
.gen = new_gen
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERNAL DWORD WINAPI win32_thread_proc(LPVOID params)
|
INTERNAL struct win32_thread *win32_thread_from_sys_thread_assume_locked(struct sys_thread st)
|
||||||
{
|
{
|
||||||
struct win32_thread_params thread_params = *(struct win32_thread_params *)params;
|
u64 gen = st.handle[0];
|
||||||
thread_params_release((struct win32_thread_params *)params);
|
struct win32_thread *t = (struct win32_thread *)st.handle[1];
|
||||||
|
if (t->gen == gen) {
|
||||||
|
return t;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL struct sys_thread sys_thread_from_win32_thread_assume_locked(struct win32_thread *t)
|
||||||
|
{
|
||||||
|
return (struct sys_thread) {
|
||||||
|
.handle[0] = t->gen,
|
||||||
|
.handle[1] = (u64)t
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL DWORD WINAPI win32_thread_proc(LPVOID vt)
|
||||||
|
{
|
||||||
|
struct win32_thread *t = (struct win32_thread *)vt;
|
||||||
|
|
||||||
/* Initialize COM */
|
/* Initialize COM */
|
||||||
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
||||||
@ -1534,7 +1577,7 @@ INTERNAL DWORD WINAPI win32_thread_proc(LPVOID params)
|
|||||||
win32_thread_set_tls(&tls);
|
win32_thread_set_tls(&tls);
|
||||||
|
|
||||||
/* Set thread name */
|
/* Set thread name */
|
||||||
struct string thread_name = string_from_cstr(thread_params.thread_name_cstr);
|
struct string thread_name = string_from_cstr(t->thread_name_cstr);
|
||||||
if (thread_name.len > 0) {
|
if (thread_name.len > 0) {
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
wchar_t *wc_thread_name = wstr_from_string(scratch.arena, thread_name);
|
wchar_t *wc_thread_name = wstr_from_string(scratch.arena, thread_name);
|
||||||
@ -1545,7 +1588,14 @@ INTERNAL DWORD WINAPI win32_thread_proc(LPVOID params)
|
|||||||
logf_info("New thread \"%F\" created with ID %F", FMT_STR(thread_name), FMT_UINT(sys_thread_id()));
|
logf_info("New thread \"%F\" created with ID %F", FMT_STR(thread_name), FMT_UINT(sys_thread_id()));
|
||||||
|
|
||||||
/* Start thread */
|
/* Start thread */
|
||||||
thread_params.thread_func(thread_params.thread_data);
|
t->entry_point(t->thread_data);
|
||||||
|
|
||||||
|
/* Release thread object */
|
||||||
|
sys_rw_mutex_lock_exclusive(&G.threads_rw_mutex);
|
||||||
|
{
|
||||||
|
win32_thread_release_assume_locked(t);
|
||||||
|
}
|
||||||
|
sys_rw_mutex_unlock_exclusive(&G.threads_rw_mutex);
|
||||||
|
|
||||||
/* Release TLS */
|
/* Release TLS */
|
||||||
win32_tls_release(&tls);
|
win32_tls_release(&tls);
|
||||||
@ -1556,50 +1606,64 @@ INTERNAL DWORD WINAPI win32_thread_proc(LPVOID params)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sys_thread sys_thread_init(sys_thread_func *thread_func, void *thread_data, struct string thread_name)
|
struct sys_thread sys_thread_init(sys_thread_entry_point_func *entry_point, void *thread_data, struct string thread_name)
|
||||||
{
|
{
|
||||||
struct sys_thread thread = { 0 };
|
ASSERT(entry_point != NULL);
|
||||||
|
|
||||||
ASSERT(thread_func != NULL);
|
|
||||||
|
|
||||||
logf_info("Creating thread \"%F\"", FMT_STR(thread_name));
|
logf_info("Creating thread \"%F\"", FMT_STR(thread_name));
|
||||||
|
|
||||||
/* Create thread params */
|
struct sys_thread res = { 0 };
|
||||||
struct win32_thread_params *tp = thread_params_alloc();
|
sys_rw_mutex_lock_exclusive(&G.threads_rw_mutex);
|
||||||
tp->thread_func = thread_func;
|
{
|
||||||
tp->thread_data = thread_data;
|
/* Allocate thread object */
|
||||||
|
struct win32_thread *t = win32_thread_alloc_assume_locked();
|
||||||
|
t->entry_point = entry_point;
|
||||||
|
t->thread_data = thread_data;
|
||||||
|
|
||||||
/* Copy thread name to params */
|
/* Copy thread name to params */
|
||||||
cstr_buff_from_string(BUFFER_FROM_ARRAY(tp->thread_name_cstr), thread_name);
|
cstr_buff_from_string(BUFFER_FROM_ARRAY(t->thread_name_cstr), thread_name);
|
||||||
|
|
||||||
HANDLE handle = CreateThread(
|
t->handle = CreateThread(
|
||||||
NULL,
|
NULL,
|
||||||
SYS_THREAD_STACK_SIZE,
|
SYS_THREAD_STACK_SIZE,
|
||||||
win32_thread_proc,
|
win32_thread_proc,
|
||||||
tp,
|
t,
|
||||||
0,
|
0,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Initialize struct */
|
if (!t->handle) {
|
||||||
if (handle) {
|
|
||||||
/* Success */
|
|
||||||
thread.handle = (u64)handle;
|
|
||||||
} else {
|
|
||||||
sys_panic(STR("Failed to create thread"));
|
sys_panic(STR("Failed to create thread"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return thread;
|
res = sys_thread_from_win32_thread_assume_locked(t);
|
||||||
|
}
|
||||||
|
sys_rw_mutex_unlock_exclusive(&G.threads_rw_mutex);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sys_thread_join(struct sys_thread *thread)
|
void sys_thread_join(struct sys_thread *thread)
|
||||||
{
|
{
|
||||||
DWORD res = WaitForSingleObject((HANDLE)thread->handle, INFINITE);
|
HANDLE handle = 0;
|
||||||
CloseHandle((HANDLE)thread->handle);
|
|
||||||
|
/* Lookup */
|
||||||
|
sys_rw_mutex_lock_shared(&G.threads_rw_mutex);
|
||||||
|
{
|
||||||
|
struct win32_thread *t = win32_thread_from_sys_thread_assume_locked(*thread);
|
||||||
|
if (t) {
|
||||||
|
handle = t->handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sys_rw_mutex_unlock_shared(&G.threads_rw_mutex);
|
||||||
|
|
||||||
|
/* Wait */
|
||||||
|
if (handle) {
|
||||||
|
DWORD res = WaitForSingleObject(handle, INFINITE);
|
||||||
|
CloseHandle(handle);
|
||||||
|
|
||||||
(UNUSED)res;
|
(UNUSED)res;
|
||||||
ASSERT(res != WAIT_FAILED);
|
ASSERT(res != WAIT_FAILED);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
u32 sys_thread_id(void)
|
u32 sys_thread_id(void)
|
||||||
{
|
{
|
||||||
@ -1893,7 +1957,7 @@ void sys_sleep(f64 seconds)
|
|||||||
* Entry point
|
* Entry point
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL SYS_THREAD_FUNC_DEF(app_thread_entry_point, arg)
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(app_thread_entry_point, arg)
|
||||||
{
|
{
|
||||||
(UNUSED)arg;
|
(UNUSED)arg;
|
||||||
app_entry_point();
|
app_entry_point();
|
||||||
@ -1932,8 +1996,8 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
|
|||||||
G.condition_variables_arena = arena_alloc(GIGABYTE(64));
|
G.condition_variables_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
|
||||||
/* Set up threads */
|
/* Set up threads */
|
||||||
G.thread_params_mutex = sys_mutex_alloc();
|
G.threads_rw_mutex = sys_rw_mutex_alloc();
|
||||||
G.thread_params_arena = arena_alloc(GIGABYTE(64));
|
G.threads_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
|
||||||
/* Set up windows */
|
/* Set up windows */
|
||||||
G.windows_mutex = sys_mutex_alloc();
|
G.windows_mutex = sys_mutex_alloc();
|
||||||
@ -1990,17 +2054,24 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Main thread setup
|
* App thread setup
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
/* Initialize main thread context. */
|
/* Initialize main thread context. */
|
||||||
struct win32_tls main_thread_tls = win32_tls_alloc();
|
struct win32_tls main_thread_tls = win32_tls_alloc();
|
||||||
win32_thread_set_tls(&main_thread_tls);
|
win32_thread_set_tls(&main_thread_tls);
|
||||||
|
|
||||||
/* Create app thread & wait for return */
|
/* Call app thread and wait for return */
|
||||||
struct sys_thread app_thread = sys_thread_init(&app_thread_entry_point, NULL, STR("[P9] App thread"));
|
struct sys_thread app_thread = sys_thread_init(&app_thread_entry_point, NULL, STR("[P9] App thread"));
|
||||||
sys_thread_join(&app_thread);
|
sys_thread_join(&app_thread);
|
||||||
|
|
||||||
|
/* Release main thread context */
|
||||||
|
win32_tls_release(&main_thread_tls);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Abort
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
abort:
|
abort:
|
||||||
|
|
||||||
if (error_msg) {
|
if (error_msg) {
|
||||||
@ -2009,8 +2080,6 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
win32_tls_release(&main_thread_tls);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
121
src/user.c
121
src/user.c
@ -16,6 +16,8 @@
|
|||||||
#include "world.h"
|
#include "world.h"
|
||||||
#include "entity.h"
|
#include "entity.h"
|
||||||
#include "mixer.h"
|
#include "mixer.h"
|
||||||
|
#include "atomic.h"
|
||||||
|
#include "app.h"
|
||||||
|
|
||||||
struct bind_state {
|
struct bind_state {
|
||||||
b32 is_held; /* Is this bind held down this frame */
|
b32 is_held; /* Is this bind held down this frame */
|
||||||
@ -30,7 +32,7 @@ struct blend_tick {
|
|||||||
};
|
};
|
||||||
|
|
||||||
GLOBAL struct {
|
GLOBAL struct {
|
||||||
b32 shutdown;
|
struct atomic_i32 user_thread_shutdown;
|
||||||
struct sys_thread user_thread;
|
struct sys_thread user_thread;
|
||||||
|
|
||||||
struct arena arena;
|
struct arena arena;
|
||||||
@ -93,6 +95,57 @@ GLOBAL READONLY enum user_bind_kind g_binds[SYS_BTN_COUNT] = {
|
|||||||
[SYS_BTN_CTRL] = USER_BIND_KIND_CTRL_TEST
|
[SYS_BTN_CTRL] = USER_BIND_KIND_CTRL_TEST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Startup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(user_shutdown);
|
||||||
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg);
|
||||||
|
INTERNAL SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(window_event_callback, event);
|
||||||
|
|
||||||
|
struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
||||||
|
struct renderer_startup_receipt *renderer_sr,
|
||||||
|
struct font_startup_receipt *font_sr,
|
||||||
|
struct texture_startup_receipt *texture_sr,
|
||||||
|
struct draw_startup_receipt *draw_sr,
|
||||||
|
struct game_startup_receipt *game_sr,
|
||||||
|
struct asset_cache_startup_receipt *asset_cache_sr,
|
||||||
|
struct mixer_startup_receipt *mixer_sr,
|
||||||
|
struct sys_window *window)
|
||||||
|
{
|
||||||
|
(UNUSED)work_sr;
|
||||||
|
(UNUSED)renderer_sr;
|
||||||
|
(UNUSED)font_sr;
|
||||||
|
(UNUSED)texture_sr;
|
||||||
|
(UNUSED)draw_sr;
|
||||||
|
(UNUSED)game_sr;
|
||||||
|
(UNUSED)asset_cache_sr;
|
||||||
|
(UNUSED)mixer_sr;
|
||||||
|
|
||||||
|
G.arena = arena_alloc(GIGABYTE(64));
|
||||||
|
G.sys_events_mutex = sys_mutex_alloc();
|
||||||
|
G.sys_events_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
world_alloc(&G.world);
|
||||||
|
G.world_canvas = renderer_canvas_alloc();
|
||||||
|
G.world_view = XFORM_TRS(.t = V2(0, 0), .r = 0, .s = V2(PIXELS_PER_UNIT, PIXELS_PER_UNIT));
|
||||||
|
G.viewport_bg_canvas = renderer_canvas_alloc();
|
||||||
|
G.viewport_canvas = renderer_canvas_alloc();
|
||||||
|
G.window = window;
|
||||||
|
sys_window_register_event_callback(G.window, &window_event_callback);
|
||||||
|
|
||||||
|
G.user_thread = sys_thread_init(&user_thread_entry_point, NULL, STR("[P1] User thread"));
|
||||||
|
app_register_exit_callback(&user_shutdown);
|
||||||
|
|
||||||
|
return (struct user_startup_receipt) { 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(user_shutdown)
|
||||||
|
{
|
||||||
|
__prof;
|
||||||
|
atomic_i32_eval_exchange(&G.user_thread_shutdown, true);
|
||||||
|
sys_thread_join(&G.user_thread);
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Window -> user communication
|
* Window -> user communication
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -113,7 +166,7 @@ INTERNAL struct sys_event_array pop_sys_events(struct arena *arena)
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERNAL SYS_WINDOW_EVENT_CALLBACK_DEF(window_event_callback, event)
|
INTERNAL SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(window_event_callback, event)
|
||||||
{
|
{
|
||||||
sys_mutex_lock(&G.sys_events_mutex);
|
sys_mutex_lock(&G.sys_events_mutex);
|
||||||
{
|
{
|
||||||
@ -452,14 +505,14 @@ INTERNAL void user_update(void)
|
|||||||
struct sys_event *event = &events.events[entity_index];
|
struct sys_event *event = &events.events[entity_index];
|
||||||
|
|
||||||
if (event->kind == SYS_EVENT_KIND_QUIT) {
|
if (event->kind == SYS_EVENT_KIND_QUIT) {
|
||||||
app_quit();
|
app_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->kind == SYS_EVENT_KIND_BUTTON_UP) {
|
if (event->kind == SYS_EVENT_KIND_BUTTON_UP) {
|
||||||
#if DEVELOPER
|
#if DEVELOPER
|
||||||
/* Escape quit */
|
/* Escape quit */
|
||||||
if (event->button == SYS_BTN_ESC) {
|
if (event->button == SYS_BTN_ESC) {
|
||||||
app_quit();
|
app_exit();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -1020,17 +1073,17 @@ INTERNAL void user_update(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Startup
|
* User thread entry point
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL SYS_THREAD_FUNC_DEF(user_thread_entry_point, arg)
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg)
|
||||||
{
|
{
|
||||||
(UNUSED)arg;
|
(UNUSED)arg;
|
||||||
|
|
||||||
sys_timestamp_t last_frame_ts = 0;
|
sys_timestamp_t last_frame_ts = 0;
|
||||||
f64 target_dt = USER_FRAME_LIMIT > (0) ? (1.0 / USER_FRAME_LIMIT) : 0;
|
f64 target_dt = USER_FRAME_LIMIT > (0) ? (1.0 / USER_FRAME_LIMIT) : 0;
|
||||||
|
|
||||||
while (!G.shutdown) {
|
while (!atomic_i32_eval(&G.user_thread_shutdown)) {
|
||||||
__profscope(user_update_w_sleep);
|
__profscope(user_update_w_sleep);
|
||||||
sleep_frame(last_frame_ts, target_dt);
|
sleep_frame(last_frame_ts, target_dt);
|
||||||
last_frame_ts = sys_timestamp();
|
last_frame_ts = sys_timestamp();
|
||||||
@ -1038,52 +1091,6 @@ INTERNAL SYS_THREAD_FUNC_DEF(user_thread_entry_point, arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|
||||||
struct renderer_startup_receipt *renderer_sr,
|
|
||||||
struct font_startup_receipt *font_sr,
|
|
||||||
struct texture_startup_receipt *texture_sr,
|
|
||||||
struct draw_startup_receipt *draw_sr,
|
|
||||||
struct game_startup_receipt *game_sr,
|
|
||||||
struct asset_cache_startup_receipt *asset_cache_sr,
|
|
||||||
struct mixer_startup_receipt *mixer_sr,
|
|
||||||
struct sys_window *window)
|
|
||||||
{
|
|
||||||
(UNUSED)work_sr;
|
|
||||||
(UNUSED)renderer_sr;
|
|
||||||
(UNUSED)font_sr;
|
|
||||||
(UNUSED)texture_sr;
|
|
||||||
(UNUSED)draw_sr;
|
|
||||||
(UNUSED)game_sr;
|
|
||||||
(UNUSED)asset_cache_sr;
|
|
||||||
(UNUSED)mixer_sr;
|
|
||||||
|
|
||||||
G.arena = arena_alloc(GIGABYTE(64));
|
|
||||||
|
|
||||||
G.sys_events_mutex = sys_mutex_alloc();
|
|
||||||
G.sys_events_arena = arena_alloc(GIGABYTE(64));
|
|
||||||
|
|
||||||
world_alloc(&G.world);
|
|
||||||
|
|
||||||
G.world_canvas = renderer_canvas_alloc();
|
|
||||||
G.world_view = XFORM_TRS(.t = V2(0, 0), .r = 0, .s = V2(PIXELS_PER_UNIT, PIXELS_PER_UNIT));
|
|
||||||
|
|
||||||
G.viewport_bg_canvas = renderer_canvas_alloc();
|
|
||||||
G.viewport_canvas = renderer_canvas_alloc();
|
|
||||||
|
|
||||||
G.window = window;
|
|
||||||
sys_window_register_event_callback(G.window, &window_event_callback);
|
|
||||||
|
|
||||||
G.user_thread = sys_thread_init(&user_thread_entry_point, NULL, STR("[P1] User thread"));
|
|
||||||
|
|
||||||
return (struct user_startup_receipt) { 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
void user_shutdown(void)
|
|
||||||
{
|
|
||||||
G.shutdown = true;
|
|
||||||
sys_thread_join(&G.user_thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#include "user.h"
|
#include "user.h"
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
@ -1198,7 +1205,7 @@ INTERNAL struct sys_event_array pop_sys_events(struct arena *arena)
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERNAL SYS_WINDOW_EVENT_CALLBACK_DEF(window_event_callback, event)
|
INTERNAL SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(window_event_callback, event)
|
||||||
{
|
{
|
||||||
sys_mutex_lock(&G.sys_events_mutex);
|
sys_mutex_lock(&G.sys_events_mutex);
|
||||||
{
|
{
|
||||||
@ -1543,14 +1550,14 @@ INTERNAL void user_update(void)
|
|||||||
struct sys_event *event = &events.events[entity_index];
|
struct sys_event *event = &events.events[entity_index];
|
||||||
|
|
||||||
if (event->kind == SYS_EVENT_KIND_QUIT) {
|
if (event->kind == SYS_EVENT_KIND_QUIT) {
|
||||||
app_quit();
|
app_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->kind == SYS_EVENT_KIND_BUTTON_UP) {
|
if (event->kind == SYS_EVENT_KIND_BUTTON_UP) {
|
||||||
#if DEVELOPER
|
#if DEVELOPER
|
||||||
/* Escape quit */
|
/* Escape quit */
|
||||||
if (event->button == SYS_BTN_ESC) {
|
if (event->button == SYS_BTN_ESC) {
|
||||||
app_quit();
|
app_exit();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -2118,7 +2125,7 @@ INTERNAL void user_update(void)
|
|||||||
* Startup
|
* Startup
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL SYS_THREAD_FUNC_DEF(user_thread_entry_point, arg)
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg)
|
||||||
{
|
{
|
||||||
(UNUSED)arg;
|
(UNUSED)arg;
|
||||||
|
|
||||||
|
|||||||
@ -43,6 +43,5 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|||||||
struct asset_cache_startup_receipt *asset_cache_sr,
|
struct asset_cache_startup_receipt *asset_cache_sr,
|
||||||
struct mixer_startup_receipt *mixer_sr,
|
struct mixer_startup_receipt *mixer_sr,
|
||||||
struct sys_window *window);
|
struct sys_window *window);
|
||||||
void user_shutdown(void);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
25
src/work.c
25
src/work.c
@ -7,6 +7,8 @@
|
|||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "thread_local.h"
|
#include "thread_local.h"
|
||||||
|
#include "atomic.h"
|
||||||
|
#include "app.h"
|
||||||
|
|
||||||
/* Terminology:
|
/* Terminology:
|
||||||
*
|
*
|
||||||
@ -65,13 +67,12 @@ struct work_task {
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
GLOBAL struct {
|
GLOBAL struct {
|
||||||
b32 shutdown;
|
|
||||||
|
|
||||||
struct arena arena;
|
struct arena arena;
|
||||||
|
|
||||||
struct sys_mutex mutex;
|
struct sys_mutex mutex;
|
||||||
struct sys_semaphore semaphore;
|
struct sys_semaphore semaphore;
|
||||||
|
|
||||||
|
struct atomic_i32 workers_shutdown;
|
||||||
u32 worker_count;
|
u32 worker_count;
|
||||||
u32 idle_worker_count;
|
u32 idle_worker_count;
|
||||||
struct worker *worker_head;
|
struct worker *worker_head;
|
||||||
@ -102,7 +103,8 @@ GLOBAL THREAD_LOCAL_VAR_DEF(tl_worker_ctx, struct worker_ctx, NULL, NULL);
|
|||||||
* Startup
|
* Startup
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL void worker_thread_entry_point(void *thread_data);
|
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(work_shutdown);
|
||||||
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(worker_thread_entry_point, thread_data);
|
||||||
|
|
||||||
struct work_startup_receipt work_startup(u32 num_worker_threads)
|
struct work_startup_receipt work_startup(u32 num_worker_threads)
|
||||||
{
|
{
|
||||||
@ -117,6 +119,7 @@ struct work_startup_receipt work_startup(u32 num_worker_threads)
|
|||||||
G.semaphore = sys_semaphore_alloc(num_worker_threads);
|
G.semaphore = sys_semaphore_alloc(num_worker_threads);
|
||||||
G.worker_count = num_worker_threads;
|
G.worker_count = num_worker_threads;
|
||||||
G.idle_worker_count = num_worker_threads;
|
G.idle_worker_count = num_worker_threads;
|
||||||
|
app_register_exit_callback(&work_shutdown);
|
||||||
|
|
||||||
/* Initialize threads */
|
/* Initialize threads */
|
||||||
{
|
{
|
||||||
@ -146,10 +149,10 @@ struct work_startup_receipt work_startup(u32 num_worker_threads)
|
|||||||
return (struct work_startup_receipt) { 0 };
|
return (struct work_startup_receipt) { 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
void work_shutdown(void)
|
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(work_shutdown)
|
||||||
{
|
{
|
||||||
G.shutdown = true;
|
__prof;
|
||||||
WRITE_BARRIER();
|
atomic_i32_eval_exchange(&G.workers_shutdown, true);
|
||||||
sys_semaphore_signal(&G.semaphore, G.worker_count);
|
sys_semaphore_signal(&G.semaphore, G.worker_count);
|
||||||
for (struct worker *worker = G.worker_head; (worker = worker->next);) {
|
for (struct worker *worker = G.worker_head; (worker = worker->next);) {
|
||||||
sys_thread_join(&worker->thread);
|
sys_thread_join(&worker->thread);
|
||||||
@ -375,7 +378,7 @@ INTERNAL void work_exec_remaining_tasks_maybe_release_assume_locked(struct work
|
|||||||
* Work thread proc
|
* Work thread proc
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL void worker_thread_entry_point(void *thread_data)
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(worker_thread_entry_point, thread_data)
|
||||||
{
|
{
|
||||||
(UNUSED)thread_data;
|
(UNUSED)thread_data;
|
||||||
|
|
||||||
@ -384,12 +387,8 @@ INTERNAL void worker_thread_entry_point(void *thread_data)
|
|||||||
.is_worker = true
|
.is_worker = true
|
||||||
};
|
};
|
||||||
|
|
||||||
while (true) {
|
while (!atomic_i32_eval(&G.workers_shutdown)) {
|
||||||
sys_semaphore_wait(&G.semaphore);
|
sys_semaphore_wait_timed(&G.semaphore, 0.500); /* Timed to poll G.shutdown */
|
||||||
if (G.shutdown) {
|
|
||||||
/* Exit thread */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
while (G.scheduled_work_head) {
|
while (G.scheduled_work_head) {
|
||||||
/* Do work from top */
|
/* Do work from top */
|
||||||
sys_mutex_lock(&G.mutex);
|
sys_mutex_lock(&G.mutex);
|
||||||
|
|||||||
@ -33,7 +33,6 @@ struct work_slate {
|
|||||||
|
|
||||||
struct work_startup_receipt { i32 _; };
|
struct work_startup_receipt { i32 _; };
|
||||||
struct work_startup_receipt work_startup(u32 num_worker_threads);
|
struct work_startup_receipt work_startup(u32 num_worker_threads);
|
||||||
void work_shutdown(void);
|
|
||||||
|
|
||||||
struct work_slate work_slate_begin(void);
|
struct work_slate work_slate_begin(void);
|
||||||
struct work_handle work_slate_end(struct work_slate *ws, enum work_priority priority);
|
struct work_handle work_slate_end(struct work_slate *ws, enum work_priority priority);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user