diff --git a/meson.build b/meson.build index a8d39d62..5b4ec879 100644 --- a/meson.build +++ b/meson.build @@ -295,7 +295,7 @@ src += [ 'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c', 'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c', 'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c', - 'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c' + 'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'timer.c' ] # quirc src src += [ diff --git a/source/cell.c b/source/cell.c index e836a11d..563dec85 100644 --- a/source/cell.c +++ b/source/cell.c @@ -28,6 +28,7 @@ #include "stb_ds.h" #include "jsffi.h" #include "cell.h" +#include "timer.h" #ifdef TRACY_ENABLE #include @@ -55,7 +56,7 @@ static cell_rt **main_ready_queue = NULL; static SDL_Mutex *queue_mutex = NULL; // for the ready queue static SDL_Condition *queue_cond = NULL; static SDL_Mutex *mainqueue_mutex = NULL; -static SDL_Condition *mainqueue_cond = NULL; +SDL_Condition *mainqueue_cond = NULL; static SDL_Mutex *actors_mutex = NULL; static struct { char *key; cell_rt *value; } *actors = NULL; static unsigned char *zip_buffer_global = NULL; @@ -64,113 +65,6 @@ cell_rt *root_cell = NULL; static SDL_Thread **runners = NULL; -// ───────────────────────────────────────────────────────────────────────────── -// TIMER “SDL_AddTimerNS”–replacement -// ───────────────────────────────────────────────────────────────────────────── - -typedef Uint32 (*TimerCallback)(Uint32 timer_id, Uint32 interval, void *param); - -typedef struct { - Uint32 id; - uint64_t due_ns; - uint64_t interval_ns; - TimerCallback callback; - void *param; -} timer_t; - -// stb_ds array of timer_t -static timer_t *timers = NULL; -static Uint32 next_timer_id = 1; - -// Must be initialized exactly once (e.g. in main after SDL_Init) -static SDL_Mutex *timer_mutex = NULL; - -// Return monotonic time in nanoseconds -static uint64_t get_time_ns(void) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t)ts.tv_sec * 1000000000ull + ts.tv_nsec; -} - -// Schedule a new timer to fire after `delay_ns` nanoseconds. -// Returns a Uint32 timer ID. When it fires, `callback(interval_ms,param)` is invoked. -// If `callback` returns nonzero (in ms), the timer is rescheduled with that -// new interval; otherwise it is removed permanently. -Uint32 add_timer_ns(uint64_t delay_ns, TimerCallback callback, void *param) { - timer_t t; - t.id = next_timer_id++; - t.interval_ns = delay_ns; - t.due_ns = get_time_ns() + delay_ns; - t.callback = callback; - t.param = param; - SDL_LockMutex(timer_mutex); - arrput(timers, t); - SDL_UnlockMutex(timer_mutex); - SDL_SignalCondition(mainqueue_cond); - return t.id; -} - -// Cancel (remove) a pending timer by its ID. If not found, does nothing. -void remove_timer(Uint32 id) { - SDL_LockMutex(timer_mutex); - for (int i = 0; i < arrlen(timers); i++) { - if (timers[i].id == id) { - arrdel(timers, i); - break; - } - } - SDL_UnlockMutex(timer_mutex); -} - -// Scan the global `timers[]` array and invoke any timer whose due_ns <= now. -// For each such timer: -// 1) pop it out of the `timers` array -// 2) call `callback(interval_ms,param)` -// 3) if callback returns nonzero "next_ms", re-insert it with new due_ns = now + next_ms*1e6 -// This must be called once per iteration of your main loop (before polling). -void process_due_timers(void) { - uint64_t now = get_time_ns(); - SDL_LockMutex(timer_mutex); - for (int i = 0; i < arrlen(timers); i++) { - if (timers[i].due_ns <= now) { - timer_t t = timers[i]; - arrdel(timers, i); - SDL_UnlockMutex(timer_mutex); - - // Convert interval_ns back to milliseconds for the callback - Uint32 next_ms = t.callback(t.id, t.interval_ns, t.param); - if (next_ms > 0) { - uint64_t next_ns = (uint64_t)next_ms * 1000000ull; - add_timer_ns(next_ns, t.callback, t.param); - } - - // restart scan because array may have shifted - SDL_LockMutex(timer_mutex); - i = -1; - continue; - } - } - SDL_UnlockMutex(timer_mutex); -} - -uint64_t next_timeout_ns(void) { - SDL_LockMutex(timer_mutex); - if (!timers || arrlen(timers) == 0) { - SDL_UnlockMutex(timer_mutex); - return UINT64_MAX; - } - uint64_t now = get_time_ns(); - uint64_t min_due = timers[0].due_ns; - for (int i = 1; i < arrlen(timers); i++) { - if (timers[i].due_ns < min_due) - min_due = timers[i].due_ns; - } - SDL_UnlockMutex(timer_mutex); - if (min_due <= now) - return 0; - return min_due - now; -} - static inline uint64_t now_ns() { return SDL_GetTicksNS(); @@ -863,7 +757,6 @@ queue_cond = SDL_CreateCondition(); mainqueue_mutex = SDL_CreateMutex(); mainqueue_cond = SDL_CreateCondition(); actors_mutex = SDL_CreateMutex(); -timer_mutex = SDL_CreateMutex(); add_runners(SDL_GetNumLogicalCPUCores()-1); diff --git a/source/cell.h b/source/cell.h index 3888cee2..3028bf78 100644 --- a/source/cell.h +++ b/source/cell.h @@ -78,6 +78,7 @@ typedef struct cell_rt { int disrupt; int main_thread_only; + int affinity; JSAtom actor_sym; diff --git a/source/timer.c b/source/timer.c new file mode 100644 index 00000000..05907b6d --- /dev/null +++ b/source/timer.c @@ -0,0 +1,98 @@ +#include "timer.h" +#include +#include +#include "stb_ds.h" + +/* External variables from cell.c */ +extern SDL_Condition *mainqueue_cond; + +/* Global timer state */ +static timer_t *timers = NULL; +static Uint32 next_timer_id = 1; +static SDL_Mutex *timer_mutex = NULL; + +void timer_init(void) +{ + if (!timer_mutex) { + timer_mutex = SDL_CreateMutex(); + } +} + +uint64_t get_time_ns(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000000000ull + ts.tv_nsec; +} + +Uint32 add_timer_ns(uint64_t delay_ns, TimerCallback callback, void *param) +{ + timer_t t; + t.id = next_timer_id++; + t.interval_ns = delay_ns; + t.due_ns = get_time_ns() + delay_ns; + t.callback = callback; + t.param = param; + SDL_LockMutex(timer_mutex); + arrput(timers, t); + SDL_UnlockMutex(timer_mutex); + SDL_SignalCondition(mainqueue_cond); + return t.id; +} + +void remove_timer(Uint32 id) +{ + SDL_LockMutex(timer_mutex); + for (int i = 0; i < arrlen(timers); i++) { + if (timers[i].id == id) { + arrdel(timers, i); + break; + } + } + SDL_UnlockMutex(timer_mutex); +} + +void process_due_timers(void) +{ + uint64_t now = get_time_ns(); + SDL_LockMutex(timer_mutex); + for (int i = 0; i < arrlen(timers); i++) { + if (timers[i].due_ns <= now) { + timer_t t = timers[i]; + arrdel(timers, i); + SDL_UnlockMutex(timer_mutex); + + /* Convert interval_ns back to milliseconds for the callback */ + Uint32 next_ms = t.callback(t.id, t.interval_ns, t.param); + if (next_ms > 0) { + uint64_t next_ns = (uint64_t)next_ms * 1000000ull; + add_timer_ns(next_ns, t.callback, t.param); + } + + /* restart scan because array may have shifted */ + SDL_LockMutex(timer_mutex); + i = -1; + continue; + } + } + SDL_UnlockMutex(timer_mutex); +} + +uint64_t next_timeout_ns(void) +{ + SDL_LockMutex(timer_mutex); + if (!timers || arrlen(timers) == 0) { + SDL_UnlockMutex(timer_mutex); + return UINT64_MAX; + } + uint64_t now = get_time_ns(); + uint64_t min_due = timers[0].due_ns; + for (int i = 1; i < arrlen(timers); i++) { + if (timers[i].due_ns < min_due) + min_due = timers[i].due_ns; + } + SDL_UnlockMutex(timer_mutex); + if (min_due <= now) + return 0; + return min_due - now; +} \ No newline at end of file diff --git a/source/timer.h b/source/timer.h new file mode 100644 index 00000000..faee06f8 --- /dev/null +++ b/source/timer.h @@ -0,0 +1,35 @@ +#ifndef TIMER_H +#define TIMER_H + +#include +#include + +typedef Uint32 (*TimerCallback)(Uint32 timer_id, Uint32 interval, void *param); + +typedef struct { + Uint32 id; + uint64_t due_ns; + uint64_t interval_ns; + TimerCallback callback; + void *param; +} timer_t; + +/* Initialize timer system - must be called once */ +void timer_init(void); + +/* Schedule a new timer to fire after delay_ns nanoseconds */ +Uint32 add_timer_ns(uint64_t delay_ns, TimerCallback callback, void *param); + +/* Cancel a pending timer by its ID */ +void remove_timer(Uint32 id); + +/* Process due timers - call once per main loop iteration */ +void process_due_timers(void); + +/* Get time until next timer expires (in nanoseconds) */ +uint64_t next_timeout_ns(void); + +/* Get current monotonic time in nanoseconds */ +uint64_t get_time_ns(void); + +#endif /* TIMER_H */ \ No newline at end of file