flag used for actor stopping insetad of counter

This commit is contained in:
2026-02-17 17:59:12 -06:00
parent 5ee51198a7
commit b16fa75706
10 changed files with 115 additions and 109 deletions

View File

@@ -22,7 +22,9 @@ typedef struct actor_node {
typedef enum {
TIMER_JS,
TIMER_NATIVE_REMOVE
TIMER_NATIVE_REMOVE,
TIMER_PAUSE,
TIMER_KILL
} timer_type;
typedef struct {
@@ -30,6 +32,7 @@ typedef struct {
cell_rt *actor;
uint32_t timer_id;
timer_type type;
uint32_t turn_gen; /* generation at registration time */
} timer_node;
static timer_node *timer_heap = NULL;
@@ -122,7 +125,9 @@ uint32_t actor_remove_cb(cell_rt *actor, uint32_t id, uint32_t interval);
void actor_turn(cell_rt *actor);
void heap_push(uint64_t when, cell_rt *actor, uint32_t timer_id, timer_type type) {
timer_node node = { .execute_at_ns = when, .actor = actor, .timer_id = timer_id, .type = type };
timer_node node = { .execute_at_ns = when, .actor = actor, .timer_id = timer_id, .type = type, .turn_gen = 0 };
if (type == TIMER_PAUSE || type == TIMER_KILL)
node.turn_gen = atomic_load_explicit(&actor->turn_gen, memory_order_relaxed);
arrput(timer_heap, node);
// Bubble up
@@ -192,8 +197,19 @@ void *timer_thread_func(void *arg) {
if (t.type == TIMER_NATIVE_REMOVE) {
actor_remove_cb(t.actor, t.timer_id, 0);
} else if (t.type == TIMER_PAUSE || t.type == TIMER_KILL) {
/* Only fire if turn_gen still matches (stale timers are ignored) */
uint32_t cur = atomic_load_explicit(&t.actor->turn_gen, memory_order_relaxed);
if (cur == t.turn_gen) {
if (t.type == TIMER_PAUSE) {
JS_SetPauseFlag(t.actor->context, 1);
} else {
t.actor->disrupt = 1;
JS_SetPauseFlag(t.actor->context, 2);
}
}
} else {
pthread_mutex_lock(t.actor->msg_mutex);
pthread_mutex_lock(t.actor->msg_mutex);
int idx = hmgeti(t.actor->timers, t.timer_id);
if (idx != -1) {
JSValue cb = t.actor->timers[idx].value;
@@ -343,7 +359,6 @@ void actor_free(cell_rt *actor)
arrfree(actor->letters);
JS_SetInterruptHandler(js, NULL, NULL);
JS_FreeContext(js);
free(actor->id);
@@ -628,36 +643,6 @@ const char *register_actor(const char *id, cell_rt *actor, int mainthread, doubl
return NULL;
}
int actor_interrupt_cb(JSRuntime *rt, cell_rt *crt)
{
if (engine.shutting_down || crt->disrupt)
return -1;
if (crt->turn_deadline_ns == 0)
return 0; /* no timer set (e.g. during startup) */
uint64_t now = cell_ns();
if (now < crt->turn_deadline_ns)
return 0;
if (crt->is_slow_turn) {
#ifdef ACTOR_TRACE
fprintf(stderr, "[ACTOR_TRACE] %s: slow timer expired, killing\n",
crt->name ? crt->name : crt->id);
#endif
crt->disrupt = 1;
return -1;
}
/* Fast timer expired — check if we can suspend */
if (JS_GetVMCallDepth(crt->context) > 0) {
/* Can't suspend with C frames on stack, switch to slow timer */
crt->is_slow_turn = 1;
crt->turn_deadline_ns = now + ACTOR_SLOW_TIMER_NS;
return 0;
}
#ifdef ACTOR_TRACE
fprintf(stderr, "[ACTOR_TRACE] %s: fast timer expired, requesting suspend\n",
crt->name ? crt->name : crt->id);
#endif
return 1;
}
const char *send_message(const char *id, void *msg)
{
@@ -699,16 +684,23 @@ void actor_turn(cell_rt *actor)
JSValue result;
if (actor->vm_suspended) {
/* RESUME path: continue suspended turn under slow timer */
/* RESUME path: continue suspended turn under kill timer only */
atomic_fetch_add_explicit(&actor->turn_gen, 1, memory_order_relaxed);
JS_SetPauseFlag(actor->context, 0);
actor->turn_start_ns = cell_ns();
actor->turn_deadline_ns = actor->turn_start_ns + ACTOR_SLOW_TIMER_NS;
actor->is_slow_turn = 1;
/* Register kill timer only for resume */
pthread_mutex_lock(&engine.lock);
heap_push(actor->turn_start_ns + ACTOR_SLOW_TIMER_NS,
actor, 0, TIMER_KILL);
pthread_cond_signal(&engine.timer_cond);
pthread_mutex_unlock(&engine.lock);
result = JS_ResumeRegisterVM(actor->context);
actor->vm_suspended = 0;
if (JS_IsSuspended(result)) {
/* Still suspended after slow timer — shouldn't happen, handler returns -1 */
/* Still suspended after kill timer — shouldn't happen, kill it */
actor->disrupt = 1;
goto ENDTURN;
}
@@ -747,9 +739,18 @@ void actor_turn(cell_rt *actor)
arrdel(actor->letters, 0);
pthread_mutex_unlock(actor->msg_mutex);
atomic_fetch_add_explicit(&actor->turn_gen, 1, memory_order_relaxed);
JS_SetPauseFlag(actor->context, 0);
actor->turn_start_ns = cell_ns();
actor->turn_deadline_ns = actor->turn_start_ns + ACTOR_FAST_TIMER_NS;
actor->is_slow_turn = 0;
/* Register both pause and kill timers */
pthread_mutex_lock(&engine.lock);
heap_push(actor->turn_start_ns + ACTOR_FAST_TIMER_NS,
actor, 0, TIMER_PAUSE);
heap_push(actor->turn_start_ns + ACTOR_SLOW_TIMER_NS + ACTOR_FAST_TIMER_NS,
actor, 0, TIMER_KILL);
pthread_cond_signal(&engine.timer_cond);
pthread_mutex_unlock(&engine.lock);
if (l.type == LETTER_BLOB) {
size_t size = blob_length(l.blob_data) / 8;
@@ -784,6 +785,8 @@ void actor_turn(cell_rt *actor)
actor->slow_strikes = 0; /* completed within fast timer */
ENDTURN:
/* Invalidate any outstanding pause/kill timers for this turn */
atomic_fetch_add_explicit(&actor->turn_gen, 1, memory_order_relaxed);
actor->state = ACTOR_IDLE;
if (actor->trace_hook)