diff --git a/scripts/core/engine.js b/scripts/core/engine.js index d96f4a52..c237d282 100644 --- a/scripts/core/engine.js +++ b/scripts/core/engine.js @@ -31,10 +31,6 @@ if (os.trace) var js = use_embed('js') -prosperon.on('exit', _ => { - os.mailbox_destroy(prosperon.id); -}) - prosperon.on('SIGINT', function() { os.exit(1) }) @@ -206,7 +202,7 @@ console.assert = function(op, str = `assertion failed [value '${op}']`) { if (!op) console.panic(str) } -os.on('uncaught_exception', function(e) { console.error(e) }) +//os.on('uncaught_exception', function(e) { console.log("HERRE!"); console.error(e); }) console[prosperon.DOC] = { doc: "The console object provides various logging, debugging, and output methods.", @@ -727,7 +723,7 @@ $_.start = function(cb, prg, arg) { if (prg) argv = argv.concat(['--program', prg]) if (arg) argv = argv.concat(cmd.encode(arg)) underlings.add(id) - os.createthread(argv) + os.createactor(argv) } $_.start[prosperon.DOC] = "The start function creates a new actor..." @@ -758,12 +754,9 @@ $_.unneeded = function(fn, seconds = ar) { } $_.unneeded[prosperon.DOC] = "registers a function that is called when the actor..." -var timers = [] -var timer_id = 0 - $_.delay = function(fn, seconds) { - var id = os.os.addtimer(prosperon.id, fn, seconds); - return function() { os.removetimer(id); } + var id = os.addtimer(prosperon.id, fn, seconds); + return function() { os.removetimer(prosperon.id, id); } } $_.delay[prosperon.DOC] = "used to schedule the invocation of a function..." @@ -843,6 +836,8 @@ cmd.process(prosperon.argv) if (!prosperon.args.id) prosperon.id = util.guid() else prosperon.id = prosperon.args.id +os.register_actor(prosperon.id, handle_local); + tracy.thread_name(prosperon.id) $_.__ACTORDATA__.id = prosperon.id @@ -851,13 +846,9 @@ if (prosperon.args.overling) overling = { __ACTORDATA__: {id: prosperon.args.ove if (prosperon.args.root) root = json.decode(prosperon.args.root) else root = $_ -os.mailbox_start(prosperon.id) - if (overling) actor_send(overling, {type:'greet', id: prosperon.id}) if (prosperon.args.program) actor.spawn(prosperon.args.program) -var unneeded_timer = $_.delay($_.stop, ar) - function destroyself() { console.log(`Got the message to destroy self.`) if (overling) actor_send(overling, { type: "stopped", id: prosperon.id }) @@ -876,10 +867,12 @@ function handle_actor_disconnect(id) { } function handle_local(msg) { + console.log("LOCAL MESSAGE"); handle_message(wota.decode(msg)) } function handle_message(msg) { + console.log(`message was ${json.encode(msg)}`) if (msg.target) { if (msg.target !== prosperon.id) { os.mailbox_push(msg.target, wota.encode(msg)) @@ -932,24 +925,6 @@ function handle_message(msg) { unneeded_timer = $_.delay(unneeded_fn, unneeded_time) } -var hang = 0.1 -var last_t = os.now() - -var doexit = false -while (!doexit) { - if (portal) portal.service(handle_host, hang) - if (contactor) contactor.service(handle_host, hang) - os.mailbox_service(prosperon.id, handle_local, hang) - var elapsed = os.now() - last_t - last_t = os.now() - for (var i in timers) { - var t = timers[i] - t.seconds -= elapsed - if (t.seconds <= 0) { - t.fn() - delete timers[i] - } - } -} +var unneeded_timer = $_.delay($_.stop, ar) })() diff --git a/source/jsffi.c b/source/jsffi.c index 942cc2a5..2844c839 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -23,10 +23,10 @@ #include "par/par_shapes.h" #include #include "timer.h" -#include #include "cute_aseprite.h" #include "cgltf.h" #include "physfs.h" +#include "prosperon.h" #include "qjs_dmon.h" #include "qjs_nota.h" @@ -2483,9 +2483,6 @@ JSC_CCALL(os_engine_start, JS_SDL_PROP(js, p, SDL_PROP_APP_METADATA_URL_STRING, url) JS_SDL_PROP(js, p, SDL_PROP_APP_METADATA_TYPE_STRING, type) - if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA)) - return JS_ThrowReferenceError(js, "Couldn't initialize SDL: %s\n", SDL_GetError()); - const char *title; JS_GETPROP(js,title,argv[0],title,cstring) @@ -6505,20 +6502,56 @@ JSC_CCALL(os_value_id, return number2js(js,(intptr_t)JS_VALUE_GET_PTR(self)); ) -static double gc_t = 0; -static double gc_mem = 0; -static double gc_startmem = 0; -void script_report_gc_time(double t, double startmem, double mem) -{ - gc_t = t; - gc_mem = mem; - gc_startmem = startmem; -} +JSC_SSCALL(os_eval, + ret = JS_Eval(js, str2, strlen(str2), str, JS_EVAL_FLAG_STRICT); +) -JSC_SSCALL(os_eval, ret = script_eval(js,str, str2)) JSC_CCALL(os_make_timer, return timer2js(js,timer_make(js,argv[0]))) JSC_CCALL(os_update_timers, timer_update(js, js2number(js,argv[0]))) +Uint32 timer_cb(prosperon_rt *actor, SDL_TimerID id, Uint32 interval) +{ + int idx = hmgeti(actor->timers, id); + if (idx == -1) return 0; + + SDL_LockMutex(actor->mutex); + JSValue cb = actor->timers[idx].value; + hmdel(actor->timers, id); + arrput(actor->events, cb); + SDL_UnlockMutex(actor->mutex); + + actor_signal(actor); +} + +JSC_CCALL(os_addtimer, + char *guid = JS_ToCString(js,argv[0]); + prosperon_rt *actor = get_actor(guid); + JS_FreeCString(js, guid); + if (!actor) return JS_ThrowInternalError(js, "Could not get actor from guid."); + + Uint64 ns = js2number(js,argv[2]) * 1000000000.0f; + Uint32 id = SDL_AddTimerNS(ns, timer_cb, actor); + + // appropriately add it to the actor + SDL_LockMutex(actor->mutex); + hmput(actor->timers, id, JS_DupValue(js,argv[1])); + SDL_UnlockMutex(actor->mutex); +) + +JSC_CCALL(os_removetimer, + char *guid = JS_ToCString(js,argv[0]); + prosperon_rt *actor = get_actor(guid); + JS_FreeCString(js, guid); + if (!actor) return JS_ThrowInternalError(js, "Could not get actor from guid."); + + Uint32 timer_id = js2number(js,argv[1]); + SDL_RemoveTimer(timer_id); + + SDL_LockMutex(actor->mutex); + hmdel(actor->timers, timer_id); + SDL_UnlockMutex(actor->mutex); +) + // input: (encoded image data of jpg, png, bmp, tiff) JSC_CCALL(os_make_texture, size_t len; @@ -6956,8 +6989,9 @@ JSC_CCALL(os_rects_to_sprites, ) JSC_CCALL(os_on, - JSValue *onexp = SDL_GetTLS(&on_exception); - *onexp = JS_DupValue(js,argv[1]); + prosperon_rt *rt = JS_GetContextOpaque(js); + JS_FreeValue(js, rt->on_exception); + rt->on_exception = JS_DupValue(js,argv[1]); ) JSC_CCALL(os_clean_transforms, @@ -7086,83 +7120,30 @@ JSC_CCALL(os_createprocess, return JS_ThrowReferenceError(js, "Unable to create process: %s\n", SDL_GetError()); ) -typedef struct { - int argc; - char **argv; -} cmdargs; +JSC_CCALL(os_createactor, + int margc = JS_ArrayLength(js, argv[0]); -int create_new_runtime(cmdargs *data) -{ - script_startup(data->argc, data->argv); - for (int i = 0; i < data->argc; i++) free(data->argv[i]); - free(data->argv); - free(data); - return 0; -} + char **margv = malloc(margc*sizeof(char*)); -JSC_CCALL(os_createthread, - cmdargs *cmd = malloc(sizeof(cmdargs)); - if (!cmd) return JS_ThrowInternalError(js, "Out of memory"); - cmd->argc = JS_ArrayLength(js, argv[0]); - cmd->argv = (char **)calloc(cmd->argc, sizeof(char *)); - if (!cmd->argv) { - free(cmd); - return JS_ThrowInternalError(js, "Out of memory"); - } - for (int i = 0; i < cmd->argc; i++) { + for (int i = 0; i < margc; i++) { JSValue val = JS_GetPropertyUint32(js, argv[0], i); - if (JS_IsException(val)) { - for (int j = 0; j < i; j++) free(cmd->argv[j]); - free(cmd->argv); - free(cmd); - return val; - } - const char *str = JS_ToCString(js, val); - if (!str) { - for (int j = 0; j < i; j++) free(cmd->argv[j]); - free(cmd->argv); - free(cmd); - JS_FreeValue(js, val); - return JS_ThrowInternalError(js, "Could not convert argument to string"); - } - cmd->argv[i] = strdup(str); - JS_FreeCString(js, str); - JS_FreeValue(js, val); + char *cstr = JS_ToCString(js,val); + margv[i] = strdup(cstr); + JS_FreeCString(js,cstr); + JS_FreeValue(js,val); } - SDL_Thread *thread = SDL_CreateThread(create_new_runtime, "newrt", cmd); - if (!thread) { - for (int i = 0; i < cmd->argc; i++) free(cmd->argv[i]); - free(cmd->argv); - free(cmd); - return JS_ThrowInternalError(js, "Could not create a new thread: %s", SDL_GetError()); - } - SDL_DetachThread(thread); - return JS_UNDEFINED; + + create_actor(margc, margv); ) -struct message { - void *data; - size_t size; -}; - -typedef struct mailbox { - SDL_Mutex *mutex; - SDL_Condition *cond; - struct message *messages; -} mailbox; - -static struct { char *key; mailbox value; } *mailboxes = NULL; -static SDL_Mutex *mailboxes_mutex = NULL; - JSC_CCALL(os_mailbox_push, if (argc < 2) return JS_ThrowInternalError(js, "Need an actor and a message"); if (!JS_IsArrayBuffer(js, argv[1])) return JS_ThrowInternalError(js, "Object to push must be an array buffer"); const char *id = JS_ToCString(js, argv[0]); - int mailbox_index = shgeti(mailboxes, id); - JS_FreeCString(js, id); - - if (mailbox_index == -1) return JS_ThrowInternalError(js, "No mailbox found for given ID"); + prosperon_rt *target = get_actor(id); + if (!target) + return JS_ThrowInternalError(js, "No mailbox found for given ID"); size_t size; uint8_t *buf = JS_GetArrayBuffer(js, &size, argv[1]); @@ -7173,116 +7154,41 @@ JSC_CCALL(os_mailbox_push, memcpy(data, buf, size); - mailbox *mb = &mailboxes[mailbox_index].value; - SDL_LockMutex(mb->mutex); struct message msg = {data, size}; - arrput(mb->messages, msg); - SDL_SignalCondition(mb->cond); // Signal waiting threads - SDL_UnlockMutex(mb->mutex); + send_message(target, msg); ) -void js_dofree(JSRuntime *rt, void *opaque, void *ptr) -{ - js_free_rt(rt, ptr); -} - -JSC_CCALL(os_mailbox_service, - if (!JS_IsFunction(js, argv[1])) return JS_ThrowInternalError(js, "Arg 2 must be a function"); - if (argc < 3 || !JS_IsNumber(argv[2])) return JS_ThrowInternalError(js, "Arg 3 must be a number (timeout in seconds)"); - - const char *id = JS_ToCString(js, argv[0]); - int mb_index = shgeti(mailboxes, id); - JS_FreeCString(js, id); - - if (mb_index == -1) return JS_ThrowInternalError(js, "No mailbox found for given ID"); - - // Get timeout in seconds and convert to milliseconds - int32_t timeout_seconds; - if (JS_ToInt32(js, &timeout_seconds, argv[2]) != 0) return JS_ThrowInternalError(js, "Invalid timeout value"); - int timeout_ms = timeout_seconds * 1000; // Convert to milliseconds for SDL - - mailbox *mb = &mailboxes[mb_index].value; - struct message *temp = NULL; - SDL_LockMutex(mb->mutex); - - // Wait for messages if the mailbox is empty - while (arrlen(mb->messages) == 0) { - bool signaled = SDL_WaitConditionTimeout(mb->cond, mb->mutex, timeout_ms); - if (!signaled) { // Timeout occurred - SDL_UnlockMutex(mb->mutex); - return JS_UNDEFINED; - } - } - - // Process messages after wait - int count = arrlen(mb->messages); - if (count > 0) { - arrsetlen(temp, count); - memcpy(temp, mb->messages, sizeof(*mb->messages) * count); - arrsetlen(mb->messages, 0); - } - SDL_UnlockMutex(mb->mutex); - - if (count == 0) return JS_UNDEFINED; - - JSValue fn = JS_DupValue(js, argv[1]); - - for (int i = 0; i < count; i++) { - JSValue arg = JS_NewArrayBuffer(js, temp[i].data, temp[i].size, js_dofree, NULL, 0); - JSValue call = JS_Call(js, fn, JS_UNDEFINED, 1, &arg); - uncaught_exception(js, call); - JS_FreeValue(js, arg); - } - - arrfree(temp); - JS_FreeValue(js, fn); +JSC_CCALL(os_register_actor, + printf("calling registera ctor for context %p\n", js); + prosperon_rt *rt = JS_GetContextOpaque(js); + printf("context after get_opaque: %p\n", js); + char *id = JS_ToCString(js, argv[0]); + printf("context after get_id: %p\n", js); + char *err = register_actor(id, rt); + printf("context after register_actor: %p\n", js); + if (err) return JS_ThrowInternalError(js, "Could not register actor: %s", err); + rt->message_handle = JS_DupValue(js, argv[1]); + printf("context after dup_value: %p\n", js); + const char *fnstr = JS_ToCString(js, rt->message_handle); + printf("context after get_fnstr: %p\n", js); + printf("registered %s\n", fnstr); + JS_FreeCString(js, fnstr); + rt->context = js; + printf("jsvalue is in context %p\n", js); + int tag = JS_VALUE_GET_TAG(rt->message_handle); + printf("message_handle tag: %d\n", tag); + if (tag == 0) printf(" as int: %d\n", JS_VALUE_GET_INT(rt->message_handle)); + else printf(" as ptr: %p\n", JS_VALUE_GET_PTR(rt->message_handle)); + ) JSC_CCALL(os_mailbox_exist, const char *id = JS_ToCString(js, argv[0]); - bool exists = shgeti(mailboxes, id) != -1; - JS_FreeCString(js, id); - return JS_NewBool(js, exists); -) - -JSC_CCALL(os_mailbox_start, - const char *id = JS_ToCString(js, argv[0]); - mailbox mb; - mb.mutex = SDL_CreateMutex(); - mb.cond = SDL_CreateCondition(); - mb.messages = NULL; - if (!mailboxes_mutex) mailboxes_mutex = SDL_CreateMutex(); - - SDL_LockMutex(mailboxes_mutex); - shput(mailboxes, id, mb); - SDL_UnlockMutex(mailboxes_mutex); - JS_FreeCString(js, id); -) - -JSC_CCALL(os_mailbox_destroy, - if (argc < 1) return JS_ThrowInternalError(js, "Need an actor/mailbox ID to destroy"); - - const char *id = JS_ToCString(js, argv[0]); - int mailbox_index = shgeti(mailboxes, id); + prosperon_rt *actor = get_actor(id); + printf("actor from %s is %p\n", id, actor); JS_FreeCString(js, id); - if (mailbox_index != -1) { - mailbox *mb = &mailboxes[mailbox_index].value; - SDL_LockMutex(mb->mutex); - - int count = arrlen(mb->messages); - for (int i = 0; i < count; i++) - js_free_rt(JS_GetRuntime(js), mb->messages[i].data); - arrfree(mb->messages); - SDL_UnlockMutex(mb->mutex); - - SDL_DestroyMutex(mb->mutex); - SDL_DestroyCondition(mb->cond); - - SDL_LockMutex(mailboxes_mutex); - shdel(mailboxes, id); - SDL_UnlockMutex(mailboxes_mutex); - } + return JS_NewBool(js, actor ? 1 : 0); ) JSC_CCALL(os_waitevent, @@ -7317,50 +7223,10 @@ JSValue js_os_set_trace(JSContext *js, JSValue self, JSValue value) return JS_UNDEFINED; } -typedef struct { - char *timer_id; // Unique timer identifier - char *mailbox_id; // Mailbox to send the message to -} timer_data; - -typedef struct { - char *timer_id; // Timer ID - JSValue fn; // Function to invoke -} timer_mapping; - -static timer_mapping *timers = NULL; // Dynamic array of timer mappings -static SDL_Mutex *timers_mutex = NULL; // Mutex for thread-safe access to timers - -void init_timers(void) -{ - if (!timers_mutex) timers_mutex = SDL_CreateMutex(); -} - -typedef struct { - JSValue fn; - const char *id; -} timer_cb; - -JSC_CCALL(os_addtimer, - - Uint64 ns = js2number(js,argv[1])*1000000000.0; - timer_cb cb; - cb.fn = JS_DupValue(js,argv[0]); - cb.id = JS_ToCString(js,argv[2]); - - -) - -JSC_CCALL(os_removetimer, - -) - static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, make_transform, 0), MIST_FUNC_DEF(os, clean_transforms, 0), - MIST_FUNC_DEF(os, addtimer, 3), - MIST_FUNC_DEF(os, removetimer, 1), - MIST_FUNC_DEF(os, platform, 0), MIST_FUNC_DEF(os, arch, 0), MIST_FUNC_DEF(os, totalmem, 0), @@ -7394,14 +7260,14 @@ static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, env, 1), MIST_FUNC_DEF(os, system, 1), MIST_FUNC_DEF(os, createprocess, 0), + MIST_FUNC_DEF(os, createactor, 1), MIST_FUNC_DEF(os, waitevent, 2), MIST_FUNC_DEF(os, mailbox_push, 2), - MIST_FUNC_DEF(os, mailbox_service, 2), - MIST_FUNC_DEF(os, mailbox_start, 1), MIST_FUNC_DEF(os, mailbox_exist, 1), - MIST_FUNC_DEF(os, mailbox_destroy, 1), - MIST_FUNC_DEF(os, createthread, 1), + MIST_FUNC_DEF(os, addtimer, 3), + MIST_FUNC_DEF(os, removetimer, 2), + MIST_FUNC_DEF(os, register_actor, 2), }; JSC_CCALL(js_dump_class, return js_get_object_class_distribution(js)) @@ -7750,41 +7616,6 @@ JSValue js_miniz_use(JSContext *js); JSValue js_tracy_use(JSContext *js); #endif -static void signal_handler(int sig) { - const char *str = NULL; - switch(sig) { - case SIGABRT: - str = "SIGABRT"; - break; - case SIGFPE: - str = "SIGFPE"; - break; - case SIGILL: - str = "SIGILL"; - break; - case SIGINT: - str = "SIGINT"; - break; - case SIGSEGV: - str = "SIGSEGV"; - break; - case SIGTERM: - str = "SIGTERM"; - break; - } - if (!str) return; - - JSContext *js = SDL_GetTLS(&js_id); - - script_evalf(js, "prosperon.dispatch('%s')", str); -} - -static void exit_handler() -{ -// JSContext *js = SDL_GetTLS(&js_id); -// script_evalf(js, "prosperon.dispatch('exit')"); -} - #include "monocypher.h" // randombytes.c - Minimal cross-platform CSPRNG shim (single file) @@ -8019,9 +7850,10 @@ JSValue js_imgui_use(JSContext *js); #define MISTLINE(NAME) (ModuleEntry){#NAME, js_##NAME##_use} -void ffi_load(JSContext *js, int argc, char **argv) { - prosperon_rt *rt = calloc(1,sizeof(*rt)); - JS_SetContextOpaque(js, rt); +void ffi_load(JSContext *js) +{ + prosperon_rt *rt = JS_GetContextOpaque(js); + m_seedRand(&rt->mrand, time(NULL)); arrput(rt->module_registry, MISTLINE(io)); @@ -8114,15 +7946,9 @@ void ffi_load(JSContext *js, int argc, char **argv) { JS_FreeValue(js,jsnumber); JS_FreeValue(js,number_proto); - signal(SIGINT, signal_handler); - signal(SIGTERM, signal_handler); - signal(SIGSEGV, signal_handler); - signal(SIGABRT, signal_handler); - atexit(exit_handler); - JSValue args = JS_NewArray(js); - for (int i = 0; i < argc; i++) - JS_SetPropertyUint32(js,args, i, JS_NewString(js,argv[i])); + for (int i = 0; i < rt->cmd.argc; i++) + JS_SetPropertyUint32(js,args, i, JS_NewString(js,rt->cmd.argv[i])); JS_SetPropertyStr(js,prosp,"argv", args); JS_SetPropertyStr(js,prosp, "version", JS_NewString(js,PROSPERON_VERSION)); @@ -8130,6 +7956,4 @@ void ffi_load(JSContext *js, int argc, char **argv) { JS_SetPropertyStr(js,prosp,"engine_start", JS_NewCFunction(js,js_os_engine_start, "engine_start", 1)); JS_FreeValue(js,globalThis); - - SDL_Init(SDL_INIT_EVENTS); } diff --git a/source/jsffi.h b/source/jsffi.h index d8524404..a6f9b51b 100644 --- a/source/jsffi.h +++ b/source/jsffi.h @@ -1,7 +1,8 @@ #ifndef FFI_H #define FFI_H + #include -void ffi_load(JSContext *js, int argc, char **argv); +void ffi_load(JSContext *js); int js_print_exception(JSContext *js, JSValue v); #endif diff --git a/source/prosperon.c b/source/prosperon.c index 1836b097..257b4726 100644 --- a/source/prosperon.c +++ b/source/prosperon.c @@ -4,10 +4,26 @@ #include #include #include +#include + +#include "stb_ds.h" + +#include "prosperon.h" +#include "jsffi.h" + +static prosperon_rt **ready_queue = NULL; +static prosperon_rt **slow_queue = NULL; // Slow actors +static prosperon_rt **reclaim_queue = NULL; // Actors needing reclamation +static SDL_Mutex *queue_mutex = NULL; // Protects queue access +static SDL_Condition *queue_cond = NULL; // Signals when work is available + +static struct { char *key; prosperon_rt *value } *actors; static unsigned char *zip_buffer_global; static char *prosperon; +SDL_ThreadID main_thread = 0; + void free_zip(void) { free(zip_buffer_global); @@ -92,7 +108,179 @@ if (!ret) { return 1; } +void free_actor(prosperon_rt *actor) +{ + for (int i = 0; i < actor->cmd.argc; i++) free(actor->cmd.argv[i]); + script_stop(actor->context); + JSRuntime *rt = JS_GetRuntime(actor->context); + arrfree(actor->messages); + SDL_DestroyMutex(actor->mutex); + arrfree(actor->js_swapchains); + arrfree(actor->module_registry); + free(actor); +} + +// this function should be called to spin off a new thread with actor +prosperon_rt *create_actor(int argc, char **argv) +{ + prosperon_rt *rt = calloc(sizeof(*rt),1); + rt->cmd.argc = argc; + rt->cmd.argv = argv; + rt->cycle_fn = JS_UNDEFINED; + rt->idx_buffer = JS_UNDEFINED; + + script_startup(rt); + + return rt; +} + +void js_dofree(JSRuntime *rt, void *opaque, void *ptr) +{ + js_free_rt(rt, ptr); +} + +void run_actor_turn(prosperon_rt *actor) +{ + SDL_LockMutex(actor->mutex); + if (actor->state != ACTOR_READY) goto END; + if (arrlen(actor->messages) == 0) { + actor->state = ACTOR_IDLE; + goto END; + } + struct message msg = actor->messages[0]; + arrdel(actor->messages, 0); + JSValue arg = JS_NewArrayBuffer(actor->context, msg.data, msg.size, js_dofree, NULL, 0); + printf("running actor %p, fn? %d. context %p\n", actor, JS_IsFunction(actor->context, actor->message_handle), actor->context); + int tag = JS_VALUE_GET_TAG(actor->message_handle); + printf("message_handle tag: %d, value: %llu\n", tag, actor->message_handle); + if (tag >= 0) printf(" as ptr: %p\n", JS_VALUE_GET_PTR(actor->message_handle)); + JSValue fstr = JS_ToString(actor->context, actor->message_handle); + const char *fnstr = JS_ToCString(actor->context, fstr); + printf("function is %s\n", fnstr ? fnstr : "(null)"); + JS_FreeCString(actor->context, fnstr); + JSValue result = JS_Call(actor->context, actor->message_handle, JS_UNDEFINED, 1, &arg); + printf("exception? %d\n", JS_IsException(result)); + if (JS_IsException(result)) { + JSValue exception = JS_GetException(actor->context); + const char *err_str = JS_ToCString(actor->context, exception); + printf("exception details: %s\n", err_str ? err_str : "(null)"); + JS_FreeCString(actor->context, err_str); + JS_FreeValue(actor->context, exception); + } + uncaught_exception(actor->context, result); + printf("ran actor %p\n", actor); +END: + SDL_UnlockMutex(actor->mutex); +} + +int reclaimer_thread(void *data) +{ + while (true) { + SDL_LockMutex(queue_mutex); + while (arrlen(reclaim_queue) == 0) + SDL_WaitCondition(queue_cond, queue_mutex); + + prosperon_rt *actor = reclaim_queue[0]; + arrdel(reclaim_queue, 0); + SDL_UnlockMutex(queue_mutex); + + SDL_LockMutex(actor->mutex); + if (actor->state == ACTOR_EXHAUSTED) { + actor->state = ACTOR_RECLAIMING; + JS_RunGC(JS_GetRuntime(actor->context)); + } + SDL_UnlockMutex(actor->mutex); + + actor_signal(actor); + } +} + +int crank_actor() +{ + while (true) { + prosperon_rt *actor = NULL; + SDL_LockMutex(queue_mutex); + if (arrlen(ready_queue) > 0) { + actor = ready_queue[0]; + arrdel(ready_queue, 0); + } else if (arrlen(slow_queue) > 0) { + actor = slow_queue[0]; + arrdel(slow_queue, 0); + } else { + SDL_WaitCondition(queue_cond, queue_mutex); + continue; + } + + SDL_UnlockMutex(queue_mutex); + if (actor) + run_actor_turn(actor); + } + + return 0; +} + +static void signal_handler(int sig) { + const char *str = NULL; + switch(sig) { + case SIGABRT: + str = "SIGABRT"; + break; + case SIGFPE: + str = "SIGFPE"; + break; + case SIGILL: + str = "SIGILL"; + break; + case SIGINT: + str = "SIGINT"; + break; + case SIGSEGV: + str = "SIGSEGV"; + break; + case SIGTERM: + str = "SIGTERM"; + break; + } + if (!str) return; + + for (int i = 0; i < shlen(actors); i++) { + prosperon_rt *main = actors[i].value; + JSContext *js = main->context; + script_evalf(js, "prosperon.dispatch('%s')", str); + } + + switch(sig) { + case SIGTERM: + exit(1); + case SIGINT: + exit(1); + } +} + +static void exit_handler() +{ + for (int i = 0; i < shlen(actors); i++) { + prosperon_rt *main = actors[i].value; + JSContext *js = main->context; + script_evalf(js, "prosperon.dispatch('exit')", NULL); + } +} + int main(int argc, char **argv) { + SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO); + int cores = SDL_GetNumLogicalCPUCores(); + if (cores == 1) { + printf("Platform has only one core!\n"); + return 1; + } + + main_thread = SDL_GetCurrentThreadID(); + + if (!main_thread) { + printf("Platform does not support threads!\n"); + return 1; + } + prosperon = argv[0]; PHYSFS_init(argv[0]); @@ -107,7 +295,74 @@ int main(int argc, char **argv) { printf("Could not mount core. Reason: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); return 1; } + + char **margv = malloc(sizeof(char*)); + for (int i = 0; i < argc; i++) + margv[i] = strdup(argv[i]); + + create_actor(argc, margv); + + queue_mutex = SDL_CreateMutex(); + queue_cond = SDL_CreateCondition(); + + for (int i = 0; i < 1; i++) + SDL_CreateThread(crank_actor, "actor_runner", NULL); + + SDL_CreateThread(reclaimer_thread, "reclaimer", NULL); + + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + signal(SIGSEGV, signal_handler); + signal(SIGABRT, signal_handler); + atexit(exit_handler); + + SDL_Event *event; + while (SDL_WaitEvent(event)) { + printf("Got event!\n"); + if (event->type == SDL_EVENT_QUIT) + return 0; + } - script_startup(argc, argv); // runs engine.js return 0; } + +prosperon_rt *get_actor(char *id) +{ + int idx = shgeti(actors, id); + if (idx == -1) return NULL; + + return actors[idx].value; +} + +char *register_actor(char *id, prosperon_rt *actor) +{ + if (shgeti(actors, id) != -1) return "Actor with given ID already exists."; + shput(actors, id, actor); + printf("registerd actor %s as %p\n", id, actor); + return NULL; +} + +void send_message(prosperon_rt *target, struct message msg) +{ + printf("sending message to actor %p\n", target); + SDL_LockMutex(target->mutex); + arrput(target->messages, msg); + SDL_UnlockMutex(target->mutex); + + actor_signal(target); +} + +void actor_signal(prosperon_rt *actor) +{ + printf("waking actor %p\n", actor); + SDL_LockMutex(actor->mutex); + if (actor->state != ACTOR_READY) { + actor->state = ACTOR_READY; + SDL_LockMutex(queue_mutex); + arrput(ready_queue, actor); + SDL_SignalCondition(queue_cond); + SDL_UnlockMutex(queue_mutex); + } + + SDL_UnlockMutex(actor->mutex); +} diff --git a/source/prosperon.h b/source/prosperon.h new file mode 100644 index 00000000..ae7c6710 --- /dev/null +++ b/source/prosperon.h @@ -0,0 +1,67 @@ +#ifndef PROSPERON_H +#define PROSPERON_H + +#include +#include "quickjs.h" + +typedef JSValue (*MODULEFN)(JSContext *js); + +#define STATE_VECTOR_LENGTH 624 +#define STATE_VECTOR_M 397 /* changes to STATE_VECTOR_LENGTH also require changes to this */ + +typedef struct tagMTRand { + uint32_t mt[STATE_VECTOR_LENGTH]; + int32_t index; +} MTRand; + +extern SDL_ThreadID main_thread; + +extern SDL_TLSID prosperon_id; + +typedef struct { + const char *name; + MODULEFN fn; +} ModuleEntry; + +typedef struct { + int argc; + char **argv; +} cmdargs; + +struct message { + void *data; + size_t size; +}; + +#define ACTOR_IDLE 0 +#define ACTOR_READY 1 +#define ACTOR_RUNNING 2 +#define ACTOR_EXHAUSTED 3 // flagged when the actor is running GC +#define ACTOR_RECLAIMING 4 +#define ACTOR_SLOW 5 + +typedef struct prosperon_rt { + cmdargs cmd; // args this actor was started with + JSContext *context; // The context this actor uses + MTRand mrand; + JSValue cycle_fn; + JSValue idx_buffer; + JSValue on_exception; + JSValue message_handle; + int idx_count; + ModuleEntry *module_registry; + JSValue *js_swapchains; + struct { Uint32 key; JSValue value; } *timers; + JSValue *events; // stack of events that need triggered on this actor + SDL_Mutex *mutex; // blocks access to modifying this actor + struct message *messages; + int state; // +} prosperon_rt; + +prosperon_rt *get_actor(char *id); +char *register_actor(char *id, prosperon_rt *actor); +prosperon_rt *create_actor(); +void actor_signal(prosperon_rt *actor); +void actor_turn(prosperon_rt *actor); +void send_message(prosperon_rt *actor, struct message msg); +#endif diff --git a/source/script.c b/source/script.c index 17877b4a..5c71a890 100644 --- a/source/script.c +++ b/source/script.c @@ -11,6 +11,8 @@ #include #include "quickjs.h" +#include "prosperon.h" + #if defined(__APPLE__) #include #elif defined(_WIN32) @@ -22,13 +24,8 @@ #include "physfs.h" -SDL_TLSID on_exception = {0}; -SDL_TLSID js_id = {0}; - #define ENGINE "scripts/core/engine.js" -#define JS_EVAL_FLAGS JS_EVAL_FLAG_STRICT - #ifdef TRACY_ENABLE #include @@ -135,7 +132,8 @@ static const JSMallocFunctions tracy_malloc_funcs = { #endif -void script_startup(int argc, char **argv) { +// this function takes control of the program. +void script_startup(prosperon_rt *prt) { JSRuntime *rt; #ifdef TRACY_ENABLE rt = JS_NewRuntime2(&tracy_malloc_funcs, NULL); @@ -143,7 +141,6 @@ void script_startup(int argc, char **argv) { rt = JS_NewRuntime(); #endif JSContext *js = JS_NewContextRaw(rt); - SDL_SetTLS(&js_id, js, NULL); JS_AddIntrinsicBaseObjects(js); JS_AddIntrinsicEval(js); JS_AddIntrinsicRegExp(js); @@ -156,11 +153,11 @@ void script_startup(int argc, char **argv) { JS_AddIntrinsicOperators(js); JS_EnableBignumExt(js, 1); - JSValue *onexp = malloc(sizeof(JSValue)); - *onexp = JS_UNDEFINED; - SDL_SetTLS(&on_exception, onexp, NULL); + JS_SetContextOpaque(js, prt); + prt->context = js; + printf("started actor %p with context %p\n", prt, prt->context); - ffi_load(js, argc, argv); + ffi_load(js); PHYSFS_File *eng = PHYSFS_openRead(ENGINE); if (!eng) { @@ -173,9 +170,10 @@ void script_startup(int argc, char **argv) { void *data = malloc(stat.filesize); PHYSFS_readBytes(eng,data,stat.filesize); PHYSFS_close(eng); - JSValue v = script_eval(js, ENGINE, data); + SDL_LockMutex(prt->mutex); + JSValue v = JS_Eval(js, data, strlen(data), ENGINE, JS_EVAL_FLAG_STRICT); uncaught_exception(js,v); - script_stop(js); + SDL_UnlockMutex(prt->mutex); } void script_stop(JSContext *js) @@ -188,13 +186,9 @@ void script_stop(JSContext *js) free(prt); - JSValue *onexp = SDL_GetTLS(&on_exception); - JS_FreeValue(js,*onexp); JSRuntime *rt = JS_GetRuntime(js); JS_FreeContext(js); JS_FreeRuntime(rt); - - free(onexp); } void uncaught_exception(JSContext *js, JSValue v) @@ -204,11 +198,13 @@ void uncaught_exception(JSContext *js, JSValue v) return; } - JSValue *onexp = SDL_GetTLS(&on_exception); + prosperon_rt *rt = JS_GetContextOpaque(js); + + JSValue onexp = rt->on_exception; - if (!JS_IsUndefined(*onexp)) { + if (!JS_IsUndefined(onexp)) { JSValue ex = JS_GetException(js); - JSValue ret = JS_Call(js, *onexp, JS_UNDEFINED, 1, &ex); + JSValue ret = JS_Call(js, onexp, JS_UNDEFINED, 1, &ex); JS_FreeValue(js,ret); JS_FreeValue(js,ex); } else { @@ -239,13 +235,8 @@ void script_evalf(JSContext *js, const char *format, ...) vsnprintf(eval, len+1, format, args); va_end(args); - obj = JS_Eval(js, eval, len, "C eval", JS_EVAL_FLAGS); + obj = JS_Eval(js, eval, len, "C eval", JS_EVAL_FLAG_STRICT); free(eval); uncaught_exception(js,obj); } - -JSValue script_eval(JSContext *js, const char *file, const char *script) -{ - return JS_Eval(js, script, strlen(script), file, JS_EVAL_FLAGS); -} diff --git a/source/script.h b/source/script.h index c72eec87..f482ff55 100644 --- a/source/script.h +++ b/source/script.h @@ -3,35 +3,9 @@ #include "quickjs.h" #include +#include "prosperon.h" -typedef JSValue (*MODULEFN)(JSContext *js); - -typedef struct { - const char *name; - MODULEFN fn; -} ModuleEntry; - -#define STATE_VECTOR_LENGTH 624 -#define STATE_VECTOR_M 397 /* changes to STATE_VECTOR_LENGTH also require changes to this */ - -typedef struct tagMTRand { - uint32_t mt[STATE_VECTOR_LENGTH]; - int32_t index; -} MTRand; - -typedef struct prosperon_rt { - MTRand mrand; - JSValue cycle_fn; - JSValue idx_buffer; - int idx_count; - ModuleEntry *module_registry; - JSValue *js_swapchains; -} prosperon_rt; - -extern SDL_TLSID on_exception; -extern SDL_TLSID js_id; - -void script_startup(int argc, char **argv); +void script_startup(prosperon_rt *rt); void script_stop(JSContext*); void script_evalf(JSContext *js, const char *format, ...);