actors in C now
Some checks failed
Build and Deploy / package-dist (push) Blocked by required conditions
Build and Deploy / deploy-itch (push) Blocked by required conditions
Build and Deploy / deploy-gitea (push) Blocked by required conditions
Build and Deploy / build-linux (push) Successful in 1m15s
Build and Deploy / build-windows (CLANG64) (push) Failing after 11m46s

This commit is contained in:
2025-03-18 18:18:56 -05:00
parent 813cc8dbbc
commit 8627fc52ef
7 changed files with 452 additions and 365 deletions

View File

@@ -31,10 +31,6 @@ if (os.trace)
var js = use_embed('js') var js = use_embed('js')
prosperon.on('exit', _ => {
os.mailbox_destroy(prosperon.id);
})
prosperon.on('SIGINT', function() { prosperon.on('SIGINT', function() {
os.exit(1) os.exit(1)
}) })
@@ -206,7 +202,7 @@ console.assert = function(op, str = `assertion failed [value '${op}']`) {
if (!op) console.panic(str) 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] = { console[prosperon.DOC] = {
doc: "The console object provides various logging, debugging, and output methods.", 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 (prg) argv = argv.concat(['--program', prg])
if (arg) argv = argv.concat(cmd.encode(arg)) if (arg) argv = argv.concat(cmd.encode(arg))
underlings.add(id) underlings.add(id)
os.createthread(argv) os.createactor(argv)
} }
$_.start[prosperon.DOC] = "The start function creates a new actor..." $_.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..." $_.unneeded[prosperon.DOC] = "registers a function that is called when the actor..."
var timers = []
var timer_id = 0
$_.delay = function(fn, seconds) { $_.delay = function(fn, seconds) {
var id = os.os.addtimer(prosperon.id, fn, seconds); var id = os.addtimer(prosperon.id, fn, seconds);
return function() { os.removetimer(id); } return function() { os.removetimer(prosperon.id, id); }
} }
$_.delay[prosperon.DOC] = "used to schedule the invocation of a function..." $_.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() if (!prosperon.args.id) prosperon.id = util.guid()
else prosperon.id = prosperon.args.id else prosperon.id = prosperon.args.id
os.register_actor(prosperon.id, handle_local);
tracy.thread_name(prosperon.id) tracy.thread_name(prosperon.id)
$_.__ACTORDATA__.id = 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) if (prosperon.args.root) root = json.decode(prosperon.args.root)
else root = $_ else root = $_
os.mailbox_start(prosperon.id)
if (overling) actor_send(overling, {type:'greet', id: prosperon.id}) if (overling) actor_send(overling, {type:'greet', id: prosperon.id})
if (prosperon.args.program) actor.spawn(prosperon.args.program) if (prosperon.args.program) actor.spawn(prosperon.args.program)
var unneeded_timer = $_.delay($_.stop, ar)
function destroyself() { function destroyself() {
console.log(`Got the message to destroy self.`) console.log(`Got the message to destroy self.`)
if (overling) actor_send(overling, { type: "stopped", id: prosperon.id }) if (overling) actor_send(overling, { type: "stopped", id: prosperon.id })
@@ -876,10 +867,12 @@ function handle_actor_disconnect(id) {
} }
function handle_local(msg) { function handle_local(msg) {
console.log("LOCAL MESSAGE");
handle_message(wota.decode(msg)) handle_message(wota.decode(msg))
} }
function handle_message(msg) { function handle_message(msg) {
console.log(`message was ${json.encode(msg)}`)
if (msg.target) { if (msg.target) {
if (msg.target !== prosperon.id) { if (msg.target !== prosperon.id) {
os.mailbox_push(msg.target, wota.encode(msg)) os.mailbox_push(msg.target, wota.encode(msg))
@@ -932,24 +925,6 @@ function handle_message(msg) {
unneeded_timer = $_.delay(unneeded_fn, unneeded_time) unneeded_timer = $_.delay(unneeded_fn, unneeded_time)
} }
var hang = 0.1 var unneeded_timer = $_.delay($_.stop, ar)
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]
}
}
}
})() })()

