massively simplify loop logic
This commit is contained in:
@@ -10,4 +10,4 @@ actor_max = "10_000"
|
||||
stack_max = 0
|
||||
[actors]
|
||||
[actors.prosperon/sdl_video]
|
||||
main = true
|
||||
main = true
|
||||
|
||||
@@ -12,6 +12,7 @@ $_.start(e => {
|
||||
video = e.actor
|
||||
graphics = use('graphics', video)
|
||||
send(video, {kind:"window", op:"makeRenderer"}, e => {
|
||||
log.console("MADE A WINDOW! so now renderer")
|
||||
$_.start(e => {
|
||||
if (gameactor) return
|
||||
gameactor = e.actor
|
||||
@@ -24,9 +25,6 @@ $_.start(e => {
|
||||
height:500
|
||||
})
|
||||
|
||||
var input = use('input')
|
||||
|
||||
input.watch($_)
|
||||
|
||||
var geometry = use('geometry')
|
||||
|
||||
|
||||
@@ -200,6 +200,7 @@ function handle_window(msg) {
|
||||
return {success: true};
|
||||
|
||||
case 'makeRenderer':
|
||||
log.console("MAKE RENDERER")
|
||||
if (ren)
|
||||
return {reason: "Already made a renderer"}
|
||||
ren = win.make_renderer()
|
||||
|
||||
@@ -86,8 +86,6 @@ var nota = hidden.nota
|
||||
// Strip hidden from cell so nothing else can access it
|
||||
delete cell.hidden
|
||||
|
||||
var os = use_embed('os')
|
||||
|
||||
function disrupt(err)
|
||||
{
|
||||
if (overling) {
|
||||
@@ -97,10 +95,12 @@ function disrupt(err)
|
||||
|
||||
log.error(err)
|
||||
|
||||
log.console("DISRUPTING")
|
||||
|
||||
actor_mod.disrupt()
|
||||
}
|
||||
|
||||
os.on(disrupt)
|
||||
actor_mod.on_exception(disrupt)
|
||||
|
||||
var js = use_embed('js')
|
||||
|
||||
@@ -688,7 +688,7 @@ function destroyself() {
|
||||
|
||||
if (overling) report_to_overling({type:'stop'})
|
||||
|
||||
actor_mod.destroy()
|
||||
actor_mod.disrupt()
|
||||
}
|
||||
|
||||
function handle_actor_disconnect(id) {
|
||||
|
||||
@@ -7,12 +7,10 @@ os.freemem[cell.DOC] = "Return the amount of free system RAM in bytes, if known.
|
||||
os.hostname[cell.DOC] = "Return the system's hostname, or an empty string if not available."
|
||||
os.version[cell.DOC] = "Return the OS or kernel version string, if the platform provides it."
|
||||
|
||||
os.exit[cell.DOC] = "Exit the application with the specified exit code."
|
||||
os.now[cell.DOC] = "Return current time (in seconds as a float) with high resolution."
|
||||
|
||||
os.power_state[cell.DOC] = "Return a string describing power status: 'on battery', 'charging', 'charged', etc."
|
||||
|
||||
os.on[cell.DOC] = "Register a global callback for certain engine-wide or system-level events."
|
||||
os.rusage[cell.DOC] = "Return resource usage stats for this process, if the platform supports it."
|
||||
os.mallinfo[cell.DOC] = "Return detailed memory allocation info (arena size, free blocks, etc.) on some platforms."
|
||||
|
||||
|
||||
386
source/cell.c
386
source/cell.c
@@ -45,7 +45,7 @@ int tracy_profiling_enabled = 0;
|
||||
|
||||
static cell_rt **ready_queue = NULL;
|
||||
static cell_rt **main_ready_queue = NULL;
|
||||
static SDL_Mutex *queue_mutex = NULL;
|
||||
static SDL_Mutex *queue_mutex = NULL; // for the ready queue
|
||||
static SDL_Condition *queue_cond = NULL;
|
||||
static SDL_Mutex *actors_mutex = NULL;
|
||||
static struct { char *key; cell_rt *value; } *actors = NULL;
|
||||
@@ -56,106 +56,117 @@ cell_rt *root_cell = NULL;
|
||||
static SDL_AtomicInt engine_shutdown;
|
||||
static SDL_Thread **runners = NULL;
|
||||
|
||||
// TIMERS
|
||||
struct timer_entry { uint64_t when_ns; cell_rt *actor; uint32_t id; };
|
||||
static struct timer_entry *timers = NULL;
|
||||
static uint32_t timer_counter = 1;
|
||||
static SDL_Mutex *timer_mutex = NULL; // protects access to timers
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// TIMER “SDL_AddTimerNS”–replacement
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
static inline uint64_t now_ns()
|
||||
{
|
||||
return SDL_GetTicksNS();
|
||||
typedef Uint32 (*TimerCallback)(Uint32 timer_id, Uint32 interval, void *param);
|
||||
|
||||
typedef struct {
|
||||
Uint32 id;
|
||||
uint64_t due_ns;
|
||||
uint64_t interval_ns;
|
||||
TimerCallback callback;
|
||||
void *param;
|
||||
} timer_t;
|
||||
|
||||
// stb_ds array of timer_t
|
||||
static timer_t *timers = NULL;
|
||||
static Uint32 next_timer_id = 1;
|
||||
|
||||
// Must be initialized exactly once (e.g. in main after SDL_Init)
|
||||
static SDL_Mutex *timer_mutex = NULL;
|
||||
static SDL_Condition *timer_cond = NULL;
|
||||
|
||||
// Return monotonic time in nanoseconds
|
||||
static uint64_t get_time_ns(void) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint64_t)ts.tv_sec * 1000000000ull + ts.tv_nsec;
|
||||
}
|
||||
|
||||
// Add a message into each actor's event queue for expired timers
|
||||
static void process_due_timers(void)
|
||||
{
|
||||
uint64_t tnow = now_ns();
|
||||
// Schedule a new timer to fire after `delay_ns` nanoseconds.
|
||||
// Returns a Uint32 timer ID. When it fires, `callback(interval_ms,param)` is invoked.
|
||||
// If `callback` returns nonzero (in ms), the timer is rescheduled with that
|
||||
// new interval; otherwise it is removed permanently.
|
||||
Uint32 add_timer_ns(uint64_t delay_ns, TimerCallback callback, void *param) {
|
||||
timer_t t;
|
||||
t.id = next_timer_id++;
|
||||
t.interval_ns = delay_ns;
|
||||
t.due_ns = get_time_ns() + delay_ns;
|
||||
t.callback = callback;
|
||||
t.param = param;
|
||||
SDL_LockMutex(timer_mutex);
|
||||
while (arrlen(timers) && timers[0].when_ns <= tnow) {
|
||||
struct timer_entry te = timers[0];
|
||||
arrdel(timers, 0);
|
||||
SDL_UnlockMutex(timer_mutex);
|
||||
|
||||
/* Marshal callback into actor mailbox. */
|
||||
SDL_LockMutex(te.actor->msg_mutex);
|
||||
int idx = hmgeti(te.actor->timers, te.id);
|
||||
if (idx != -1) {
|
||||
JSValue cb = te.actor->timers[idx].value; /* already duped when stored */
|
||||
hmdel(te.actor->timers, te.id);
|
||||
|
||||
letter l;
|
||||
l.type = LETTER_CALLBACK;
|
||||
l.callback = cb;
|
||||
arrput(te.actor->letters, l);
|
||||
}
|
||||
SDL_UnlockMutex(te.actor->msg_mutex);
|
||||
set_actor_state(te.actor);
|
||||
|
||||
SDL_LockMutex(timer_mutex);
|
||||
}
|
||||
arrput(timers, t);
|
||||
SDL_SignalCondition(timer_cond);
|
||||
SDL_UnlockMutex(timer_mutex);
|
||||
return t.id;
|
||||
}
|
||||
|
||||
static uint64_t next_timeout_ns(void)
|
||||
{
|
||||
// Cancel (remove) a pending timer by its ID. If not found, does nothing.
|
||||
void remove_timer(Uint32 id) {
|
||||
SDL_LockMutex(timer_mutex);
|
||||
uint64_t timeout = arrlen(timers) ? (timers[0].when_ns - now_ns()) : UINT64_MAX;
|
||||
SDL_UnlockMutex(timer_mutex);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static void timer_insert(struct timer_entry te)
|
||||
{
|
||||
SDL_LockMutex(timer_mutex);
|
||||
size_t n = arrlen(timers);
|
||||
arrput(timers, te);
|
||||
for (size_t i = n; i > 0 && timers[i].when_ns < timers[i-1].when_ns; --i) {
|
||||
struct timer_entry tmp = timers[i];
|
||||
timers[i] = timers[i-1];
|
||||
timers[i-1] = tmp;
|
||||
}
|
||||
SDL_UnlockMutex(timer_mutex);
|
||||
}
|
||||
|
||||
/* Public API used by JS glue (replaces SDL_AddTimer). */
|
||||
uint32_t scheduler_add_timer(cell_rt *actor, double seconds, JSValue cb)
|
||||
{
|
||||
uint32_t id = SDL_AtomicIncRef((SDL_AtomicInt*)&timer_counter);
|
||||
uint64_t deadline = now_ns() + (uint64_t)(seconds * 1e9);
|
||||
|
||||
SDL_LockMutex(timer_mutex);
|
||||
timer_insert((struct timer_entry){ deadline, actor, id });
|
||||
SDL_UnlockMutex(timer_mutex);
|
||||
|
||||
/* Stash in actor map so removal/GC works. Duplicated to hold ref. */
|
||||
SDL_LockMutex(actor->msg_mutex);
|
||||
hmput(actor->timers, id, JS_DupValue(actor->context, cb));
|
||||
SDL_UnlockMutex(actor->msg_mutex);
|
||||
|
||||
/* Wake a sleeper if this timer is sooner than the current next. */
|
||||
SDL_SignalCondition(queue_cond);
|
||||
return id;
|
||||
}
|
||||
|
||||
void scheduler_remove_timer(cell_rt *actor, uint32_t id)
|
||||
{
|
||||
SDL_LockMutex(timer_mutex);
|
||||
for (int i = 0; i < arrlen(timers); ++i) {
|
||||
for (int i = 0; i < arrlen(timers); i++) {
|
||||
if (timers[i].id == id) {
|
||||
arrdel(timers, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(timer_mutex);
|
||||
}
|
||||
|
||||
SDL_LockMutex(actor->msg_mutex);
|
||||
int idx = hmgeti(actor->timers, id);
|
||||
if (idx != -1) {
|
||||
JS_FreeValue(actor->context, actor->timers[idx].value);
|
||||
hmdel(actor->timers, id);
|
||||
// Scan the global `timers[]` array and invoke any timer whose due_ns <= now.
|
||||
// For each such timer:
|
||||
// 1) pop it out of the `timers` array
|
||||
// 2) call `callback(interval_ms,param)`
|
||||
// 3) if callback returns nonzero "next_ms", re-insert it with new due_ns = now + next_ms*1e6
|
||||
// This must be called once per iteration of your main loop (before polling).
|
||||
void process_due_timers(void) {
|
||||
uint64_t now = get_time_ns();
|
||||
SDL_LockMutex(timer_mutex);
|
||||
for (int i = 0; i < arrlen(timers); i++) {
|
||||
if (timers[i].due_ns <= now) {
|
||||
timer_t t = timers[i];
|
||||
arrdel(timers, i);
|
||||
SDL_UnlockMutex(timer_mutex);
|
||||
|
||||
// Convert interval_ns back to milliseconds for the callback
|
||||
Uint32 next_ms = t.callback(t.id, t.interval_ns, t.param);
|
||||
if (next_ms > 0) {
|
||||
uint64_t next_ns = (uint64_t)next_ms * 1000000ull;
|
||||
add_timer_ns(next_ns, t.callback, t.param);
|
||||
}
|
||||
|
||||
// restart scan because array may have shifted
|
||||
SDL_LockMutex(timer_mutex);
|
||||
i = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(actor->msg_mutex);
|
||||
SDL_UnlockMutex(timer_mutex);
|
||||
}
|
||||
|
||||
uint64_t next_timeout_ns(void) {
|
||||
SDL_LockMutex(timer_mutex);
|
||||
if (!timers || arrlen(timers) == 0) {
|
||||
SDL_UnlockMutex(timer_mutex);
|
||||
return UINT64_MAX;
|
||||
}
|
||||
uint64_t now = get_time_ns();
|
||||
uint64_t min_due = timers[0].due_ns;
|
||||
for (int i = 1; i < arrlen(timers); i++) {
|
||||
if (timers[i].due_ns < min_due)
|
||||
min_due = timers[i].due_ns;
|
||||
}
|
||||
SDL_UnlockMutex(timer_mutex);
|
||||
if (min_due <= now)
|
||||
return 0;
|
||||
return min_due - now;
|
||||
}
|
||||
|
||||
static inline uint64_t now_ns()
|
||||
{
|
||||
return SDL_GetTicksNS();
|
||||
}
|
||||
|
||||
static void exit_handler(void)
|
||||
@@ -196,7 +207,6 @@ void actor_free(cell_rt *actor)
|
||||
SDL_UnlockMutex(queue_mutex);
|
||||
|
||||
// Do not go forward with actor destruction until the actor is completely free
|
||||
SDL_LockMutex(actor->mutex);
|
||||
SDL_LockMutex(actor->msg_mutex);
|
||||
SDL_LockMutex(actor->mutex);
|
||||
|
||||
@@ -208,19 +218,10 @@ void actor_free(cell_rt *actor)
|
||||
JS_FreeValue(js, actor->unneeded);
|
||||
JS_FreeAtom(js, actor->actor_sym);
|
||||
|
||||
SDL_RemoveTimer(actor->ar);
|
||||
remove_timer(actor->ar);
|
||||
for (int i = 0; i < arrlen(actor->js_swapchains); i++)
|
||||
JS_FreeValue(js, actor->js_swapchains[i]);
|
||||
|
||||
/* Remove all timers for this actor from the global timer queue */
|
||||
SDL_LockMutex(timer_mutex);
|
||||
for (int i = arrlen(timers) - 1; i >= 0; i--) {
|
||||
if (timers[i].actor == actor) {
|
||||
arrdel(timers, i);
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(timer_mutex);
|
||||
|
||||
/* Free timer callbacks stored in actor */
|
||||
for (int i = 0; i < hmlen(actor->timers); i++) {
|
||||
JS_FreeValue(js, actor->timers[i].value);
|
||||
@@ -251,25 +252,19 @@ void actor_free(cell_rt *actor)
|
||||
SDL_DestroyMutex(actor->mutex);
|
||||
SDL_UnlockMutex(actor->msg_mutex);
|
||||
SDL_DestroyMutex(actor->msg_mutex);
|
||||
SDL_UnlockMutex(actor->mutex);
|
||||
SDL_DestroyMutex(actor->mutex);
|
||||
|
||||
free(actor);
|
||||
|
||||
if (actor == root_cell)
|
||||
SDL_LockMutex(actors_mutex);
|
||||
if (shlen(actors) == 0)
|
||||
exit(0);
|
||||
// exit_handler();
|
||||
SDL_UnlockMutex(actors_mutex);
|
||||
}
|
||||
|
||||
JSValue js_actor_remove_cb(JSContext *js, JSValue this_val, int argc, JSValue *argv)
|
||||
{
|
||||
cell_rt *actor = JS_GetContextOpaque(js);
|
||||
|
||||
if (actor->need_stop) {
|
||||
actor_free(actor);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
if (JS_IsUndefined(actor->unneeded))
|
||||
actor_free(actor);
|
||||
else {
|
||||
@@ -497,7 +492,7 @@ const char *send_message(const char *id, void *msg)
|
||||
SDL_LockMutex(target->msg_mutex);
|
||||
arrput(target->letters, l);
|
||||
if (target->ar) {
|
||||
SDL_RemoveTimer(target->ar);
|
||||
remove_timer(target->ar);
|
||||
target->ar = 0;
|
||||
}
|
||||
SDL_UnlockMutex(target->msg_mutex);
|
||||
@@ -507,58 +502,69 @@ const char *send_message(const char *id, void *msg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
set_actor_state should check if any letters are pending.
|
||||
|
||||
*/
|
||||
void set_actor_state(cell_rt *actor)
|
||||
static Uint32 actor_remove_cb(Uint32 id, Uint32 interval, cell_rt *actor)
|
||||
{
|
||||
if (!JS_IsUndefined(actor->unneeded)) {
|
||||
SDL_LockMutex(actor->mutex);
|
||||
JSValue ret = JS_Call(actor->context, actor->unneeded, JS_UNDEFINED, 0, NULL);
|
||||
uncaught_exception(actor->context, ret);
|
||||
SDL_UnlockMutex(actor->mutex);
|
||||
}
|
||||
actor_free(actor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Timer callback adds an event to the queue under evt_mutex. */
|
||||
Uint32 actor_delay_cb(SDL_TimerID id, Uint32 interval, cell_rt *actor)
|
||||
{
|
||||
SDL_LockMutex(actor->msg_mutex);
|
||||
if (actor->need_stop) {
|
||||
if (!actor->ar) {
|
||||
JSValue remove_cb = JS_NewCFunction(actor->context, js_actor_remove_cb, "actor_remove", 0);
|
||||
actor->ar = scheduler_add_timer(actor, 0, remove_cb);
|
||||
JS_FreeValue(actor->context, remove_cb);
|
||||
}
|
||||
SDL_UnlockMutex(actor->msg_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
int has_letters = arrlen(actor->letters);
|
||||
int has_upcoming = hmlen(actor->timers);
|
||||
|
||||
if (actor->state == ACTOR_RUNNING)
|
||||
actor->state = ACTOR_IDLE;
|
||||
int idx = hmgeti(actor->timers, id);
|
||||
if (idx == -1) goto END;
|
||||
|
||||
switch(actor->state) {
|
||||
case ACTOR_IDLE:
|
||||
if (has_letters) {
|
||||
actor->state = ACTOR_READY;
|
||||
SDL_LockMutex(queue_mutex);
|
||||
if (actor->main_thread_only) {
|
||||
arrput(main_ready_queue, actor);
|
||||
} else {
|
||||
SDL_SignalCondition(queue_cond);
|
||||
arrput(ready_queue, actor);
|
||||
}
|
||||
SDL_UnlockMutex(queue_mutex);
|
||||
goto END;
|
||||
}
|
||||
break;
|
||||
|
||||
case ACTOR_READY:
|
||||
if (!has_letters) {
|
||||
actor->state = ACTOR_IDLE;
|
||||
goto END;
|
||||
}
|
||||
break;
|
||||
}
|
||||
JSValue cb = actor->timers[idx].value;
|
||||
hmdel(actor->timers, id);
|
||||
letter l ={0};
|
||||
l.type = LETTER_CALLBACK;
|
||||
l.callback = cb;
|
||||
arrput(actor->letters, l);
|
||||
set_actor_state(actor);
|
||||
|
||||
END:
|
||||
if (actor->state == ACTOR_IDLE && !actor->ar && !has_upcoming) {
|
||||
JSValue remove_cb = JS_NewCFunction(actor->context, js_actor_remove_cb, "actor_remove", 0);
|
||||
actor->ar = scheduler_add_timer(actor, actor->ar_secs, remove_cb);
|
||||
JS_FreeValue(actor->context, remove_cb);
|
||||
SDL_UnlockMutex(actor->msg_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_actor_state(cell_rt *actor)
|
||||
{
|
||||
if (actor->disrupt) {
|
||||
actor_free(actor);
|
||||
return;
|
||||
}
|
||||
SDL_LockMutex(actor->msg_mutex);
|
||||
|
||||
switch(actor->state) {
|
||||
case ACTOR_RUNNING:
|
||||
case ACTOR_READY:
|
||||
if (actor->ar) {
|
||||
remove_timer(actor->ar);
|
||||
actor->ar = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case ACTOR_IDLE:
|
||||
if (arrlen(actor->letters)) {
|
||||
actor->state = ACTOR_READY;
|
||||
SDL_LockMutex(queue_mutex);
|
||||
if (actor->main_thread_only)
|
||||
arrput(main_ready_queue, actor);
|
||||
else
|
||||
arrput(ready_queue, actor);
|
||||
SDL_SignalCondition(queue_cond);
|
||||
SDL_UnlockMutex(queue_mutex);
|
||||
|
||||
} else if (!arrlen(actor->letters) && !hmlen(actor->timers))
|
||||
actor->ar = add_timer_ns(actor->ar_secs*1e9, actor_remove_cb, actor);
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(actor->msg_mutex);
|
||||
@@ -594,6 +600,9 @@ void actor_turn(cell_rt *actor)
|
||||
JS_FreeValue(actor->context, l.callback);
|
||||
}
|
||||
|
||||
// now idle
|
||||
actor->state = ACTOR_IDLE;
|
||||
|
||||
SDL_UnlockMutex(actor->mutex);
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
@@ -604,7 +613,6 @@ void actor_turn(cell_rt *actor)
|
||||
set_actor_state(actor);
|
||||
}
|
||||
|
||||
|
||||
/* JS function that schedules a timer. */
|
||||
JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
@@ -623,13 +631,11 @@ JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
SDL_UnlockMutex(actor->msg_mutex);
|
||||
return JS_NewInt32(js, -1);
|
||||
}
|
||||
uint32_t id = scheduler_add_timer(actor, seconds, argv[0]);
|
||||
uint32_t id = add_timer_ns(seconds*1e9, actor_delay_cb, actor);
|
||||
|
||||
SDL_LockMutex(actor->msg_mutex);
|
||||
if (actor->ar) {
|
||||
SDL_RemoveTimer(actor->ar);
|
||||
actor->ar = 0;
|
||||
}
|
||||
JSValue cb = JS_DupValue(js, argv[0]);
|
||||
hmput(actor->timers, id, cb);
|
||||
SDL_UnlockMutex(actor->msg_mutex);
|
||||
|
||||
return JS_NewUint32(js, id);
|
||||
@@ -640,9 +646,21 @@ JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *arg
|
||||
cell_rt *actor = JS_GetContextOpaque(js);
|
||||
uint32_t timer_id;
|
||||
JS_ToUint32(js, &timer_id, argv[0]);
|
||||
if (timer_id == (uint32_t)-1) return JS_UNDEFINED;
|
||||
if (timer_id == -1) return JS_UNDEFINED;
|
||||
|
||||
scheduler_remove_timer(actor, timer_id);
|
||||
remove_timer(timer_id);
|
||||
|
||||
JSValue cb = JS_UNDEFINED;
|
||||
|
||||
SDL_LockMutex(actor->msg_mutex);
|
||||
int id = hmgeti(actor->timers, timer_id);
|
||||
if (id != -1) {
|
||||
cb = actor->timers[id].value;
|
||||
hmdel(actor->timers, timer_id);
|
||||
}
|
||||
SDL_UnlockMutex(actor->msg_mutex);
|
||||
|
||||
JS_FreeValue(js,cb);
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
@@ -706,14 +724,11 @@ void tracy_end_hook(JSContext *js, JSValue fn)
|
||||
void actor_disrupt(cell_rt *crt)
|
||||
{
|
||||
crt->disrupt = 1;
|
||||
crt->need_stop = 1;
|
||||
}
|
||||
|
||||
static int actor_interrupt_cb(JSRuntime *rt, cell_rt *crt)
|
||||
{
|
||||
if (crt->disrupt)
|
||||
return 1;
|
||||
return 0;
|
||||
return crt->disrupt;
|
||||
}
|
||||
|
||||
void script_startup(cell_rt *prt)
|
||||
@@ -745,9 +760,6 @@ void script_startup(cell_rt *prt)
|
||||
JS_AddIntrinsicJSON(js);
|
||||
JS_AddIntrinsicMapSet(js);
|
||||
JS_AddIntrinsicProxy(js);
|
||||
// JS_AddIntrinsicTypedArrays(js);
|
||||
// JS_AddIntrinsicDate(js);
|
||||
// JS_AddIntrinsicPromise(js);
|
||||
|
||||
JS_SetContextOpaque(js, prt);
|
||||
prt->context = js;
|
||||
@@ -765,7 +777,6 @@ void script_startup(cell_rt *prt)
|
||||
PHYSFS_close(eng);
|
||||
data[stat.filesize] = 0;
|
||||
|
||||
/* Called with actor->mutex locked by create_actor(). */
|
||||
JSValue v = JS_Eval(js, data, (size_t)stat.filesize, ENGINE, JS_EVAL_FLAG_STRICT);
|
||||
uncaught_exception(js, v);
|
||||
}
|
||||
@@ -790,29 +801,7 @@ int uncaught_exception(JSContext *js, JSValue v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void script_evalf(JSContext *js, const char *format, ...)
|
||||
{
|
||||
cell_rt *rt = JS_GetContextOpaque(js);
|
||||
SDL_LockMutex(rt->mutex);
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int len = vsnprintf(NULL, 0, format, args);
|
||||
va_end(args);
|
||||
|
||||
char *eval = malloc(len + 1);
|
||||
va_start(args, format);
|
||||
vsnprintf(eval, len + 1, format, args);
|
||||
va_end(args);
|
||||
|
||||
JSValue obj = JS_Eval(js, eval, len, "C eval", JS_EVAL_FLAG_STRICT);
|
||||
free(eval);
|
||||
uncaught_exception(js, obj);
|
||||
|
||||
SDL_UnlockMutex(rt->mutex);
|
||||
}
|
||||
|
||||
static int crank_actor(void *data)
|
||||
static int actor_runner(void *data)
|
||||
{
|
||||
while (!SDL_GetAtomicInt(&engine_shutdown)) {
|
||||
SDL_LockMutex(queue_mutex);
|
||||
@@ -848,7 +837,6 @@ static void signal_handler(int sig)
|
||||
exit_handler();
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int profile_enabled = 0;
|
||||
@@ -926,6 +914,7 @@ int main(int argc, char **argv)
|
||||
queue_cond = SDL_CreateCondition();
|
||||
actors_mutex = SDL_CreateMutex();
|
||||
timer_mutex = SDL_CreateMutex();
|
||||
timer_cond = SDL_CreateCondition();
|
||||
|
||||
/* Create the initial actor from the command line */
|
||||
int actor_argc = argc - script_start;
|
||||
@@ -946,7 +935,7 @@ int main(int argc, char **argv)
|
||||
for (int i = 0; i < cores-1; i++) { // -1 to keep the main thread free
|
||||
char threadname[128];
|
||||
snprintf(threadname, sizeof(threadname), "actor runner %d", i);
|
||||
SDL_Thread *thread = SDL_CreateThread(crank_actor, threadname, NULL);
|
||||
SDL_Thread *thread = SDL_CreateThread(actor_runner, threadname, NULL);
|
||||
arrput(runners, thread);
|
||||
}
|
||||
|
||||
@@ -975,8 +964,10 @@ int main(int argc, char **argv)
|
||||
|
||||
uint64_t to_ns = next_timeout_ns();
|
||||
if (to_ns == UINT64_MAX) {
|
||||
// No timers pending, wait indefinitely
|
||||
pselect(0, NULL, NULL, NULL, NULL, NULL);
|
||||
SDL_LockMutex(timer_mutex);
|
||||
SDL_WaitCondition(timer_cond, timer_mutex);
|
||||
SDL_UnlockMutex(timer_mutex);
|
||||
continue;
|
||||
} else {
|
||||
struct timespec timeout = {
|
||||
.tv_sec = to_ns / 1000000000,
|
||||
@@ -1000,7 +991,7 @@ int actor_exists(const char *id)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*int JS_ArrayLength(JSContext *js, JSValue a)
|
||||
int JS_ArrayLength(JSContext *js, JSValue a)
|
||||
{
|
||||
JSValue length = JS_GetPropertyStr(js, a, "length");
|
||||
int len;
|
||||
@@ -1008,4 +999,3 @@ int actor_exists(const char *id)
|
||||
JS_FreeValue(js,length);
|
||||
return len;
|
||||
}
|
||||
*/
|
||||
@@ -58,7 +58,7 @@ typedef struct cell_rt {
|
||||
|
||||
/* Protects JSContext usage */
|
||||
SDL_Mutex *mutex; /* for access to the JSContext */
|
||||
SDL_Mutex *msg_mutex; /* For messages queue only */
|
||||
SDL_Mutex *msg_mutex; /* For message queue and timers */
|
||||
|
||||
char *id;
|
||||
MTRand mrand;
|
||||
@@ -68,7 +68,6 @@ typedef struct cell_rt {
|
||||
/* The “mailbox” for incoming messages + a dedicated lock for it: */
|
||||
struct letter *letters;
|
||||
|
||||
|
||||
/* CHANGED FOR EVENTS: a separate lock for the actor->events queue */
|
||||
struct { Uint32 key; JSValue value; } *timers;
|
||||
|
||||
@@ -77,7 +76,6 @@ typedef struct cell_rt {
|
||||
double ar_secs; // time for unneeded
|
||||
JSValue unneeded; // fn to call before unneeded
|
||||
|
||||
int need_stop;
|
||||
int disrupt;
|
||||
int main_thread_only;
|
||||
|
||||
@@ -99,8 +97,6 @@ Uint32 actor_timer_cb(cell_rt *actor, SDL_TimerID id, Uint32 interval);
|
||||
JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv);
|
||||
JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *argv);
|
||||
void script_startup(cell_rt *rt);
|
||||
void script_evalf(JSContext *js, const char *format, ...);
|
||||
JSValue script_eval(JSContext *js, const char *file, const char *script);
|
||||
int uncaught_exception(JSContext *js, JSValue v);
|
||||
int actor_exists(const char *id);
|
||||
cell_rt *get_actor(char *id);
|
||||
|
||||
@@ -139,12 +139,6 @@ JSC_CCALL(os_unneeded,
|
||||
SDL_UnlockMutex(actor->msg_mutex);
|
||||
)
|
||||
|
||||
JSC_CCALL(os_destroy,
|
||||
cell_rt *rt = JS_GetContextOpaque(js);
|
||||
rt->need_stop = 1;
|
||||
set_actor_state(rt);
|
||||
)
|
||||
|
||||
JSC_CCALL(actor_disrupt,
|
||||
cell_rt *crt = JS_GetContextOpaque(js);
|
||||
actor_disrupt(crt);
|
||||
@@ -160,6 +154,12 @@ JSC_CCALL(actor_testfn,
|
||||
printf("actor? %p\n", crt);
|
||||
)
|
||||
|
||||
JSC_CCALL(actor_on_exception,
|
||||
cell_rt *rt = JS_GetContextOpaque(js);
|
||||
JS_FreeValue(js, rt->on_exception);
|
||||
rt->on_exception = JS_DupValue(js,argv[0]);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_actor_funcs[] = {
|
||||
MIST_FUNC_DEF(os, createactor, 1),
|
||||
MIST_FUNC_DEF(os, mailbox_push, 2),
|
||||
@@ -168,10 +168,10 @@ static const JSCFunctionListEntry js_actor_funcs[] = {
|
||||
MIST_FUNC_DEF(actor, removetimer, 1),
|
||||
MIST_FUNC_DEF(os, register_actor, 4),
|
||||
MIST_FUNC_DEF(os, unneeded, 2),
|
||||
MIST_FUNC_DEF(os, destroy, 0),
|
||||
MIST_FUNC_DEF(actor, disrupt, 0),
|
||||
MIST_FUNC_DEF(actor, setname, 1),
|
||||
MIST_FUNC_DEF(actor, testfn, 1),
|
||||
MIST_FUNC_DEF(actor, on_exception, 1),
|
||||
};
|
||||
|
||||
JSValue js_actor_use(JSContext *js) {
|
||||
|
||||
@@ -7,11 +7,9 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include "wildmatch.h"
|
||||
|
||||
// File descriptor wrapper structure
|
||||
typedef struct {
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
JSC_CCALL(os_exit, exit(js2number(js,argv[0]));)
|
||||
JSC_CCALL(os_now, return number2js(js, (double)SDL_GetTicksNS()/1000000000.0))
|
||||
|
||||
JSC_CCALL(os_sleep,
|
||||
@@ -169,12 +168,6 @@ static JSValue js_os_version(JSContext *js, JSValue self, int argc, JSValue *arg
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSC_CCALL(os_on,
|
||||
cell_rt *rt = JS_GetContextOpaque(js);
|
||||
JS_FreeValue(js, rt->on_exception);
|
||||
rt->on_exception = JS_DupValue(js,argv[0]);
|
||||
)
|
||||
|
||||
JSC_CCALL(os_buffer2string,
|
||||
if (argc < 1) {
|
||||
return JS_ThrowTypeError(js, "buffer2string expects an ArrayBuffer");
|
||||
@@ -289,10 +282,8 @@ static const JSCFunctionListEntry js_os_funcs[] = {
|
||||
MIST_FUNC_DEF(os, freemem, 0),
|
||||
MIST_FUNC_DEF(os, hostname, 0),
|
||||
MIST_FUNC_DEF(os, version, 0),
|
||||
MIST_FUNC_DEF(os, exit, 1),
|
||||
MIST_FUNC_DEF(os, now, 0),
|
||||
MIST_FUNC_DEF(os, power_state, 0),
|
||||
MIST_FUNC_DEF(os, on, 1),
|
||||
MIST_FUNC_DEF(os, rusage, 0),
|
||||
MIST_FUNC_DEF(os, mallinfo, 0),
|
||||
MIST_FUNC_DEF(os, buffer2string, 1),
|
||||
|
||||
@@ -9,16 +9,6 @@
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
void input_system_init(void)
|
||||
{
|
||||
// No initialization needed for simplified system
|
||||
}
|
||||
|
||||
void input_system_cleanup(void)
|
||||
{
|
||||
// No cleanup needed for simplified system
|
||||
}
|
||||
|
||||
// Internal keymod function for input module
|
||||
static JSValue js_keymod(JSContext *js)
|
||||
{
|
||||
|
||||
@@ -2,14 +2,8 @@
|
||||
#define QJS_SDL_INPUT_H
|
||||
|
||||
#include <quickjs.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
// Initialization and cleanup functions
|
||||
void input_system_init(void);
|
||||
void input_system_cleanup(void);
|
||||
|
||||
// Event conversion functions
|
||||
const char* event_type_to_string(Uint32 event_type);
|
||||
const char* event_type_to_string(uint32_t event_type);
|
||||
const char* mouse_button_to_string(int mouse);
|
||||
|
||||
// JavaScript module entry point
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
var count = 0
|
||||
function loop()
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
var io = use('io')
|
||||
var shop = use('shop')
|
||||
var time = use('time')
|
||||
|
||||
log.console("=== Testing Module System ===")
|
||||
|
||||
@@ -26,7 +27,7 @@ log.console("✓ TOML parser working")
|
||||
|
||||
// Test 2: Shop initialization
|
||||
log.console("\n2. Testing shop initialization...")
|
||||
var test_dir = "module_test_" + Date.now()
|
||||
var test_dir = "module_test_" + time.number()
|
||||
io.mkdir(test_dir)
|
||||
var old_cwd = io.basedir()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user