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')
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)
})()

View File

@@ -23,10 +23,10 @@
#include "par/par_shapes.h"
#include <stdint.h>
#include "timer.h"
#include <signal.h>
#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);
}

View File

@@ -1,7 +1,8 @@
#ifndef FFI_H
#define FFI_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);
#endif

View File

@@ -4,10 +4,26 @@
#include <stdlib.h>
#include <string.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 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);
}

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 "quickjs.h"
#include "prosperon.h"
#if defined(__APPLE__)
#include <malloc/malloc.h>
#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 <tracy/TracyC.h>
@@ -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);
}

View File

@@ -3,35 +3,9 @@
#include "quickjs.h"
#include <SDL3/SDL.h>
#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, ...);