massively simplify loop logic

This commit is contained in:
2025-06-05 00:45:40 -05:00
parent fcec2cd1dc
commit 79d5412fe6
14 changed files with 207 additions and 249 deletions

View File

@@ -10,4 +10,4 @@ actor_max = "10_000"
stack_max = 0 stack_max = 0
[actors] [actors]
[actors.prosperon/sdl_video] [actors.prosperon/sdl_video]
main = true main = true

View File

@@ -12,6 +12,7 @@ $_.start(e => {
video = e.actor video = e.actor
graphics = use('graphics', video) graphics = use('graphics', video)
send(video, {kind:"window", op:"makeRenderer"}, e => { send(video, {kind:"window", op:"makeRenderer"}, e => {
log.console("MADE A WINDOW! so now renderer")
$_.start(e => { $_.start(e => {
if (gameactor) return if (gameactor) return
gameactor = e.actor gameactor = e.actor
@@ -24,9 +25,6 @@ $_.start(e => {
height:500 height:500
}) })
var input = use('input')
input.watch($_)
var geometry = use('geometry') var geometry = use('geometry')

View File

@@ -200,6 +200,7 @@ function handle_window(msg) {
return {success: true}; return {success: true};
case 'makeRenderer': case 'makeRenderer':
log.console("MAKE RENDERER")
if (ren) if (ren)
return {reason: "Already made a renderer"} return {reason: "Already made a renderer"}
ren = win.make_renderer() ren = win.make_renderer()

View File

@@ -86,8 +86,6 @@ var nota = hidden.nota
// Strip hidden from cell so nothing else can access it // Strip hidden from cell so nothing else can access it
delete cell.hidden delete cell.hidden
var os = use_embed('os')
function disrupt(err) function disrupt(err)
{ {
if (overling) { if (overling) {
@@ -97,10 +95,12 @@ function disrupt(err)
log.error(err) log.error(err)
log.console("DISRUPTING")
actor_mod.disrupt() actor_mod.disrupt()
} }
os.on(disrupt) actor_mod.on_exception(disrupt)
var js = use_embed('js') var js = use_embed('js')
@@ -688,7 +688,7 @@ function destroyself() {
if (overling) report_to_overling({type:'stop'}) if (overling) report_to_overling({type:'stop'})
actor_mod.destroy() actor_mod.disrupt()
} }
function handle_actor_disconnect(id) { function handle_actor_disconnect(id) {

View File

@@ -7,12 +7,10 @@ os.freemem[cell.DOC] = "Return the amount of free system RAM in bytes, if known.
os.hostname[cell.DOC] = "Return the system's hostname, or an empty string if not available." os.hostname[cell.DOC] = "Return the system's hostname, or an empty string if not available."
os.version[cell.DOC] = "Return the OS or kernel version string, if the platform provides it." os.version[cell.DOC] = "Return the OS or kernel version string, if the platform provides it."
os.exit[cell.DOC] = "Exit the application with the specified exit code."
os.now[cell.DOC] = "Return current time (in seconds as a float) with high resolution." os.now[cell.DOC] = "Return current time (in seconds as a float) with high resolution."
os.power_state[cell.DOC] = "Return a string describing power status: 'on battery', 'charging', 'charged', etc." os.power_state[cell.DOC] = "Return a string describing power status: 'on battery', 'charging', 'charged', etc."
os.on[cell.DOC] = "Register a global callback for certain engine-wide or system-level events."
os.rusage[cell.DOC] = "Return resource usage stats for this process, if the platform supports it." os.rusage[cell.DOC] = "Return resource usage stats for this process, if the platform supports it."
os.mallinfo[cell.DOC] = "Return detailed memory allocation info (arena size, free blocks, etc.) on some platforms." os.mallinfo[cell.DOC] = "Return detailed memory allocation info (arena size, free blocks, etc.) on some platforms."

View File

@@ -45,7 +45,7 @@ int tracy_profiling_enabled = 0;
static cell_rt **ready_queue = NULL; static cell_rt **ready_queue = NULL;
static cell_rt **main_ready_queue = NULL; static cell_rt **main_ready_queue = NULL;
static SDL_Mutex *queue_mutex = NULL; static SDL_Mutex *queue_mutex = NULL; // for the ready queue
static SDL_Condition *queue_cond = NULL; static SDL_Condition *queue_cond = NULL;
static SDL_Mutex *actors_mutex = NULL; static SDL_Mutex *actors_mutex = NULL;
static struct { char *key; cell_rt *value; } *actors = NULL; static struct { char *key; cell_rt *value; } *actors = NULL;
@@ -56,106 +56,117 @@ cell_rt *root_cell = NULL;
static SDL_AtomicInt engine_shutdown; static SDL_AtomicInt engine_shutdown;
static SDL_Thread **runners = NULL; static SDL_Thread **runners = NULL;
// TIMERS // ─────────────────────────────────────────────────────────────────────────────
struct timer_entry { uint64_t when_ns; cell_rt *actor; uint32_t id; }; // TIMER “SDL_AddTimerNS”replacement
static struct timer_entry *timers = NULL; // ─────────────────────────────────────────────────────────────────────────────
static uint32_t timer_counter = 1;
static SDL_Mutex *timer_mutex = NULL; // protects access to timers
static inline uint64_t now_ns() typedef Uint32 (*TimerCallback)(Uint32 timer_id, Uint32 interval, void *param);
{
return SDL_GetTicksNS(); 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;
static SDL_Condition *timer_cond = 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;
} }
// Add a message into each actor's event queue for expired timers // Schedule a new timer to fire after `delay_ns` nanoseconds.
static void process_due_timers(void) // 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
uint64_t tnow = now_ns(); // 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); SDL_LockMutex(timer_mutex);
while (arrlen(timers) && timers[0].when_ns <= tnow) { arrput(timers, t);
struct timer_entry te = timers[0]; SDL_SignalCondition(timer_cond);
arrdel(timers, 0);
SDL_UnlockMutex(timer_mutex);
/* Marshal callback into actor mailbox. */
SDL_LockMutex(te.actor->msg_mutex);
int idx = hmgeti(te.actor->timers, te.id);
if (idx != -1) {
JSValue cb = te.actor->timers[idx].value; /* already duped when stored */
hmdel(te.actor->timers, te.id);
letter l;
l.type = LETTER_CALLBACK;
l.callback = cb;
arrput(te.actor->letters, l);
}
SDL_UnlockMutex(te.actor->msg_mutex);
set_actor_state(te.actor);
SDL_LockMutex(timer_mutex);
}
SDL_UnlockMutex(timer_mutex); SDL_UnlockMutex(timer_mutex);
return t.id;
} }
static uint64_t next_timeout_ns(void) // Cancel (remove) a pending timer by its ID. If not found, does nothing.
{ void remove_timer(Uint32 id) {
SDL_LockMutex(timer_mutex); SDL_LockMutex(timer_mutex);
uint64_t timeout = arrlen(timers) ? (timers[0].when_ns - now_ns()) : UINT64_MAX; for (int i = 0; i < arrlen(timers); i++) {
SDL_UnlockMutex(timer_mutex);
return timeout;
}
static void timer_insert(struct timer_entry te)
{
SDL_LockMutex(timer_mutex);
size_t n = arrlen(timers);
arrput(timers, te);
for (size_t i = n; i > 0 && timers[i].when_ns < timers[i-1].when_ns; --i) {
struct timer_entry tmp = timers[i];
timers[i] = timers[i-1];
timers[i-1] = tmp;
}
SDL_UnlockMutex(timer_mutex);
}
/* Public API used by JS glue (replaces SDL_AddTimer). */
uint32_t scheduler_add_timer(cell_rt *actor, double seconds, JSValue cb)
{
uint32_t id = SDL_AtomicIncRef((SDL_AtomicInt*)&timer_counter);
uint64_t deadline = now_ns() + (uint64_t)(seconds * 1e9);
SDL_LockMutex(timer_mutex);
timer_insert((struct timer_entry){ deadline, actor, id });
SDL_UnlockMutex(timer_mutex);
/* Stash in actor map so removal/GC works. Duplicated to hold ref. */
SDL_LockMutex(actor->msg_mutex);
hmput(actor->timers, id, JS_DupValue(actor->context, cb));
SDL_UnlockMutex(actor->msg_mutex);
/* Wake a sleeper if this timer is sooner than the current next. */
SDL_SignalCondition(queue_cond);
return id;
}
void scheduler_remove_timer(cell_rt *actor, uint32_t id)
{
SDL_LockMutex(timer_mutex);
for (int i = 0; i < arrlen(timers); ++i) {
if (timers[i].id == id) { if (timers[i].id == id) {
arrdel(timers, i); arrdel(timers, i);
break; break;
} }
} }
SDL_UnlockMutex(timer_mutex); SDL_UnlockMutex(timer_mutex);
}
SDL_LockMutex(actor->msg_mutex); // Scan the global `timers[]` array and invoke any timer whose due_ns <= now.
int idx = hmgeti(actor->timers, id); // For each such timer:
if (idx != -1) { // 1) pop it out of the `timers` array
JS_FreeValue(actor->context, actor->timers[idx].value); // 2) call `callback(interval_ms,param)`
hmdel(actor->timers, id); // 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(actor->msg_mutex); 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();
} }
static void exit_handler(void) static void exit_handler(void)
@@ -196,7 +207,6 @@ void actor_free(cell_rt *actor)
SDL_UnlockMutex(queue_mutex); SDL_UnlockMutex(queue_mutex);
// Do not go forward with actor destruction until the actor is completely free // Do not go forward with actor destruction until the actor is completely free
SDL_LockMutex(actor->mutex);
SDL_LockMutex(actor->msg_mutex); SDL_LockMutex(actor->msg_mutex);
SDL_LockMutex(actor->mutex); SDL_LockMutex(actor->mutex);
@@ -208,19 +218,10 @@ void actor_free(cell_rt *actor)
JS_FreeValue(js, actor->unneeded); JS_FreeValue(js, actor->unneeded);
JS_FreeAtom(js, actor->actor_sym); JS_FreeAtom(js, actor->actor_sym);
SDL_RemoveTimer(actor->ar); remove_timer(actor->ar);
for (int i = 0; i < arrlen(actor->js_swapchains); i++) for (int i = 0; i < arrlen(actor->js_swapchains); i++)
JS_FreeValue(js, actor->js_swapchains[i]); JS_FreeValue(js, actor->js_swapchains[i]);
/* Remove all timers for this actor from the global timer queue */
SDL_LockMutex(timer_mutex);
for (int i = arrlen(timers) - 1; i >= 0; i--) {
if (timers[i].actor == actor) {
arrdel(timers, i);
}
}
SDL_UnlockMutex(timer_mutex);
/* Free timer callbacks stored in actor */ /* Free timer callbacks stored in actor */
for (int i = 0; i < hmlen(actor->timers); i++) { for (int i = 0; i < hmlen(actor->timers); i++) {
JS_FreeValue(js, actor->timers[i].value); JS_FreeValue(js, actor->timers[i].value);
@@ -251,25 +252,19 @@ void actor_free(cell_rt *actor)
SDL_DestroyMutex(actor->mutex); SDL_DestroyMutex(actor->mutex);
SDL_UnlockMutex(actor->msg_mutex); SDL_UnlockMutex(actor->msg_mutex);
SDL_DestroyMutex(actor->msg_mutex); SDL_DestroyMutex(actor->msg_mutex);
SDL_UnlockMutex(actor->mutex);
SDL_DestroyMutex(actor->mutex);
free(actor); free(actor);
if (actor == root_cell) SDL_LockMutex(actors_mutex);
if (shlen(actors) == 0)
exit(0); exit(0);
// exit_handler(); SDL_UnlockMutex(actors_mutex);
} }
JSValue js_actor_remove_cb(JSContext *js, JSValue this_val, int argc, JSValue *argv) JSValue js_actor_remove_cb(JSContext *js, JSValue this_val, int argc, JSValue *argv)
{ {
cell_rt *actor = JS_GetContextOpaque(js); cell_rt *actor = JS_GetContextOpaque(js);
if (actor->need_stop) {
actor_free(actor);
return JS_UNDEFINED;
}
if (JS_IsUndefined(actor->unneeded)) if (JS_IsUndefined(actor->unneeded))
actor_free(actor); actor_free(actor);
else { else {
@@ -497,7 +492,7 @@ const char *send_message(const char *id, void *msg)
SDL_LockMutex(target->msg_mutex); SDL_LockMutex(target->msg_mutex);
arrput(target->letters, l); arrput(target->letters, l);
if (target->ar) { if (target->ar) {
SDL_RemoveTimer(target->ar); remove_timer(target->ar);
target->ar = 0; target->ar = 0;
} }
SDL_UnlockMutex(target->msg_mutex); SDL_UnlockMutex(target->msg_mutex);
@@ -507,58 +502,69 @@ const char *send_message(const char *id, void *msg)
return NULL; return NULL;
} }
/* static Uint32 actor_remove_cb(Uint32 id, Uint32 interval, cell_rt *actor)
set_actor_state should check if any letters are pending. {
if (!JS_IsUndefined(actor->unneeded)) {
*/ SDL_LockMutex(actor->mutex);
void set_actor_state(cell_rt *actor) JSValue ret = JS_Call(actor->context, actor->unneeded, JS_UNDEFINED, 0, NULL);
uncaught_exception(actor->context, ret);
SDL_UnlockMutex(actor->mutex);
}
actor_free(actor);
return 0;
}
/* Timer callback adds an event to the queue under evt_mutex. */
Uint32 actor_delay_cb(SDL_TimerID id, Uint32 interval, cell_rt *actor)
{ {
SDL_LockMutex(actor->msg_mutex); SDL_LockMutex(actor->msg_mutex);
if (actor->need_stop) { int idx = hmgeti(actor->timers, id);
if (!actor->ar) { if (idx == -1) goto END;
JSValue remove_cb = JS_NewCFunction(actor->context, js_actor_remove_cb, "actor_remove", 0);
actor->ar = scheduler_add_timer(actor, 0, remove_cb);
JS_FreeValue(actor->context, remove_cb);
}
SDL_UnlockMutex(actor->msg_mutex);
return;
}
int has_letters = arrlen(actor->letters);
int has_upcoming = hmlen(actor->timers);
if (actor->state == ACTOR_RUNNING)
actor->state = ACTOR_IDLE;
switch(actor->state) { JSValue cb = actor->timers[idx].value;
case ACTOR_IDLE: hmdel(actor->timers, id);
if (has_letters) { letter l ={0};
actor->state = ACTOR_READY; l.type = LETTER_CALLBACK;
SDL_LockMutex(queue_mutex); l.callback = cb;
if (actor->main_thread_only) { arrput(actor->letters, l);
arrput(main_ready_queue, actor); set_actor_state(actor);
} else {
SDL_SignalCondition(queue_cond);
arrput(ready_queue, actor);
}
SDL_UnlockMutex(queue_mutex);
goto END;
}
break;
case ACTOR_READY:
if (!has_letters) {
actor->state = ACTOR_IDLE;
goto END;
}
break;
}
END: END:
if (actor->state == ACTOR_IDLE && !actor->ar && !has_upcoming) { SDL_UnlockMutex(actor->msg_mutex);
JSValue remove_cb = JS_NewCFunction(actor->context, js_actor_remove_cb, "actor_remove", 0); return 0;
actor->ar = scheduler_add_timer(actor, actor->ar_secs, remove_cb); }
JS_FreeValue(actor->context, remove_cb);
void set_actor_state(cell_rt *actor)
{
if (actor->disrupt) {
actor_free(actor);
return;
}
SDL_LockMutex(actor->msg_mutex);
switch(actor->state) {
case ACTOR_RUNNING:
case ACTOR_READY:
if (actor->ar) {
remove_timer(actor->ar);
actor->ar = 0;
}
break;
case ACTOR_IDLE:
if (arrlen(actor->letters)) {
actor->state = ACTOR_READY;
SDL_LockMutex(queue_mutex);
if (actor->main_thread_only)
arrput(main_ready_queue, actor);
else
arrput(ready_queue, actor);
SDL_SignalCondition(queue_cond);
SDL_UnlockMutex(queue_mutex);
} else if (!arrlen(actor->letters) && !hmlen(actor->timers))
actor->ar = add_timer_ns(actor->ar_secs*1e9, actor_remove_cb, actor);
break;
} }
SDL_UnlockMutex(actor->msg_mutex); SDL_UnlockMutex(actor->msg_mutex);
@@ -594,6 +600,9 @@ void actor_turn(cell_rt *actor)
JS_FreeValue(actor->context, l.callback); JS_FreeValue(actor->context, l.callback);
} }
// now idle
actor->state = ACTOR_IDLE;
SDL_UnlockMutex(actor->mutex); SDL_UnlockMutex(actor->mutex);
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
@@ -604,7 +613,6 @@ void actor_turn(cell_rt *actor)
set_actor_state(actor); set_actor_state(actor);
} }
/* JS function that schedules a timer. */ /* JS function that schedules a timer. */
JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv) JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv)
{ {
@@ -623,13 +631,11 @@ JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv)
SDL_UnlockMutex(actor->msg_mutex); SDL_UnlockMutex(actor->msg_mutex);
return JS_NewInt32(js, -1); return JS_NewInt32(js, -1);
} }
uint32_t id = scheduler_add_timer(actor, seconds, argv[0]); uint32_t id = add_timer_ns(seconds*1e9, actor_delay_cb, actor);
SDL_LockMutex(actor->msg_mutex); SDL_LockMutex(actor->msg_mutex);
if (actor->ar) { JSValue cb = JS_DupValue(js, argv[0]);
SDL_RemoveTimer(actor->ar); hmput(actor->timers, id, cb);
actor->ar = 0;
}
SDL_UnlockMutex(actor->msg_mutex); SDL_UnlockMutex(actor->msg_mutex);
return JS_NewUint32(js, id); return JS_NewUint32(js, id);
@@ -640,9 +646,21 @@ JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *arg
cell_rt *actor = JS_GetContextOpaque(js); cell_rt *actor = JS_GetContextOpaque(js);
uint32_t timer_id; uint32_t timer_id;
JS_ToUint32(js, &timer_id, argv[0]); JS_ToUint32(js, &timer_id, argv[0]);
if (timer_id == (uint32_t)-1) return JS_UNDEFINED; if (timer_id == -1) return JS_UNDEFINED;
scheduler_remove_timer(actor, timer_id); remove_timer(timer_id);
JSValue cb = JS_UNDEFINED;
SDL_LockMutex(actor->msg_mutex);
int id = hmgeti(actor->timers, timer_id);
if (id != -1) {
cb = actor->timers[id].value;
hmdel(actor->timers, timer_id);
}
SDL_UnlockMutex(actor->msg_mutex);
JS_FreeValue(js,cb);
return JS_UNDEFINED; return JS_UNDEFINED;
} }
@@ -706,14 +724,11 @@ void tracy_end_hook(JSContext *js, JSValue fn)
void actor_disrupt(cell_rt *crt) void actor_disrupt(cell_rt *crt)
{ {
crt->disrupt = 1; crt->disrupt = 1;
crt->need_stop = 1;
} }
static int actor_interrupt_cb(JSRuntime *rt, cell_rt *crt) static int actor_interrupt_cb(JSRuntime *rt, cell_rt *crt)
{ {
if (crt->disrupt) return crt->disrupt;
return 1;
return 0;
} }
void script_startup(cell_rt *prt) void script_startup(cell_rt *prt)
@@ -745,9 +760,6 @@ void script_startup(cell_rt *prt)
JS_AddIntrinsicJSON(js); JS_AddIntrinsicJSON(js);
JS_AddIntrinsicMapSet(js); JS_AddIntrinsicMapSet(js);
JS_AddIntrinsicProxy(js); JS_AddIntrinsicProxy(js);
// JS_AddIntrinsicTypedArrays(js);
// JS_AddIntrinsicDate(js);
// JS_AddIntrinsicPromise(js);
JS_SetContextOpaque(js, prt); JS_SetContextOpaque(js, prt);
prt->context = js; prt->context = js;
@@ -765,7 +777,6 @@ void script_startup(cell_rt *prt)
PHYSFS_close(eng); PHYSFS_close(eng);
data[stat.filesize] = 0; data[stat.filesize] = 0;
/* Called with actor->mutex locked by create_actor(). */
JSValue v = JS_Eval(js, data, (size_t)stat.filesize, ENGINE, JS_EVAL_FLAG_STRICT); JSValue v = JS_Eval(js, data, (size_t)stat.filesize, ENGINE, JS_EVAL_FLAG_STRICT);
uncaught_exception(js, v); uncaught_exception(js, v);
} }
@@ -790,29 +801,7 @@ int uncaught_exception(JSContext *js, JSValue v)
return 0; return 0;
} }
void script_evalf(JSContext *js, const char *format, ...) static int actor_runner(void *data)
{
cell_rt *rt = JS_GetContextOpaque(js);
SDL_LockMutex(rt->mutex);
va_list args;
va_start(args, format);
int len = vsnprintf(NULL, 0, format, args);
va_end(args);
char *eval = malloc(len + 1);
va_start(args, format);
vsnprintf(eval, len + 1, format, args);
va_end(args);
JSValue obj = JS_Eval(js, eval, len, "C eval", JS_EVAL_FLAG_STRICT);
free(eval);
uncaught_exception(js, obj);
SDL_UnlockMutex(rt->mutex);
}
static int crank_actor(void *data)
{ {
while (!SDL_GetAtomicInt(&engine_shutdown)) { while (!SDL_GetAtomicInt(&engine_shutdown)) {
SDL_LockMutex(queue_mutex); SDL_LockMutex(queue_mutex);
@@ -848,7 +837,6 @@ static void signal_handler(int sig)
exit_handler(); exit_handler();
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int profile_enabled = 0; int profile_enabled = 0;
@@ -926,6 +914,7 @@ int main(int argc, char **argv)
queue_cond = SDL_CreateCondition(); queue_cond = SDL_CreateCondition();
actors_mutex = SDL_CreateMutex(); actors_mutex = SDL_CreateMutex();
timer_mutex = SDL_CreateMutex(); timer_mutex = SDL_CreateMutex();
timer_cond = SDL_CreateCondition();
/* Create the initial actor from the command line */ /* Create the initial actor from the command line */
int actor_argc = argc - script_start; int actor_argc = argc - script_start;
@@ -946,7 +935,7 @@ int main(int argc, char **argv)
for (int i = 0; i < cores-1; i++) { // -1 to keep the main thread free for (int i = 0; i < cores-1; i++) { // -1 to keep the main thread free
char threadname[128]; char threadname[128];
snprintf(threadname, sizeof(threadname), "actor runner %d", i); snprintf(threadname, sizeof(threadname), "actor runner %d", i);
SDL_Thread *thread = SDL_CreateThread(crank_actor, threadname, NULL); SDL_Thread *thread = SDL_CreateThread(actor_runner, threadname, NULL);
arrput(runners, thread); arrput(runners, thread);
} }
@@ -975,8 +964,10 @@ int main(int argc, char **argv)
uint64_t to_ns = next_timeout_ns(); uint64_t to_ns = next_timeout_ns();
if (to_ns == UINT64_MAX) { if (to_ns == UINT64_MAX) {
// No timers pending, wait indefinitely SDL_LockMutex(timer_mutex);
pselect(0, NULL, NULL, NULL, NULL, NULL); SDL_WaitCondition(timer_cond, timer_mutex);
SDL_UnlockMutex(timer_mutex);
continue;
} else { } else {
struct timespec timeout = { struct timespec timeout = {
.tv_sec = to_ns / 1000000000, .tv_sec = to_ns / 1000000000,
@@ -1000,7 +991,7 @@ int actor_exists(const char *id)
return 1; return 1;
} }
/*int JS_ArrayLength(JSContext *js, JSValue a) int JS_ArrayLength(JSContext *js, JSValue a)
{ {
JSValue length = JS_GetPropertyStr(js, a, "length"); JSValue length = JS_GetPropertyStr(js, a, "length");
int len; int len;
@@ -1008,4 +999,3 @@ int actor_exists(const char *id)
JS_FreeValue(js,length); JS_FreeValue(js,length);
return len; return len;
} }
*/

View File

@@ -58,7 +58,7 @@ typedef struct cell_rt {
/* Protects JSContext usage */ /* Protects JSContext usage */
SDL_Mutex *mutex; /* for access to the JSContext */ SDL_Mutex *mutex; /* for access to the JSContext */
SDL_Mutex *msg_mutex; /* For messages queue only */ SDL_Mutex *msg_mutex; /* For message queue and timers */
char *id; char *id;
MTRand mrand; MTRand mrand;
@@ -68,7 +68,6 @@ typedef struct cell_rt {
/* The “mailbox” for incoming messages + a dedicated lock for it: */ /* The “mailbox” for incoming messages + a dedicated lock for it: */
struct letter *letters; struct letter *letters;
/* CHANGED FOR EVENTS: a separate lock for the actor->events queue */ /* CHANGED FOR EVENTS: a separate lock for the actor->events queue */
struct { Uint32 key; JSValue value; } *timers; struct { Uint32 key; JSValue value; } *timers;
@@ -77,7 +76,6 @@ typedef struct cell_rt {
double ar_secs; // time for unneeded double ar_secs; // time for unneeded
JSValue unneeded; // fn to call before unneeded JSValue unneeded; // fn to call before unneeded
int need_stop;
int disrupt; int disrupt;
int main_thread_only; int main_thread_only;
@@ -99,8 +97,6 @@ Uint32 actor_timer_cb(cell_rt *actor, SDL_TimerID id, Uint32 interval);
JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv); JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv);
JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *argv); JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *argv);
void script_startup(cell_rt *rt); void script_startup(cell_rt *rt);
void script_evalf(JSContext *js, const char *format, ...);
JSValue script_eval(JSContext *js, const char *file, const char *script);
int uncaught_exception(JSContext *js, JSValue v); int uncaught_exception(JSContext *js, JSValue v);
int actor_exists(const char *id); int actor_exists(const char *id);
cell_rt *get_actor(char *id); cell_rt *get_actor(char *id);

View File

@@ -139,12 +139,6 @@ JSC_CCALL(os_unneeded,
SDL_UnlockMutex(actor->msg_mutex); SDL_UnlockMutex(actor->msg_mutex);
) )
JSC_CCALL(os_destroy,
cell_rt *rt = JS_GetContextOpaque(js);
rt->need_stop = 1;
set_actor_state(rt);
)
JSC_CCALL(actor_disrupt, JSC_CCALL(actor_disrupt,
cell_rt *crt = JS_GetContextOpaque(js); cell_rt *crt = JS_GetContextOpaque(js);
actor_disrupt(crt); actor_disrupt(crt);
@@ -160,6 +154,12 @@ JSC_CCALL(actor_testfn,
printf("actor? %p\n", crt); printf("actor? %p\n", crt);
) )
JSC_CCALL(actor_on_exception,
cell_rt *rt = JS_GetContextOpaque(js);
JS_FreeValue(js, rt->on_exception);
rt->on_exception = JS_DupValue(js,argv[0]);
)
static const JSCFunctionListEntry js_actor_funcs[] = { static const JSCFunctionListEntry js_actor_funcs[] = {
MIST_FUNC_DEF(os, createactor, 1), MIST_FUNC_DEF(os, createactor, 1),
MIST_FUNC_DEF(os, mailbox_push, 2), MIST_FUNC_DEF(os, mailbox_push, 2),
@@ -168,10 +168,10 @@ static const JSCFunctionListEntry js_actor_funcs[] = {
MIST_FUNC_DEF(actor, removetimer, 1), MIST_FUNC_DEF(actor, removetimer, 1),
MIST_FUNC_DEF(os, register_actor, 4), MIST_FUNC_DEF(os, register_actor, 4),
MIST_FUNC_DEF(os, unneeded, 2), MIST_FUNC_DEF(os, unneeded, 2),
MIST_FUNC_DEF(os, destroy, 0),
MIST_FUNC_DEF(actor, disrupt, 0), MIST_FUNC_DEF(actor, disrupt, 0),
MIST_FUNC_DEF(actor, setname, 1), MIST_FUNC_DEF(actor, setname, 1),
MIST_FUNC_DEF(actor, testfn, 1), MIST_FUNC_DEF(actor, testfn, 1),
MIST_FUNC_DEF(actor, on_exception, 1),
}; };
JSValue js_actor_use(JSContext *js) { JSValue js_actor_use(JSContext *js) {

View File

@@ -7,11 +7,9 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <dirent.h> #include <dirent.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include "wildmatch.h"
// File descriptor wrapper structure // File descriptor wrapper structure
typedef struct { typedef struct {

View File

@@ -28,7 +28,6 @@
#endif #endif
#endif #endif
JSC_CCALL(os_exit, exit(js2number(js,argv[0]));)
JSC_CCALL(os_now, return number2js(js, (double)SDL_GetTicksNS()/1000000000.0)) JSC_CCALL(os_now, return number2js(js, (double)SDL_GetTicksNS()/1000000000.0))
JSC_CCALL(os_sleep, JSC_CCALL(os_sleep,
@@ -169,12 +168,6 @@ static JSValue js_os_version(JSContext *js, JSValue self, int argc, JSValue *arg
return ret; return ret;
} }
JSC_CCALL(os_on,
cell_rt *rt = JS_GetContextOpaque(js);
JS_FreeValue(js, rt->on_exception);
rt->on_exception = JS_DupValue(js,argv[0]);
)
JSC_CCALL(os_buffer2string, JSC_CCALL(os_buffer2string,
if (argc < 1) { if (argc < 1) {
return JS_ThrowTypeError(js, "buffer2string expects an ArrayBuffer"); return JS_ThrowTypeError(js, "buffer2string expects an ArrayBuffer");
@@ -289,10 +282,8 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, freemem, 0), MIST_FUNC_DEF(os, freemem, 0),
MIST_FUNC_DEF(os, hostname, 0), MIST_FUNC_DEF(os, hostname, 0),
MIST_FUNC_DEF(os, version, 0), MIST_FUNC_DEF(os, version, 0),
MIST_FUNC_DEF(os, exit, 1),
MIST_FUNC_DEF(os, now, 0), MIST_FUNC_DEF(os, now, 0),
MIST_FUNC_DEF(os, power_state, 0), MIST_FUNC_DEF(os, power_state, 0),
MIST_FUNC_DEF(os, on, 1),
MIST_FUNC_DEF(os, rusage, 0), MIST_FUNC_DEF(os, rusage, 0),
MIST_FUNC_DEF(os, mallinfo, 0), MIST_FUNC_DEF(os, mallinfo, 0),
MIST_FUNC_DEF(os, buffer2string, 1), MIST_FUNC_DEF(os, buffer2string, 1),

View File

@@ -9,16 +9,6 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
void input_system_init(void)
{
// No initialization needed for simplified system
}
void input_system_cleanup(void)
{
// No cleanup needed for simplified system
}
// Internal keymod function for input module // Internal keymod function for input module
static JSValue js_keymod(JSContext *js) static JSValue js_keymod(JSContext *js)
{ {

View File

@@ -2,14 +2,8 @@
#define QJS_SDL_INPUT_H #define QJS_SDL_INPUT_H
#include <quickjs.h> #include <quickjs.h>
#include <SDL3/SDL.h>
// Initialization and cleanup functions const char* event_type_to_string(uint32_t event_type);
void input_system_init(void);
void input_system_cleanup(void);
// Event conversion functions
const char* event_type_to_string(Uint32 event_type);
const char* mouse_button_to_string(int mouse); const char* mouse_button_to_string(int mouse);
// JavaScript module entry point // JavaScript module entry point

View File

@@ -1,3 +1,4 @@
var count = 0 var count = 0
function loop() function loop()
{ {

View File

@@ -2,6 +2,7 @@
var io = use('io') var io = use('io')
var shop = use('shop') var shop = use('shop')
var time = use('time')
log.console("=== Testing Module System ===") log.console("=== Testing Module System ===")
@@ -26,7 +27,7 @@ log.console("✓ TOML parser working")
// Test 2: Shop initialization // Test 2: Shop initialization
log.console("\n2. Testing shop initialization...") log.console("\n2. Testing shop initialization...")
var test_dir = "module_test_" + Date.now() var test_dir = "module_test_" + time.number()
io.mkdir(test_dir) io.mkdir(test_dir)
var old_cwd = io.basedir() var old_cwd = io.basedir()