better disrupt logging; actor exit on crash

This commit is contained in:
2026-02-10 12:38:06 -06:00
parent 0d8b5cfb04
commit 54673e4a04
3 changed files with 39 additions and 12 deletions

View File

@@ -456,7 +456,7 @@ int cell_init(int argc, char **argv)
if (scheduler_actor_count() > 0) {
actor_loop();
exit_handler();
return exit_code;
exit(0);
}
/* No actors spawned — clean up CLI context */

View File

@@ -2552,7 +2552,7 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
}
/* Type mismatch — disrupt */
return JS_EXCEPTION;
return JS_ThrowTypeError(ctx, "type mismatch in binary operation");
}
/* Check for interrupt */
@@ -3021,6 +3021,8 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
JSValue func_val = frame->slots[base];
if (!JS_IsFunction(func_val)) {
JS_ThrowTypeError(ctx, "not a function");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
}
@@ -3263,7 +3265,11 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
/* push R(B) onto array R(A) */
JSValue arr = frame->slots[a];
JSValue val = frame->slots[b];
if (!JS_IsArray(arr)) goto disrupt;
if (!JS_IsArray(arr)) {
JS_ThrowTypeError(ctx, "cannot push to non-array");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
}
JSGCRef arr_gc;
JS_PushGCRef(ctx, &arr_gc);
arr_gc.val = arr;
@@ -3278,7 +3284,11 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
case MACH_POP: {
/* R(A) = pop last element from array R(B) */
JSValue arr = frame->slots[b];
if (!JS_IsArray(arr)) goto disrupt;
if (!JS_IsArray(arr)) {
JS_ThrowTypeError(ctx, "cannot pop from non-array");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
}
JSValue val = JS_ArrayPop(ctx, arr);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsException(val)) goto disrupt;
@@ -3353,7 +3363,13 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
break;
}
if (JS_IsNull(frame->caller)) {
fprintf(stderr, "unhandled disruption\n");
if (!JS_HasException(ctx)) {
/* Bare disrupt with no error message — provide location */
const char *fn_name = code->name_cstr ? code->name_cstr : "<anonymous>";
fprintf(stderr, "unhandled disruption in %s\n", fn_name);
} else {
fprintf(stderr, "unhandled disruption\n");
}
result = JS_Throw(ctx, JS_NULL);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto done;

View File

@@ -304,6 +304,7 @@ void actor_free(cell_rt *actor)
int actor_count = lockless_shlen(actors);
if (actor_count == 0) {
fprintf(stderr, "all actors are dead\n");
pthread_mutex_lock(&engine.lock);
engine.shutting_down = 1;
pthread_cond_broadcast(&engine.wake_cond);
@@ -533,7 +534,7 @@ const char *register_actor(const char *id, cell_rt *actor, int mainthread, doubl
actor->main_thread_only = mainthread;
actor->id = strdup(id);
actor->ar_secs = ar;
int added = lockless_shput_unique(actors, id, actor);
int added = lockless_shput_unique(actors, actor->id, actor);
if (!added) {
free(actor->id);
return "Actor with given ID already exists.";
@@ -591,20 +592,22 @@ void actor_turn(cell_rt *actor)
arrdel(actor->letters, 0); // O(N) but we kept array as requested
pthread_mutex_unlock(actor->msg_mutex);
if (l.type == LETTER_BLOB) {
if (l.type == LETTER_BLOB) {
// Create a JS blob from the C blob
size_t size = blob_length(l.blob_data) / 8; // Convert bits to bytes
JSValue arg = js_new_blob_stoned_copy(actor->context, (void*)blob_data(l.blob_data), size);
blob_destroy(l.blob_data);
result = JS_Call(actor->context, actor->message_handle_ref.val, JS_NULL, 1, &arg);
uncaught_exception(actor->context, result);
if (!uncaught_exception(actor->context, result))
actor->disrupt = 1;
JS_FreeValue(actor->context, arg);
} else if (l.type == LETTER_CALLBACK) {
result = JS_Call(actor->context, l.callback, JS_NULL, 0, NULL);
uncaught_exception(actor->context, result);
if (!uncaught_exception(actor->context, result))
actor->disrupt = 1;
JS_FreeValue(actor->context, l.callback);
}
if (actor->disrupt) goto ENDTURN;
ENDTURN:
@@ -612,9 +615,17 @@ void actor_turn(cell_rt *actor)
if (actor->trace_hook)
actor->trace_hook(actor->name, CELL_HOOK_EXIT);
if (actor->disrupt) {
/* Actor must die. Unlock before freeing so actor_free can
lock/unlock/destroy the mutex without use-after-free. */
pthread_mutex_unlock(actor->mutex);
actor_free(actor);
return;
}
set_actor_state(actor);
pthread_mutex_unlock(actor->mutex);
}