root actor runs only on main thread; restrict ffi main thread functions to it

This commit is contained in:
2025-03-22 20:55:20 -05:00
parent 73594c8599
commit f9c1a3e71a
4 changed files with 80 additions and 7 deletions

View File

@@ -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);

View File

@@ -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);
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;
@@ -327,6 +340,8 @@ void actor_turn(prosperon_rt *actor)
JS_FreeValue(actor->context,arg);
SDL_UnlockMutex(actor->mutex);
if (!greedy) goto END;
SDL_LockMutex(actor->msg_mutex);
need_stop = actor->need_stop;
msgs = arrlen(actor->messages);
@@ -359,6 +374,8 @@ void actor_turn(prosperon_rt *actor)
JS_FreeValue(actor->context, event);
SDL_UnlockMutex(actor->mutex);
if (!greedy) goto END;
SDL_LockMutex(actor->msg_mutex);
events = arrlen(actor->events);
need_stop = actor->need_stop;
@@ -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;
}

View File

@@ -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;

27
tests/window.js Normal file
View File

@@ -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)