View File

@@ -23,10 +23,10 @@
#include "par/par_shapes.h" #include "par/par_shapes.h"
#include <stdint.h> #include <stdint.h>
#include "timer.h" #include "timer.h"
#include <signal.h>
#include "cute_aseprite.h" #include "cute_aseprite.h"
#include "cgltf.h" #include "cgltf.h"
#include "physfs.h" #include "physfs.h"
#include "prosperon.h"
#include "qjs_dmon.h" #include "qjs_dmon.h"
#include "qjs_nota.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_URL_STRING, url)
JS_SDL_PROP(js, p, SDL_PROP_APP_METADATA_TYPE_STRING, type) 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; const char *title;
JS_GETPROP(js,title,argv[0],title,cstring) 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)); return number2js(js,(intptr_t)JS_VALUE_GET_PTR(self));
) )
static double gc_t = 0; JSC_SSCALL(os_eval,
static double gc_mem = 0; ret = JS_Eval(js, str2, strlen(str2), str, JS_EVAL_FLAG_STRICT);
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 = script_eval(js,str, str2))
JSC_CCALL(os_make_timer, return timer2js(js,timer_make(js,argv[0]))) 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]))) 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) // input: (encoded image data of jpg, png, bmp, tiff)
JSC_CCALL(os_make_texture, JSC_CCALL(os_make_texture,
size_t len; size_t len;
@@ -6956,8 +6989,9 @@ JSC_CCALL(os_rects_to_sprites,
) )
JSC_CCALL(os_on, JSC_CCALL(os_on,
JSValue *onexp = SDL_GetTLS(&on_exception); prosperon_rt *rt = JS_GetContextOpaque(js);
*onexp = JS_DupValue(js,argv[1]); JS_FreeValue(js, rt->on_exception);
rt->on_exception = JS_DupValue(js,argv[1]);
) )
JSC_CCALL(os_clean_transforms, 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()); return JS_ThrowReferenceError(js, "Unable to create process: %s\n", SDL_GetError());
) )
typedef struct { JSC_CCALL(os_createactor,
int argc; int margc = JS_ArrayLength(js, argv[0]);
char **argv;
} cmdargs;
int create_new_runtime(cmdargs *data) char **margv = malloc(margc*sizeof(char*));
{
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;
}
JSC_CCALL(os_createthread, for (int i = 0; i < margc; i++) {
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++) {
JSValue val = JS_GetPropertyUint32(js, argv[0], i); JSValue val = JS_GetPropertyUint32(js, argv[0], i);
if (JS_IsException(val)) { char *cstr = JS_ToCString(js,val);
for (int j = 0; j < i; j++) free(cmd->argv[j]); margv[i] = strdup(cstr);
free(cmd->argv); JS_FreeCString(js,cstr);
free(cmd); JS_FreeValue(js,val);
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);
} }
SDL_Thread *thread = SDL_CreateThread(create_new_runtime, "newrt", cmd);
if (!thread) { create_actor(margc, margv);
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;
) )
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, JSC_CCALL(os_mailbox_push,
if (argc < 2) return JS_ThrowInternalError(js, "Need an actor and a message"); 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"); 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]); const char *id = JS_ToCString(js, argv[0]);
int mailbox_index = shgeti(mailboxes, id); prosperon_rt *target = get_actor(id);
JS_FreeCString(js, id); if (!target)
return JS_ThrowInternalError(js, "No mailbox found for given ID");
if (mailbox_index == -1) return JS_ThrowInternalError(js, "No mailbox found for given ID");
size_t size; size_t size;
uint8_t *buf = JS_GetArrayBuffer(js, &size, argv[1]); uint8_t *buf = JS_GetArrayBuffer(js, &size, argv[1]);
@@ -7173,116 +7154,41 @@ JSC_CCALL(os_mailbox_push,
memcpy(data, buf, size); memcpy(data, buf, size);
mailbox *mb = &mailboxes[mailbox_index].value;
SDL_LockMutex(mb->mutex);
struct message msg = {data, size}; struct message msg = {data, size};
arrput(mb->messages, msg); send_message(target, msg);
SDL_SignalCondition(mb->cond); // Signal waiting threads
SDL_UnlockMutex(mb->mutex);
) )
void js_dofree(JSRuntime *rt, void *opaque, void *ptr) JSC_CCALL(os_register_actor,
{ printf("calling registera ctor for context %p\n", js);
js_free_rt(rt, ptr); prosperon_rt *rt = JS_GetContextOpaque(js);
} printf("context after get_opaque: %p\n", js);
char *id = JS_ToCString(js, argv[0]);
JSC_CCALL(os_mailbox_service, printf("context after get_id: %p\n", js);
if (!JS_IsFunction(js, argv[1])) return JS_ThrowInternalError(js, "Arg 2 must be a function"); char *err = register_actor(id, rt);
if (argc < 3 || !JS_IsNumber(argv[2])) return JS_ThrowInternalError(js, "Arg 3 must be a number (timeout in seconds)"); printf("context after register_actor: %p\n", js);
if (err) return JS_ThrowInternalError(js, "Could not register actor: %s", err);
const char *id = JS_ToCString(js, argv[0]); rt->message_handle = JS_DupValue(js, argv[1]);
int mb_index = shgeti(mailboxes, id); printf("context after dup_value: %p\n", js);
JS_FreeCString(js, id); const char *fnstr = JS_ToCString(js, rt->message_handle);
printf("context after get_fnstr: %p\n", js);
if (mb_index == -1) return JS_ThrowInternalError(js, "No mailbox found for given ID"); printf("registered %s\n", fnstr);
JS_FreeCString(js, fnstr);
// Get timeout in seconds and convert to milliseconds rt->context = js;
int32_t timeout_seconds; printf("jsvalue is in context %p\n", js);
if (JS_ToInt32(js, &timeout_seconds, argv[2]) != 0) return JS_ThrowInternalError(js, "Invalid timeout value"); int tag = JS_VALUE_GET_TAG(rt->message_handle);
int timeout_ms = timeout_seconds * 1000; // Convert to milliseconds for SDL printf("message_handle tag: %d\n", tag);
if (tag == 0) printf(" as int: %d\n", JS_VALUE_GET_INT(rt->message_handle));
mailbox *mb = &mailboxes[mb_index].value; else printf(" as ptr: %p\n", JS_VALUE_GET_PTR(rt->message_handle));
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_mailbox_exist, JSC_CCALL(os_mailbox_exist,
const char *id = JS_ToCString(js, argv[0]); const char *id = JS_ToCString(js, argv[0]);
bool exists = shgeti(mailboxes, id) != -1; prosperon_rt *actor = get_actor(id);
JS_FreeCString(js, id); printf("actor from %s is %p\n", id, actor);
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);
JS_FreeCString(js, id); JS_FreeCString(js, id);
if (mailbox_index != -1) { return JS_NewBool(js, actor ? 1 : 0);
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);
}
) )
JSC_CCALL(os_waitevent, JSC_CCALL(os_waitevent,
@@ -7317,50 +7223,10 @@ JSValue js_os_set_trace(JSContext *js, JSValue self, JSValue value)
return JS_UNDEFINED; 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[] = { static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_transform, 0), MIST_FUNC_DEF(os, make_transform, 0),
MIST_FUNC_DEF(os, clean_transforms, 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, platform, 0),
MIST_FUNC_DEF(os, arch, 0), MIST_FUNC_DEF(os, arch, 0),
MIST_FUNC_DEF(os, totalmem, 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, env, 1),
MIST_FUNC_DEF(os, system, 1), MIST_FUNC_DEF(os, system, 1),
MIST_FUNC_DEF(os, createprocess, 0), MIST_FUNC_DEF(os, createprocess, 0),
MIST_FUNC_DEF(os, createactor, 1),
MIST_FUNC_DEF(os, waitevent, 2), MIST_FUNC_DEF(os, waitevent, 2),
MIST_FUNC_DEF(os, mailbox_push, 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_exist, 1),
MIST_FUNC_DEF(os, mailbox_destroy, 1), MIST_FUNC_DEF(os, addtimer, 3),
MIST_FUNC_DEF(os, createthread, 1), 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)) 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); JSValue js_tracy_use(JSContext *js);
#endif #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" #include "monocypher.h"
// randombytes.c - Minimal cross-platform CSPRNG shim (single file) // 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} #define MISTLINE(NAME) (ModuleEntry){#NAME, js_##NAME##_use}
void ffi_load(JSContext *js, int argc, char **argv) { void ffi_load(JSContext *js)
prosperon_rt *rt = calloc(1,sizeof(*rt)); {
JS_SetContextOpaque(js, rt); prosperon_rt *rt = JS_GetContextOpaque(js);
m_seedRand(&rt->mrand, time(NULL)); m_seedRand(&rt->mrand, time(NULL));
arrput(rt->module_registry, MISTLINE(io)); 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,jsnumber);
JS_FreeValue(js,number_proto); 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); JSValue args = JS_NewArray(js);
for (int i = 0; i < argc; i++) for (int i = 0; i < rt->cmd.argc; i++)
JS_SetPropertyUint32(js,args, i, JS_NewString(js,argv[i])); JS_SetPropertyUint32(js,args, i, JS_NewString(js,rt->cmd.argv[i]));
JS_SetPropertyStr(js,prosp,"argv", args); JS_SetPropertyStr(js,prosp,"argv", args);
JS_SetPropertyStr(js,prosp, "version", JS_NewString(js,PROSPERON_VERSION)); 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_SetPropertyStr(js,prosp,"engine_start", JS_NewCFunction(js,js_os_engine_start, "engine_start", 1));
JS_FreeValue(js,globalThis); JS_FreeValue(js,globalThis);
SDL_Init(SDL_INIT_EVENTS);
} }

