From f9c1a3e71a15343d75ea77caf06498abca7d8846 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sat, 22 Mar 2025 20:55:20 -0500 Subject: [PATCH] root actor runs only on main thread; restrict ffi main thread functions to it --- source/jsffi.c | 6 ++++++ source/prosperon.c | 53 ++++++++++++++++++++++++++++++++++++++++------ source/prosperon.h | 1 + tests/window.js | 27 +++++++++++++++++++++++ 4 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 tests/window.js diff --git a/source/jsffi.c b/source/jsffi.c index 345e8e2a..42fc48dc 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -2457,6 +2457,9 @@ JS_FreeValue(js,v); \ } \ JSC_CCALL(os_engine_start, + if (SDL_GetCurrentThreadID() != main_thread) + return JS_ThrowInternalError(js, "This can only be called from the root actor."); + if (global_window) return JS_ThrowReferenceError(js, "The engine was already started."); @@ -6298,6 +6301,9 @@ JSC_CCALL(os_make_surface, ) JSC_CCALL(os_make_cursor, + if (SDL_GetCurrentThreadID() != main_thread) + return JS_ThrowInternalError(js, "This can only be called from the root actor."); + SDL_Surface *s = js2SDL_Surface(js,argv[0]); HMM_Vec2 hot = js2vec2(js,argv[1]); SDL_Cursor *c = SDL_CreateColorCursor(s, hot.x, hot.y); diff --git a/source/prosperon.c b/source/prosperon.c index 2b1e3ba1..873af499 100644 --- a/source/prosperon.c +++ b/source/prosperon.c @@ -41,6 +41,7 @@ #define ENGINE "scripts/core/engine.js" static prosperon_rt **ready_queue = NULL; +static prosperon_rt **main_ready_queue = NULL; static SDL_Mutex *queue_mutex = NULL; static SDL_Condition *queue_cond = NULL; static SDL_Mutex *actors_mutex = NULL; @@ -48,6 +49,8 @@ static struct { char *key; prosperon_rt *value; } *actors = NULL; static unsigned char *zip_buffer_global = NULL; static char *prosperon = NULL; +static Uint32 queue_event; + static SDL_AtomicInt shutdown; static SDL_Thread **runners = NULL; @@ -226,6 +229,8 @@ prosperon_rt *get_actor(char *id) char *register_actor(char *id, prosperon_rt *actor) { if (shgeti(actors, id) != -1) return "Actor with given ID already exists."; + if (shlen(actors) == 0) + actor->main_thread_only = 1; actor->id = strdup(id); SDL_LockMutex(actors_mutex); shput(actors, id, actor); @@ -262,6 +267,7 @@ static void set_actor_state(prosperon_rt *actor) int has_messages = arrlen(actor->messages); int has_events = arrlen(actor->events); + int has_upcoming = hmlen(actor->timers); if (actor->state == ACTOR_RUNNING) actor->state = ACTOR_IDLE; @@ -271,8 +277,15 @@ static void set_actor_state(prosperon_rt *actor) if (has_messages || has_events) { actor->state = ACTOR_READY; SDL_LockMutex(queue_mutex); - arrput(ready_queue, actor); - SDL_SignalCondition(queue_cond); + if (actor->main_thread_only) { + arrput(main_ready_queue, actor); + SDL_Event event; + event.type = queue_event; + SDL_PushEvent(&event); + } else { + SDL_SignalCondition(queue_cond); + arrput(ready_queue, actor); + } SDL_UnlockMutex(queue_mutex); goto END; } @@ -287,12 +300,12 @@ static void set_actor_state(prosperon_rt *actor) } END: - if (actor->state == ACTOR_IDLE && !actor->ar) + if (actor->state == ACTOR_IDLE && !actor->ar && !has_upcoming) actor->ar = SDL_AddTimerNS(SDL_SECONDS_TO_NS(1), actor_remove_cb, actor); SDL_UnlockMutex(actor->msg_mutex); } -void actor_turn(prosperon_rt *actor) +void actor_turn(prosperon_rt *actor, int greedy) { SDL_LockMutex(actor->msg_mutex); actor->state = ACTOR_RUNNING; @@ -326,6 +339,8 @@ void actor_turn(prosperon_rt *actor) uncaught_exception(actor->context, result); JS_FreeValue(actor->context,arg); SDL_UnlockMutex(actor->mutex); + + if (!greedy) goto END; SDL_LockMutex(actor->msg_mutex); need_stop = actor->need_stop; @@ -358,6 +373,8 @@ void actor_turn(prosperon_rt *actor) uncaught_exception(actor->context, result); JS_FreeValue(actor->context, event); SDL_UnlockMutex(actor->mutex); + + if (!greedy) goto END; SDL_LockMutex(actor->msg_mutex); events = arrlen(actor->events); @@ -486,6 +503,10 @@ JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv) SDL_LockMutex(actor->msg_mutex); JSValue cb = JS_DupValue(js, argv[0]); hmput(actor->timers, id, cb); + if (actor->ar) { + SDL_RemoveTimer(actor->ar); + actor->ar = 0; + } SDL_UnlockMutex(actor->msg_mutex); return JS_UNDEFINED; @@ -627,7 +648,7 @@ static int crank_actor(void *data) } SDL_UnlockMutex(queue_mutex); - if (actor) actor_turn(actor); + if (actor) actor_turn(actor, 1); } return 0; } @@ -1279,17 +1300,18 @@ static WotaBuffer event2wota(const SDL_Event *event) { return wb; } - - int main(int argc, char **argv) { SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO); + queue_event = SDL_RegisterEvents(1); + int cores = SDL_GetNumLogicalCPUCores(); if (cores == 1) { printf("Platform has only one core!\n"); return 1; } main_thread = SDL_GetCurrentThreadID(); + printf("main is %u\n", main_thread); if (!main_thread) { printf("Platform does not support threads!\n"); return 1; @@ -1331,6 +1353,9 @@ int main(int argc, char **argv) SDL_Event event; while (SDL_WaitEvent(&event)) { + if (event.type == queue_event) + goto QUEUE; + WotaBuffer wb = event2wota(&event); // Broadcast this event to all actors @@ -1345,6 +1370,20 @@ int main(int argc, char **argv) SDL_UnlockMutex(actors_mutex); wota_buffer_free(&wb); // free WotaBuffer + + QUEUE: + SDL_LockMutex(queue_mutex); + if (arrlen(main_ready_queue) == 0) { + SDL_UnlockMutex(queue_mutex); + continue; + } + prosperon_rt *actor = main_ready_queue[0]; + arrdel(main_ready_queue, 0); + SDL_UnlockMutex(queue_mutex); + + actor_turn(actor, 0); + + END: } return 0; } diff --git a/source/prosperon.h b/source/prosperon.h index 28ffbde5..6f7e623a 100644 --- a/source/prosperon.h +++ b/source/prosperon.h @@ -62,6 +62,7 @@ typedef struct prosperon_rt { int state; Uint32 ar; int need_stop; + int main_thread_only; } prosperon_rt; extern SDL_ThreadID main_thread; diff --git a/tests/window.js b/tests/window.js new file mode 100644 index 00000000..30d65007 --- /dev/null +++ b/tests/window.js @@ -0,0 +1,27 @@ +prosperon.win = prosperon.engine_start({ + title:`Prosperon [${prosperon.version}-${prosperon.revision}]`, + width: 1280, + height: 720, + high_dpi:0, + alpha:1, + fullscreen:0, + sample_count:1, + enable_clipboard:true, + enable_dragndrop: true, + max_dropped_files: 1, + swap_interval: 1, + name: "Prosperon", + version:prosperon.version + "-" + prosperon.revision, + identifier: "world.pockle.prosperon", + creator: "Pockle World LLC", + copyright: "Copyright Pockle World 2025", + type: "game", + url: "https://prosperon.dev" +}) + +function loop() { + $_.delay(loop, 1/60) +} +loop() + +$_.delay($_.stop, 3)