flag used for actor stopping insetad of counter
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user