View File

@@ -1,7 +1,8 @@
#ifndef FFI_H #ifndef FFI_H
#define FFI_H #define FFI_H
#include <quickjs.h> #include <quickjs.h>
void ffi_load(JSContext *js, int argc, char **argv); void ffi_load(JSContext *js);
int js_print_exception(JSContext *js, JSValue v); int js_print_exception(JSContext *js, JSValue v);
#endif #endif

View File

@@ -4,10 +4,26 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <signal.h>
#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 unsigned char *zip_buffer_global;
static char *prosperon; static char *prosperon;
SDL_ThreadID main_thread = 0;
void free_zip(void) void free_zip(void)
{ {
free(zip_buffer_global); free(zip_buffer_global);
@@ -92,7 +108,179 @@ if (!ret) {
return 1; 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) { 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]; prosperon = argv[0];
PHYSFS_init(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())); printf("Could not mount core. Reason: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return 1; 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; 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);
}

67
source/prosperon.h Normal file
View File

@@ -0,0 +1,67 @@
#ifndef PROSPERON_H
#define PROSPERON_H
#include <SDL3/SDL.h>
#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

View File

@@ -11,6 +11,8 @@
#include <assert.h> #include <assert.h>
#include "quickjs.h" #include "quickjs.h"
#include "prosperon.h"
#if defined(__APPLE__) #if defined(__APPLE__)
#include <malloc/malloc.h> #include <malloc/malloc.h>
#elif defined(_WIN32) #elif defined(_WIN32)
@@ -22,13 +24,8 @@
#include "physfs.h" #include "physfs.h"
SDL_TLSID on_exception = {0};
SDL_TLSID js_id = {0};
#define ENGINE "scripts/core/engine.js" #define ENGINE "scripts/core/engine.js"
#define JS_EVAL_FLAGS JS_EVAL_FLAG_STRICT
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
#include <tracy/TracyC.h> #include <tracy/TracyC.h>
@@ -135,7 +132,8 @@ static const JSMallocFunctions tracy_malloc_funcs = {
#endif #endif
void script_startup(int argc, char **argv) { // this function takes control of the program.
void script_startup(prosperon_rt *prt) {
JSRuntime *rt; JSRuntime *rt;
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
rt = JS_NewRuntime2(&tracy_malloc_funcs, NULL); rt = JS_NewRuntime2(&tracy_malloc_funcs, NULL);
@@ -143,7 +141,6 @@ void script_startup(int argc, char **argv) {
rt = JS_NewRuntime(); rt = JS_NewRuntime();
#endif #endif
JSContext *js = JS_NewContextRaw(rt); JSContext *js = JS_NewContextRaw(rt);
SDL_SetTLS(&js_id, js, NULL);
JS_AddIntrinsicBaseObjects(js); JS_AddIntrinsicBaseObjects(js);
JS_AddIntrinsicEval(js); JS_AddIntrinsicEval(js);
JS_AddIntrinsicRegExp(js); JS_AddIntrinsicRegExp(js);
@@ -156,11 +153,11 @@ void script_startup(int argc, char **argv) {
JS_AddIntrinsicOperators(js); JS_AddIntrinsicOperators(js);
JS_EnableBignumExt(js, 1); JS_EnableBignumExt(js, 1);
JSValue *onexp = malloc(sizeof(JSValue)); JS_SetContextOpaque(js, prt);
*onexp = JS_UNDEFINED; prt->context = js;
SDL_SetTLS(&on_exception, onexp, NULL); 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); PHYSFS_File *eng = PHYSFS_openRead(ENGINE);
if (!eng) { if (!eng) {
@@ -173,9 +170,10 @@ void script_startup(int argc, char **argv) {
void *data = malloc(stat.filesize); void *data = malloc(stat.filesize);
PHYSFS_readBytes(eng,data,stat.filesize); PHYSFS_readBytes(eng,data,stat.filesize);
PHYSFS_close(eng); 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); uncaught_exception(js,v);
script_stop(js); SDL_UnlockMutex(prt->mutex);
} }
void script_stop(JSContext *js) void script_stop(JSContext *js)
@@ -188,13 +186,9 @@ void script_stop(JSContext *js)
free(prt); free(prt);
JSValue *onexp = SDL_GetTLS(&on_exception);
JS_FreeValue(js,*onexp);
JSRuntime *rt = JS_GetRuntime(js); JSRuntime *rt = JS_GetRuntime(js);
JS_FreeContext(js); JS_FreeContext(js);
JS_FreeRuntime(rt); JS_FreeRuntime(rt);
free(onexp);
} }
void uncaught_exception(JSContext *js, JSValue v) void uncaught_exception(JSContext *js, JSValue v)
@@ -204,11 +198,13 @@ void uncaught_exception(JSContext *js, JSValue v)
return; 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 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,ret);
JS_FreeValue(js,ex); JS_FreeValue(js,ex);
} else { } else {
@@ -239,13 +235,8 @@ void script_evalf(JSContext *js, const char *format, ...)
vsnprintf(eval, len+1, format, args); vsnprintf(eval, len+1, format, args);
va_end(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); free(eval);
uncaught_exception(js,obj); 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);
}

View File

@@ -3,35 +3,9 @@
#include "quickjs.h" #include "quickjs.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include "prosperon.h"
typedef JSValue (*MODULEFN)(JSContext *js); void script_startup(prosperon_rt *rt);
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_stop(JSContext*); void script_stop(JSContext*);
void script_evalf(JSContext *js, const char *format, ...); void script_evalf(JSContext *js, const char *format, ...);