diff --git a/source/cell.c b/source/cell.c index d8f36e23..4fc6050e 100644 --- a/source/cell.c +++ b/source/cell.c @@ -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 */ diff --git a/source/mach.c b/source/mach.c index 606c1afc..3b6436ce 100644 --- a/source/mach.c +++ b/source/mach.c @@ -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 : ""; + 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; diff --git a/source/scheduler.c b/source/scheduler.c index df0ed40e..ad566855 100644 --- a/source/scheduler.c +++ b/source/scheduler.c @@ -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); }