refactor
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
#include "cell.h"
|
#include "cell.h"
|
||||||
#include "quickjs-internal.h"
|
#include "pit_internal.h"
|
||||||
|
|
||||||
JSC_CCALL(os_mem_limit, JS_SetMemoryLimit(JS_GetRuntime(js), js2number(js,argv[0])))
|
JSC_CCALL(os_mem_limit, JS_SetMemoryLimit(JS_GetRuntime(js), js2number(js,argv[0])))
|
||||||
JSC_CCALL(os_max_stacksize, JS_SetMaxStackSize(js, js2number(js,argv[0])))
|
JSC_CCALL(os_max_stacksize, JS_SetMaxStackSize(js, js2number(js,argv[0])))
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ source/
|
|||||||
|
|
||||||
**`cell_runtime.c`** is the single file that defines the native code contract. It should:
|
**`cell_runtime.c`** is the single file that defines the native code contract. It should:
|
||||||
|
|
||||||
1. Include `quickjs-internal.h` for access to value representation and heap types
|
1. Include `pit_internal.h` for access to value representation and heap types
|
||||||
2. Export all `cell_rt_*` functions with C linkage (no `static`)
|
2. Export all `cell_rt_*` functions with C linkage (no `static`)
|
||||||
3. Keep each function thin — delegate to existing `JS_*` functions where possible
|
3. Keep each function thin — delegate to existing `JS_*` functions where possible
|
||||||
4. Handle GC safety: after any allocation (frame, string, array), callers' frames may have moved
|
4. Handle GC safety: after any allocation (frame, string, array), callers' frames may have moved
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#define NOTA_IMPLEMENTATION
|
#define NOTA_IMPLEMENTATION
|
||||||
#include "quickjs-internal.h"
|
#include "pit_internal.h"
|
||||||
#include "cell.h"
|
#include "cell.h"
|
||||||
|
|
||||||
static int nota_get_arr_len (JSContext *ctx, JSValue arr) {
|
static int nota_get_arr_len (JSContext *ctx, JSValue arr) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "cell.h"
|
#include "cell.h"
|
||||||
#include "cell_internal.h"
|
#include "pit_internal.h"
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@@ -308,12 +308,9 @@ JSC_SCALL(os_system,
|
|||||||
setting pause_flag = 2. Bump turn_gen so stale timer events are
|
setting pause_flag = 2. Bump turn_gen so stale timer events are
|
||||||
ignored, and clear the pause flag so the VM doesn't raise
|
ignored, and clear the pause flag so the VM doesn't raise
|
||||||
"interrupted" on the next backward branch. */
|
"interrupted" on the next backward branch. */
|
||||||
cell_rt *crt = JS_GetContextOpaque(js);
|
atomic_fetch_add_explicit(&js->turn_gen, 1, memory_order_relaxed);
|
||||||
if (crt) {
|
JS_SetPauseFlag(js, 0);
|
||||||
atomic_fetch_add_explicit(&crt->turn_gen, 1, memory_order_relaxed);
|
js->turn_start_ns = cell_ns();
|
||||||
JS_SetPauseFlag(js, 0);
|
|
||||||
crt->turn_start_ns = cell_ns();
|
|
||||||
}
|
|
||||||
ret = number2js(js, err);
|
ret = number2js(js, err);
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#define WOTA_IMPLEMENTATION
|
#define WOTA_IMPLEMENTATION
|
||||||
#include "quickjs-internal.h"
|
#include "pit_internal.h"
|
||||||
#include "cell.h"
|
#include "cell.h"
|
||||||
|
|
||||||
typedef struct ObjectRef {
|
typedef struct ObjectRef {
|
||||||
|
|||||||
130
source/cell.c
130
source/cell.c
@@ -8,8 +8,7 @@
|
|||||||
#include "stb_ds.h"
|
#include "stb_ds.h"
|
||||||
|
|
||||||
#include "cell.h"
|
#include "cell.h"
|
||||||
#include "quickjs-internal.h"
|
#include "pit_internal.h"
|
||||||
#include "cell_internal.h"
|
|
||||||
|
|
||||||
#define BOOTSTRAP_MCODE "boot/bootstrap.cm.mcode"
|
#define BOOTSTRAP_MCODE "boot/bootstrap.cm.mcode"
|
||||||
#define ENGINE_SRC "internal/engine.cm"
|
#define ENGINE_SRC "internal/engine.cm"
|
||||||
@@ -27,13 +26,13 @@
|
|||||||
int run_c_test_suite(JSContext *ctx);
|
int run_c_test_suite(JSContext *ctx);
|
||||||
static int run_test_suite(size_t heap_size);
|
static int run_test_suite(size_t heap_size);
|
||||||
|
|
||||||
cell_rt *root_cell = NULL;
|
static JSContext *root_ctx = NULL;
|
||||||
static char *shop_path = NULL;
|
static char *shop_path = NULL;
|
||||||
volatile JSContext *g_crash_ctx = NULL;
|
volatile JSContext *g_crash_ctx = NULL;
|
||||||
static char *core_path = NULL;
|
static char *core_path = NULL;
|
||||||
static int native_mode = 0;
|
static int native_mode = 0;
|
||||||
static int warn_mode = 1;
|
static int warn_mode = 1;
|
||||||
static JSRuntime *g_runtime = NULL;
|
JSRuntime *g_runtime = NULL;
|
||||||
|
|
||||||
// Compute blake2b hash of data and return hex string (caller must free)
|
// Compute blake2b hash of data and return hex string (caller must free)
|
||||||
static char *compute_blake2_hex(const char *data, size_t size) {
|
static char *compute_blake2_hex(const char *data, size_t size) {
|
||||||
@@ -303,44 +302,28 @@ const char* cell_get_core_path(void) {
|
|||||||
return core_path;
|
return core_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void actor_disrupt(cell_rt *crt)
|
void actor_disrupt(JSContext *ctx)
|
||||||
{
|
{
|
||||||
crt->disrupt = 1;
|
ctx->disrupt = 1;
|
||||||
JS_SetPauseFlag(crt->context, 2);
|
JS_SetPauseFlag(ctx, 2);
|
||||||
if (crt->state != ACTOR_RUNNING)
|
if (ctx->state != ACTOR_RUNNING)
|
||||||
actor_free(crt);
|
actor_free(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue js_core_internal_os_use(JSContext *js);
|
JSValue js_core_internal_os_use(JSContext *js);
|
||||||
JSValue js_core_json_use(JSContext *js);
|
JSValue js_core_json_use(JSContext *js);
|
||||||
|
|
||||||
void script_startup(cell_rt *prt)
|
void script_startup(JSContext *js)
|
||||||
{
|
{
|
||||||
if (!g_runtime) {
|
if (!g_runtime) {
|
||||||
g_runtime = JS_NewRuntime();
|
g_runtime = JS_NewRuntime();
|
||||||
}
|
}
|
||||||
JSContext *js = JS_NewContext(g_runtime);
|
|
||||||
|
|
||||||
JS_SetContextOpaque(js, prt);
|
|
||||||
JS_SetGCScanExternal(js, actor_gc_scan);
|
JS_SetGCScanExternal(js, actor_gc_scan);
|
||||||
prt->context = js;
|
|
||||||
|
|
||||||
/* Set per-actor heap memory limit */
|
/* Set per-actor heap memory limit */
|
||||||
JS_SetHeapMemoryLimit(js, ACTOR_MEMORY_LIMIT);
|
JS_SetHeapMemoryLimit(js, ACTOR_MEMORY_LIMIT);
|
||||||
|
|
||||||
/* Register all GCRef fields so the Cheney GC can relocate them. */
|
|
||||||
JS_AddGCRef(js, &prt->idx_buffer_ref);
|
|
||||||
JS_AddGCRef(js, &prt->on_exception_ref);
|
|
||||||
JS_AddGCRef(js, &prt->message_handle_ref);
|
|
||||||
JS_AddGCRef(js, &prt->unneeded_ref);
|
|
||||||
JS_AddGCRef(js, &prt->actor_sym_ref);
|
|
||||||
prt->idx_buffer_ref.val = JS_NULL;
|
|
||||||
prt->on_exception_ref.val = JS_NULL;
|
|
||||||
prt->message_handle_ref.val = JS_NULL;
|
|
||||||
prt->unneeded_ref.val = JS_NULL;
|
|
||||||
prt->actor_sym_ref.val = JS_NULL;
|
|
||||||
|
|
||||||
cell_rt *crt = JS_GetContextOpaque(js);
|
|
||||||
JS_FreeValue(js, js_core_blob_use(js));
|
JS_FreeValue(js, js_core_blob_use(js));
|
||||||
|
|
||||||
// Try engine fast-path: load engine.cm from source-hash cache
|
// Try engine fast-path: load engine.cm from source-hash cache
|
||||||
@@ -381,11 +364,11 @@ void script_startup(cell_rt *prt)
|
|||||||
JSValue boot_env = JS_Stone(js, boot_env_ref.val);
|
JSValue boot_env = JS_Stone(js, boot_env_ref.val);
|
||||||
JS_DeleteGCRef(js, &boot_env_ref);
|
JS_DeleteGCRef(js, &boot_env_ref);
|
||||||
|
|
||||||
crt->state = ACTOR_RUNNING;
|
js->state = ACTOR_RUNNING;
|
||||||
JSValue bv = JS_RunMachBin(js, (const uint8_t *)boot_bin, boot_bin_size, boot_env);
|
JSValue bv = JS_RunMachBin(js, (const uint8_t *)boot_bin, boot_bin_size, boot_env);
|
||||||
free(boot_bin);
|
free(boot_bin);
|
||||||
uncaught_exception(js, bv);
|
uncaught_exception(js, bv);
|
||||||
crt->state = ACTOR_IDLE;
|
js->state = ACTOR_IDLE;
|
||||||
|
|
||||||
// Retry engine from cache
|
// Retry engine from cache
|
||||||
bin_data = try_engine_cache(&bin_size);
|
bin_data = try_engine_cache(&bin_size);
|
||||||
@@ -405,20 +388,20 @@ void script_startup(cell_rt *prt)
|
|||||||
tmp = js_core_json_use(js);
|
tmp = js_core_json_use(js);
|
||||||
JS_SetPropertyStr(js, env_ref.val, "json", tmp);
|
JS_SetPropertyStr(js, env_ref.val, "json", tmp);
|
||||||
|
|
||||||
crt->actor_sym_ref.val = JS_NewObject(js);
|
js->actor_sym_ref.val = JS_NewObject(js);
|
||||||
JS_CellStone(js, crt->actor_sym_ref.val);
|
JS_CellStone(js, js->actor_sym_ref.val);
|
||||||
JS_SetActorSym(js, JS_DupValue(js, crt->actor_sym_ref.val));
|
JS_SetActorSym(js, JS_DupValue(js, js->actor_sym_ref.val));
|
||||||
JS_SetPropertyStr(js, env_ref.val, "actorsym", JS_DupValue(js, crt->actor_sym_ref.val));
|
JS_SetPropertyStr(js, env_ref.val, "actorsym", JS_DupValue(js, js->actor_sym_ref.val));
|
||||||
|
|
||||||
// Always set init (even if null)
|
// Always set init (even if null)
|
||||||
if (crt->init_wota) {
|
if (js->init_wota) {
|
||||||
JSGCRef init_ref;
|
JSGCRef init_ref;
|
||||||
JS_PushGCRef(js, &init_ref);
|
JS_PushGCRef(js, &init_ref);
|
||||||
init_ref.val = wota2value(js, crt->init_wota);
|
init_ref.val = wota2value(js, js->init_wota);
|
||||||
JS_SetPropertyStr(js, env_ref.val, "init", init_ref.val);
|
JS_SetPropertyStr(js, env_ref.val, "init", init_ref.val);
|
||||||
JS_PopGCRef(js, &init_ref);
|
JS_PopGCRef(js, &init_ref);
|
||||||
free(crt->init_wota);
|
free(js->init_wota);
|
||||||
crt->init_wota = NULL;
|
js->init_wota = NULL;
|
||||||
} else {
|
} else {
|
||||||
JS_SetPropertyStr(js, env_ref.val, "init", JS_NULL);
|
JS_SetPropertyStr(js, env_ref.val, "init", JS_NULL);
|
||||||
}
|
}
|
||||||
@@ -438,12 +421,12 @@ void script_startup(cell_rt *prt)
|
|||||||
JS_DeleteGCRef(js, &env_ref);
|
JS_DeleteGCRef(js, &env_ref);
|
||||||
|
|
||||||
// Run engine from binary
|
// Run engine from binary
|
||||||
crt->state = ACTOR_RUNNING;
|
js->state = ACTOR_RUNNING;
|
||||||
JSValue v = JS_RunMachBin(js, (const uint8_t *)bin_data, bin_size, hidden_env);
|
JSValue v = JS_RunMachBin(js, (const uint8_t *)bin_data, bin_size, hidden_env);
|
||||||
free(bin_data);
|
free(bin_data);
|
||||||
uncaught_exception(js, v);
|
uncaught_exception(js, v);
|
||||||
crt->state = ACTOR_IDLE;
|
js->state = ACTOR_IDLE;
|
||||||
set_actor_state(crt);
|
set_actor_state(js);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void signal_handler(int sig)
|
static void signal_handler(int sig)
|
||||||
@@ -624,35 +607,23 @@ int cell_init(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a cell_rt for the CLI context so JS-C bridge functions work */
|
/* Set up mutexes on the CLI context */
|
||||||
cell_rt *cli_rt = calloc(sizeof(*cli_rt), 1);
|
ctx->mutex = malloc(sizeof(pthread_mutex_t));
|
||||||
cli_rt->mutex = malloc(sizeof(pthread_mutex_t));
|
|
||||||
pthread_mutexattr_t mattr;
|
pthread_mutexattr_t mattr;
|
||||||
pthread_mutexattr_init(&mattr);
|
pthread_mutexattr_init(&mattr);
|
||||||
pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
|
pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
|
||||||
pthread_mutex_init(cli_rt->mutex, &mattr);
|
pthread_mutex_init(ctx->mutex, &mattr);
|
||||||
cli_rt->msg_mutex = malloc(sizeof(pthread_mutex_t));
|
ctx->msg_mutex = malloc(sizeof(pthread_mutex_t));
|
||||||
pthread_mutex_init(cli_rt->msg_mutex, &mattr);
|
pthread_mutex_init(ctx->msg_mutex, &mattr);
|
||||||
pthread_mutexattr_destroy(&mattr);
|
pthread_mutexattr_destroy(&mattr);
|
||||||
|
|
||||||
cli_rt->context = ctx;
|
|
||||||
JS_SetContextOpaque(ctx, cli_rt);
|
|
||||||
JS_SetGCScanExternal(ctx, actor_gc_scan);
|
JS_SetGCScanExternal(ctx, actor_gc_scan);
|
||||||
|
|
||||||
JS_AddGCRef(ctx, &cli_rt->idx_buffer_ref);
|
ctx->actor_sym_ref.val = JS_NewObject(ctx);
|
||||||
JS_AddGCRef(ctx, &cli_rt->on_exception_ref);
|
JS_CellStone(ctx, ctx->actor_sym_ref.val);
|
||||||
JS_AddGCRef(ctx, &cli_rt->message_handle_ref);
|
JS_SetActorSym(ctx, JS_DupValue(ctx, ctx->actor_sym_ref.val));
|
||||||
JS_AddGCRef(ctx, &cli_rt->unneeded_ref);
|
|
||||||
JS_AddGCRef(ctx, &cli_rt->actor_sym_ref);
|
|
||||||
cli_rt->idx_buffer_ref.val = JS_NULL;
|
|
||||||
cli_rt->on_exception_ref.val = JS_NULL;
|
|
||||||
cli_rt->message_handle_ref.val = JS_NULL;
|
|
||||||
cli_rt->unneeded_ref.val = JS_NULL;
|
|
||||||
cli_rt->actor_sym_ref.val = JS_NewObject(ctx);
|
|
||||||
JS_CellStone(ctx, cli_rt->actor_sym_ref.val);
|
|
||||||
JS_SetActorSym(ctx, JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
|
|
||||||
|
|
||||||
root_cell = cli_rt;
|
root_ctx = ctx;
|
||||||
|
|
||||||
JS_FreeValue(ctx, js_core_blob_use(ctx));
|
JS_FreeValue(ctx, js_core_blob_use(ctx));
|
||||||
|
|
||||||
@@ -699,7 +670,7 @@ int cell_init(int argc, char **argv)
|
|||||||
JS_SetPropertyStr(ctx, boot_env_ref.val, "core_path", btmp);
|
JS_SetPropertyStr(ctx, boot_env_ref.val, "core_path", btmp);
|
||||||
btmp = shop_path ? JS_NewString(ctx, shop_path) : JS_NULL;
|
btmp = shop_path ? JS_NewString(ctx, shop_path) : JS_NULL;
|
||||||
JS_SetPropertyStr(ctx, boot_env_ref.val, "shop_path", btmp);
|
JS_SetPropertyStr(ctx, boot_env_ref.val, "shop_path", btmp);
|
||||||
JS_SetPropertyStr(ctx, boot_env_ref.val, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
|
JS_SetPropertyStr(ctx, boot_env_ref.val, "actorsym", JS_DupValue(ctx, ctx->actor_sym_ref.val));
|
||||||
btmp = js_core_json_use(ctx);
|
btmp = js_core_json_use(ctx);
|
||||||
JS_SetPropertyStr(ctx, boot_env_ref.val, "json", btmp);
|
JS_SetPropertyStr(ctx, boot_env_ref.val, "json", btmp);
|
||||||
if (native_mode)
|
if (native_mode)
|
||||||
@@ -763,7 +734,7 @@ int cell_init(int argc, char **argv)
|
|||||||
JS_SetPropertyStr(ctx, env_ref.val, "core_path", tmp);
|
JS_SetPropertyStr(ctx, env_ref.val, "core_path", tmp);
|
||||||
tmp = shop_path ? JS_NewString(ctx, shop_path) : JS_NULL;
|
tmp = shop_path ? JS_NewString(ctx, shop_path) : JS_NULL;
|
||||||
JS_SetPropertyStr(ctx, env_ref.val, "shop_path", tmp);
|
JS_SetPropertyStr(ctx, env_ref.val, "shop_path", tmp);
|
||||||
JS_SetPropertyStr(ctx, env_ref.val, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
|
JS_SetPropertyStr(ctx, env_ref.val, "actorsym", JS_DupValue(ctx, ctx->actor_sym_ref.val));
|
||||||
tmp = js_core_json_use(ctx);
|
tmp = js_core_json_use(ctx);
|
||||||
JS_SetPropertyStr(ctx, env_ref.val, "json", tmp);
|
JS_SetPropertyStr(ctx, env_ref.val, "json", tmp);
|
||||||
if (native_mode || !warn_mode || eval_script) {
|
if (native_mode || !warn_mode || eval_script) {
|
||||||
@@ -826,18 +797,11 @@ check_actors:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* No actors spawned — clean up CLI context */
|
/* No actors spawned — clean up CLI context */
|
||||||
JS_DeleteGCRef(ctx, &cli_rt->idx_buffer_ref);
|
pthread_mutex_destroy(ctx->mutex);
|
||||||
JS_DeleteGCRef(ctx, &cli_rt->on_exception_ref);
|
free(ctx->mutex);
|
||||||
JS_DeleteGCRef(ctx, &cli_rt->message_handle_ref);
|
pthread_mutex_destroy(ctx->msg_mutex);
|
||||||
JS_DeleteGCRef(ctx, &cli_rt->unneeded_ref);
|
free(ctx->msg_mutex);
|
||||||
JS_DeleteGCRef(ctx, &cli_rt->actor_sym_ref);
|
root_ctx = NULL;
|
||||||
|
|
||||||
pthread_mutex_destroy(cli_rt->mutex);
|
|
||||||
free(cli_rt->mutex);
|
|
||||||
pthread_mutex_destroy(cli_rt->msg_mutex);
|
|
||||||
free(cli_rt->msg_mutex);
|
|
||||||
free(cli_rt);
|
|
||||||
root_cell = NULL;
|
|
||||||
|
|
||||||
JS_FreeContext(ctx);
|
JS_FreeContext(ctx);
|
||||||
JS_FreeRuntime(g_runtime);
|
JS_FreeRuntime(g_runtime);
|
||||||
@@ -890,18 +854,7 @@ cell_hook cell_trace_gethook(void)
|
|||||||
|
|
||||||
void cell_rt_set_trace_hook(JSContext *ctx, cell_hook hook)
|
void cell_rt_set_trace_hook(JSContext *ctx, cell_hook hook)
|
||||||
{
|
{
|
||||||
struct cell_rt *rt = JS_GetContextOpaque(ctx);
|
ctx->actor_trace_hook = hook;
|
||||||
if (rt) rt->trace_hook = hook;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *cell_rt_name(struct cell_rt *rt)
|
|
||||||
{
|
|
||||||
return rt->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSContext *cell_rt_context(struct cell_rt *rt)
|
|
||||||
{
|
|
||||||
return rt->context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int uncaught_exception(JSContext *js, JSValue v)
|
int uncaught_exception(JSContext *js, JSValue v)
|
||||||
@@ -914,13 +867,12 @@ int uncaught_exception(JSContext *js, JSValue v)
|
|||||||
by JS_ThrowError2 / print_backtrace. Just clear the flag. */
|
by JS_ThrowError2 / print_backtrace. Just clear the flag. */
|
||||||
if (has_exc)
|
if (has_exc)
|
||||||
JS_GetException(js);
|
JS_GetException(js);
|
||||||
cell_rt *crt = JS_GetContextOpaque(js);
|
if (!JS_IsNull(js->on_exception_ref.val)) {
|
||||||
if (crt && !JS_IsNull(crt->on_exception_ref.val)) {
|
|
||||||
/* Disable interruption so actor_die can send messages
|
/* Disable interruption so actor_die can send messages
|
||||||
without being re-interrupted. */
|
without being re-interrupted. */
|
||||||
JS_SetPauseFlag(js, 0);
|
JS_SetPauseFlag(js, 0);
|
||||||
JSValue err = JS_NewString(js, "interrupted");
|
JSValue err = JS_NewString(js, "interrupted");
|
||||||
JS_Call(js, crt->on_exception_ref.val, JS_NULL, 1, &err);
|
JS_Call(js, js->on_exception_ref.val, JS_NULL, 1, &err);
|
||||||
/* Clear any secondary exception from the callback. */
|
/* Clear any secondary exception from the callback. */
|
||||||
if (JS_HasException(js))
|
if (JS_HasException(js))
|
||||||
JS_GetException(js);
|
JS_GetException(js);
|
||||||
|
|||||||
@@ -1,28 +1,3 @@
|
|||||||
/*
|
|
||||||
* cell.h — Consolidated public C API for ƿit modules
|
|
||||||
*
|
|
||||||
* QuickJS Javascript Engine
|
|
||||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
|
||||||
* Copyright (c) 2017-2021 Charlie Gordon
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to
|
|
||||||
* deal in the Software without restriction, including without limitation the
|
|
||||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
* sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
* IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#ifndef CELL_H
|
#ifndef CELL_H
|
||||||
#define CELL_H
|
#define CELL_H
|
||||||
|
|
||||||
@@ -282,7 +257,7 @@ static inline JSValue MIST_TryNewImmediateASCII (const char *str, size_t len) {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Heap object type checks (non-inline — see mist_is_* in quickjs-internal.h
|
/* Heap object type checks (non-inline — see mist_is_* in pit_internal.h
|
||||||
for inline versions used by the VM dispatch loop) */
|
for inline versions used by the VM dispatch loop) */
|
||||||
JS_BOOL JS_IsArray(JSValue v);
|
JS_BOOL JS_IsArray(JSValue v);
|
||||||
JS_BOOL JS_IsRecord(JSValue v);
|
JS_BOOL JS_IsRecord(JSValue v);
|
||||||
@@ -881,14 +856,11 @@ void js_debug_sethook (JSContext *ctx, js_hook, int type, void *user);
|
|||||||
|
|
||||||
#define CELL_HOOK_ENTER 1
|
#define CELL_HOOK_ENTER 1
|
||||||
#define CELL_HOOK_EXIT 2
|
#define CELL_HOOK_EXIT 2
|
||||||
struct cell_rt;
|
typedef void (*cell_hook)(JSContext *ctx, int type);
|
||||||
typedef void (*cell_hook)(struct cell_rt *rt, int type);
|
|
||||||
void cell_trace_sethook(cell_hook);
|
void cell_trace_sethook(cell_hook);
|
||||||
cell_hook cell_trace_gethook(void);
|
cell_hook cell_trace_gethook(void);
|
||||||
void cell_rt_set_trace_hook(JSContext *ctx, cell_hook hook);
|
void cell_rt_set_trace_hook(JSContext *ctx, cell_hook hook);
|
||||||
size_t cell_ctx_heap_used(JSContext *ctx);
|
size_t cell_ctx_heap_used(JSContext *ctx);
|
||||||
const char *cell_rt_name(struct cell_rt *rt);
|
|
||||||
JSContext *cell_rt_context(struct cell_rt *rt);
|
|
||||||
void js_debug_gethook(JSContext *ctx, js_hook *hook, int *type, void **user);
|
void js_debug_gethook(JSContext *ctx, js_hook *hook, int *type, void **user);
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdatomic.h>
|
|
||||||
|
|
||||||
/* Internal runtime accessors — not in public cell.h API */
|
|
||||||
void *JS_GetContextOpaque (JSContext *ctx);
|
|
||||||
void JS_SetContextOpaque (JSContext *ctx, void *opaque);
|
|
||||||
void JS_SetPauseFlag(JSContext *ctx, int value);
|
|
||||||
JSValue JS_GetStack (JSContext *ctx);
|
|
||||||
|
|
||||||
/* Letter type for unified message queue */
|
|
||||||
typedef enum {
|
|
||||||
LETTER_BLOB, /* Blob message */
|
|
||||||
LETTER_CALLBACK /* JSValue callback function */
|
|
||||||
} letter_type;
|
|
||||||
|
|
||||||
typedef struct letter {
|
|
||||||
letter_type type;
|
|
||||||
union {
|
|
||||||
blob *blob_data; /* For LETTER_BLOB */
|
|
||||||
JSValue callback; /* For LETTER_CALLBACK */
|
|
||||||
};
|
|
||||||
} letter;
|
|
||||||
|
|
||||||
#define ACTOR_IDLE 0 // Actor not doing anything
|
|
||||||
#define ACTOR_READY 1 // Actor ready for a turn
|
|
||||||
#define ACTOR_RUNNING 2 // Actor taking a turn
|
|
||||||
#define ACTOR_EXHAUSTED 3 // Actor waiting for GC
|
|
||||||
#define ACTOR_RECLAIMING 4 // Actor running GC
|
|
||||||
#define ACTOR_SLOW 5 // Actor going slowly; deprioritize
|
|
||||||
#define ACTOR_REFRESHED 6 // GC finished, ready to resume
|
|
||||||
|
|
||||||
// #define ACTOR_TRACE
|
|
||||||
|
|
||||||
#define ACTOR_FAST_TIMER_NS (10ULL * 1000000) // 10ms per turn
|
|
||||||
#define ACTOR_SLOW_TIMER_NS (60000ULL * 1000000) // 60s for slow actors
|
|
||||||
#define ACTOR_SLOW_STRIKES_MAX 3 // consecutive slow turns -> kill
|
|
||||||
#define ACTOR_MEMORY_LIMIT (16ULL * 1024 * 1024) // 16MB heap cap
|
|
||||||
|
|
||||||
typedef struct cell_rt {
|
|
||||||
JSContext *context;
|
|
||||||
|
|
||||||
/* JSValues on the GC heap — each paired with a JSGCRef so the
|
|
||||||
Cheney GC can relocate them during compaction. */
|
|
||||||
JSGCRef idx_buffer_ref;
|
|
||||||
JSGCRef on_exception_ref;
|
|
||||||
JSGCRef message_handle_ref;
|
|
||||||
JSGCRef unneeded_ref;
|
|
||||||
JSGCRef actor_sym_ref;
|
|
||||||
|
|
||||||
void *init_wota;
|
|
||||||
|
|
||||||
/* Protects JSContext usage */
|
|
||||||
pthread_mutex_t *mutex; /* for everything else */
|
|
||||||
pthread_mutex_t *msg_mutex; /* For message queue and timers queue */
|
|
||||||
|
|
||||||
char *id;
|
|
||||||
|
|
||||||
int idx_count;
|
|
||||||
|
|
||||||
/* The "mailbox" for incoming messages + a dedicated lock for it: */
|
|
||||||
letter *letters;
|
|
||||||
|
|
||||||
/* CHANGED FOR EVENTS: a separate lock for the actor->events queue */
|
|
||||||
struct { uint32_t key; JSValue value; } *timers;
|
|
||||||
|
|
||||||
int state;
|
|
||||||
uint32_t ar; // timer for unneeded
|
|
||||||
double ar_secs; // time for unneeded
|
|
||||||
|
|
||||||
int disrupt;
|
|
||||||
int is_quiescent; // tracked by scheduler for quiescence detection
|
|
||||||
int main_thread_only;
|
|
||||||
int affinity;
|
|
||||||
|
|
||||||
uint64_t turn_start_ns; // cell_ns() when turn began
|
|
||||||
_Atomic uint32_t turn_gen; // incremented each turn start
|
|
||||||
int slow_strikes; // consecutive slow-completed turns
|
|
||||||
int vm_suspended; // 1 if VM is paused mid-turn
|
|
||||||
|
|
||||||
const char *name; // human friendly name
|
|
||||||
cell_hook trace_hook;
|
|
||||||
} cell_rt;
|
|
||||||
|
|
||||||
/* Set by actor_turn/CLI before entering the VM, cleared after.
|
|
||||||
Read by signal_handler to print JS stack on crash. */
|
|
||||||
extern volatile JSContext *g_crash_ctx;
|
|
||||||
|
|
||||||
cell_rt *create_actor(void *wota);
|
|
||||||
const char *register_actor(const char *id, cell_rt *actor, int mainthread, double ar);
|
|
||||||
void actor_disrupt(cell_rt *actor);
|
|
||||||
|
|
||||||
const char *send_message(const char *id, void *msg);
|
|
||||||
const char *register_actor(const char *id, cell_rt *actor, int mainthread, double ar);
|
|
||||||
void actor_unneeded(cell_rt *actor, JSValue fn, double seconds);
|
|
||||||
void script_startup(cell_rt *rt);
|
|
||||||
int uncaught_exception(JSContext *js, JSValue v);
|
|
||||||
int actor_exists(const char *id);
|
|
||||||
|
|
||||||
void set_actor_state(cell_rt *actor);
|
|
||||||
void enqueue_actor_priority(cell_rt *actor);
|
|
||||||
void actor_clock(cell_rt *actor, JSValue fn);
|
|
||||||
uint32_t actor_delay(cell_rt *actor, JSValue fn, double seconds);
|
|
||||||
JSValue actor_remove_timer(cell_rt *actor, uint32_t timer_id);
|
|
||||||
void exit_handler(void);
|
|
||||||
void actor_loop();
|
|
||||||
void actor_initialize(void);
|
|
||||||
void actor_free(cell_rt *actor);
|
|
||||||
void actor_gc_scan(JSContext *ctx, uint8_t *fb, uint8_t *fe,
|
|
||||||
uint8_t *tb, uint8_t **tf, uint8_t *te);
|
|
||||||
int scheduler_actor_count(void);
|
|
||||||
void scheduler_enable_quiescence(void);
|
|
||||||
|
|
||||||
JSValue JS_ResumeRegisterVM(JSContext *ctx);
|
|
||||||
|
|
||||||
uint64_t cell_ns();
|
|
||||||
void cell_sleep(double seconds);
|
|
||||||
int randombytes(void *buf, size_t n);
|
|
||||||
@@ -1,29 +1,4 @@
|
|||||||
/*
|
#include "pit_internal.h"
|
||||||
* QuickJS Javascript Engine
|
|
||||||
*
|
|
||||||
* Copyright (c) 2017-2025 Fabrice Bellard
|
|
||||||
* Copyright (c) 2017-2025 Charlie Gordon
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to
|
|
||||||
* deal in the Software without restriction, including without limitation the
|
|
||||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
* sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
* IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "quickjs-internal.h"
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
|
|||||||
@@ -1,30 +1,6 @@
|
|||||||
#ifndef QUICKJS_INTERNAL_H
|
#ifndef QUICKJS_INTERNAL_H
|
||||||
#define QUICKJS_INTERNAL_H
|
#define QUICKJS_INTERNAL_H
|
||||||
|
|
||||||
/*
|
|
||||||
* QuickJS Javascript Engine
|
|
||||||
*
|
|
||||||
* Copyright (c) 2017-2025 Fabrice Bellard
|
|
||||||
* Copyright (c) 2017-2025 Charlie Gordon
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to
|
|
||||||
* deal in the Software without restriction, including without limitation the
|
|
||||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
* sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
* IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
@@ -36,6 +12,9 @@
|
|||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#ifndef TARGET_PLAYDATE
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
#include <malloc/malloc.h>
|
#include <malloc/malloc.h>
|
||||||
#elif defined(__linux__) || defined(__GLIBC__)
|
#elif defined(__linux__) || defined(__GLIBC__)
|
||||||
@@ -103,8 +82,6 @@ void JS_SetPoolSize (JSRuntime *rt, size_t initial, size_t cap);
|
|||||||
JSContext *JS_NewContext (JSRuntime *rt);
|
JSContext *JS_NewContext (JSRuntime *rt);
|
||||||
JSContext *JS_NewContextWithHeapSize (JSRuntime *rt, size_t heap_size);
|
JSContext *JS_NewContextWithHeapSize (JSRuntime *rt, size_t heap_size);
|
||||||
void JS_FreeContext (JSContext *s);
|
void JS_FreeContext (JSContext *s);
|
||||||
void *JS_GetContextOpaque (JSContext *ctx);
|
|
||||||
void JS_SetContextOpaque (JSContext *ctx, void *opaque);
|
|
||||||
|
|
||||||
typedef void (*JS_GCScanFn)(JSContext *ctx,
|
typedef void (*JS_GCScanFn)(JSContext *ctx,
|
||||||
uint8_t *from_base, uint8_t *from_end,
|
uint8_t *from_base, uint8_t *from_end,
|
||||||
@@ -864,6 +841,38 @@ typedef struct CCallRoot {
|
|||||||
struct CCallRoot *prev; /* stack for nesting (C -> JS -> C -> ...) */
|
struct CCallRoot *prev; /* stack for nesting (C -> JS -> C -> ...) */
|
||||||
} CCallRoot;
|
} CCallRoot;
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
Actor Types (merged from cell_internal.h)
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
/* Letter type for unified message queue */
|
||||||
|
typedef enum {
|
||||||
|
LETTER_BLOB,
|
||||||
|
LETTER_CALLBACK
|
||||||
|
} letter_type;
|
||||||
|
|
||||||
|
typedef struct letter {
|
||||||
|
letter_type type;
|
||||||
|
union {
|
||||||
|
blob *blob_data;
|
||||||
|
JSValue callback;
|
||||||
|
};
|
||||||
|
} letter;
|
||||||
|
|
||||||
|
/* Actor state machine constants */
|
||||||
|
#define ACTOR_IDLE 0
|
||||||
|
#define ACTOR_READY 1
|
||||||
|
#define ACTOR_RUNNING 2
|
||||||
|
#define ACTOR_EXHAUSTED 3
|
||||||
|
#define ACTOR_RECLAIMING 4
|
||||||
|
#define ACTOR_SLOW 5
|
||||||
|
#define ACTOR_REFRESHED 6
|
||||||
|
|
||||||
|
#define ACTOR_FAST_TIMER_NS (10ULL * 1000000)
|
||||||
|
#define ACTOR_SLOW_TIMER_NS (60000ULL * 1000000)
|
||||||
|
#define ACTOR_SLOW_STRIKES_MAX 3
|
||||||
|
#define ACTOR_MEMORY_LIMIT (16ULL * 1024 * 1024)
|
||||||
|
|
||||||
struct JSContext {
|
struct JSContext {
|
||||||
JSRuntime *rt;
|
JSRuntime *rt;
|
||||||
|
|
||||||
@@ -919,7 +928,6 @@ struct JSContext {
|
|||||||
|
|
||||||
/* if NULL, RegExp compilation is not supported */
|
/* if NULL, RegExp compilation is not supported */
|
||||||
JSValue (*compile_regexp) (JSContext *ctx, JSValue pattern, JSValue flags);
|
JSValue (*compile_regexp) (JSContext *ctx, JSValue pattern, JSValue flags);
|
||||||
void *user_opaque;
|
|
||||||
|
|
||||||
/* GC callback to scan external C-side roots (actor letters, timers) */
|
/* GC callback to scan external C-side roots (actor letters, timers) */
|
||||||
void (*gc_scan_external)(JSContext *ctx,
|
void (*gc_scan_external)(JSContext *ctx,
|
||||||
@@ -956,6 +964,44 @@ struct JSContext {
|
|||||||
// todo: want this, but should be a simple increment/decrement counter while frames are pushed
|
// todo: want this, but should be a simple increment/decrement counter while frames are pushed
|
||||||
size_t stack_depth;
|
size_t stack_depth;
|
||||||
size_t stack_limit;
|
size_t stack_limit;
|
||||||
|
|
||||||
|
/* === Actor fields (merged from cell_rt) === */
|
||||||
|
|
||||||
|
JSGCRef idx_buffer_ref;
|
||||||
|
JSGCRef on_exception_ref;
|
||||||
|
JSGCRef message_handle_ref;
|
||||||
|
JSGCRef unneeded_ref;
|
||||||
|
JSGCRef actor_sym_ref;
|
||||||
|
|
||||||
|
void *init_wota;
|
||||||
|
|
||||||
|
#ifndef TARGET_PLAYDATE
|
||||||
|
pthread_mutex_t *mutex;
|
||||||
|
pthread_mutex_t *msg_mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *id;
|
||||||
|
int idx_count;
|
||||||
|
|
||||||
|
letter *letters;
|
||||||
|
struct { uint32_t key; JSValue value; } *timers;
|
||||||
|
|
||||||
|
int state;
|
||||||
|
uint32_t ar;
|
||||||
|
double ar_secs;
|
||||||
|
|
||||||
|
int disrupt;
|
||||||
|
int is_quiescent;
|
||||||
|
int main_thread_only;
|
||||||
|
int affinity;
|
||||||
|
|
||||||
|
uint64_t turn_start_ns;
|
||||||
|
_Atomic uint32_t turn_gen;
|
||||||
|
int slow_strikes;
|
||||||
|
int vm_suspended;
|
||||||
|
|
||||||
|
const char *name;
|
||||||
|
cell_hook actor_trace_hook;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
@@ -979,6 +1025,44 @@ static inline const char *JS_KeyGetStr (JSContext *ctx, char *buf, size_t buf_si
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
Actor / Scheduler Declarations (merged from cell_internal.h)
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
/* Set by actor_turn/CLI before entering the VM, cleared after.
|
||||||
|
Read by signal_handler to print JS stack on crash. */
|
||||||
|
extern volatile JSContext *g_crash_ctx;
|
||||||
|
|
||||||
|
JSContext *create_actor(void *wota);
|
||||||
|
const char *register_actor(const char *id, JSContext *actor, int mainthread, double ar);
|
||||||
|
void actor_disrupt(JSContext *actor);
|
||||||
|
|
||||||
|
const char *send_message(const char *id, void *msg);
|
||||||
|
void actor_unneeded(JSContext *actor, JSValue fn, double seconds);
|
||||||
|
void script_startup(JSContext *ctx);
|
||||||
|
int uncaught_exception(JSContext *js, JSValue v);
|
||||||
|
int actor_exists(const char *id);
|
||||||
|
|
||||||
|
void set_actor_state(JSContext *actor);
|
||||||
|
void enqueue_actor_priority(JSContext *actor);
|
||||||
|
void actor_clock(JSContext *actor, JSValue fn);
|
||||||
|
uint32_t actor_delay(JSContext *actor, JSValue fn, double seconds);
|
||||||
|
JSValue actor_remove_timer(JSContext *actor, uint32_t timer_id);
|
||||||
|
void exit_handler(void);
|
||||||
|
void actor_loop(void);
|
||||||
|
void actor_initialize(void);
|
||||||
|
void actor_free(JSContext *actor);
|
||||||
|
void actor_gc_scan(JSContext *ctx, uint8_t *fb, uint8_t *fe,
|
||||||
|
uint8_t *tb, uint8_t **tf, uint8_t *te);
|
||||||
|
int scheduler_actor_count(void);
|
||||||
|
void scheduler_enable_quiescence(void);
|
||||||
|
|
||||||
|
JSValue JS_ResumeRegisterVM(JSContext *ctx);
|
||||||
|
|
||||||
|
uint64_t cell_ns(void);
|
||||||
|
void cell_sleep(double seconds);
|
||||||
|
int randombytes(void *buf, size_t n);
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
Constant Text Pool Functions
|
Constant Text Pool Functions
|
||||||
============================================================ */
|
============================================================ */
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
* string comparison, bitwise ops on floats, and boolean conversion.
|
* string comparison, bitwise ops on floats, and boolean conversion.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "quickjs-internal.h"
|
#include "pit_internal.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
#include "cell.h"
|
#include "cell.h"
|
||||||
#include "cell_internal.h"
|
#include "pit_internal.h"
|
||||||
#include "quickjs-internal.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
JSC_CCALL(os_createactor,
|
JSC_CCALL(os_createactor,
|
||||||
cell_rt *rt = JS_GetContextOpaque(js);
|
if (js->disrupt)
|
||||||
if (rt->disrupt)
|
|
||||||
return JS_RaiseDisrupt(js, "Can't start a new actor while disrupting.");
|
return JS_RaiseDisrupt(js, "Can't start a new actor while disrupting.");
|
||||||
|
|
||||||
void *startup = value2wota(js, argv[0], JS_NULL, NULL);
|
void *startup = value2wota(js, argv[0], JS_NULL, NULL);
|
||||||
@@ -57,14 +55,12 @@ JSC_CCALL(os_mailbox_push,
|
|||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(os_register_actor,
|
JSC_CCALL(os_register_actor,
|
||||||
cell_rt *rt = JS_GetContextOpaque(js);
|
|
||||||
const char *id = JS_ToCString(js, argv[0]);
|
const char *id = JS_ToCString(js, argv[0]);
|
||||||
double ar;
|
double ar;
|
||||||
JS_ToFloat64(js, &ar, argv[3]);
|
JS_ToFloat64(js, &ar, argv[3]);
|
||||||
const char *err = register_actor(id, rt, JS_ToBool(js, argv[2]), ar);
|
const char *err = register_actor(id, js, JS_ToBool(js, argv[2]), ar);
|
||||||
if (err) return JS_RaiseDisrupt(js, "Could not register actor: %s", err);
|
if (err) return JS_RaiseDisrupt(js, "Could not register actor: %s", err);
|
||||||
rt->message_handle_ref.val = argv[1];
|
js->message_handle_ref.val = argv[1];
|
||||||
rt->context = js;
|
|
||||||
JS_FreeCString(js, id);
|
JS_FreeCString(js, id);
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -77,56 +73,49 @@ JSC_CCALL(os_mailbox_exist,
|
|||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(os_unneeded,
|
JSC_CCALL(os_unneeded,
|
||||||
cell_rt *actor = JS_GetContextOpaque(js);
|
|
||||||
double secs;
|
double secs;
|
||||||
JS_ToFloat64(js, &secs, argv[1]);
|
JS_ToFloat64(js, &secs, argv[1]);
|
||||||
actor_unneeded(actor, argv[0], secs);
|
actor_unneeded(js, argv[0], secs);
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(actor_disrupt,
|
JSC_CCALL(actor_disrupt,
|
||||||
cell_rt *crt = JS_GetContextOpaque(js);
|
actor_disrupt(js);
|
||||||
actor_disrupt(crt);
|
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_SCALL(actor_setname,
|
JSC_SCALL(actor_setname,
|
||||||
cell_rt *rt = JS_GetContextOpaque(js);
|
js->name = strdup(str);
|
||||||
rt->name = strdup(str);
|
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(actor_on_exception,
|
JSC_CCALL(actor_on_exception,
|
||||||
cell_rt *rt = JS_GetContextOpaque(js);
|
js->on_exception_ref.val = argv[0];
|
||||||
rt->on_exception_ref.val = argv[0];
|
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(actor_clock,
|
JSC_CCALL(actor_clock,
|
||||||
if (!JS_IsFunction(argv[0]))
|
if (!JS_IsFunction(argv[0]))
|
||||||
return JS_RaiseDisrupt(js, "Argument must be a function.");
|
return JS_RaiseDisrupt(js, "Argument must be a function.");
|
||||||
|
|
||||||
cell_rt *actor = JS_GetContextOpaque(js);
|
actor_clock(js, argv[0]);
|
||||||
actor_clock(actor, argv[0]);
|
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(actor_delay,
|
JSC_CCALL(actor_delay,
|
||||||
if (!JS_IsFunction(argv[0]))
|
if (!JS_IsFunction(argv[0]))
|
||||||
return JS_RaiseDisrupt(js, "Argument must be a function.");
|
return JS_RaiseDisrupt(js, "Argument must be a function.");
|
||||||
|
|
||||||
cell_rt *actor = JS_GetContextOpaque(js);
|
|
||||||
double seconds;
|
double seconds;
|
||||||
JS_ToFloat64(js, &seconds, argv[1]);
|
JS_ToFloat64(js, &seconds, argv[1]);
|
||||||
if (seconds <= 0) {
|
if (seconds <= 0) {
|
||||||
actor_clock(actor, argv[0]);
|
actor_clock(js, argv[0]);
|
||||||
return JS_NULL;
|
return JS_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t id = actor_delay(actor, argv[0], seconds);
|
uint32_t id = actor_delay(js, argv[0], seconds);
|
||||||
return JS_NewUint32(js, id);
|
return JS_NewUint32(js, id);
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(actor_removetimer,
|
JSC_CCALL(actor_removetimer,
|
||||||
cell_rt *actor = JS_GetContextOpaque(js);
|
|
||||||
uint32_t timer_id;
|
uint32_t timer_id;
|
||||||
JS_ToUint32(js, &timer_id, argv[0]);
|
JS_ToUint32(js, &timer_id, argv[0]);
|
||||||
(void)actor_remove_timer(actor, timer_id);
|
(void)actor_remove_timer(js, timer_id);
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Log callback bridge: called from JS_Log, calls ƿit log(channel, [msg, stack])
|
/* Log callback bridge: called from JS_Log, calls ƿit log(channel, [msg, stack])
|
||||||
|
|||||||
@@ -1,30 +1,5 @@
|
|||||||
/*
|
|
||||||
* QuickJS Javascript Engine
|
|
||||||
*
|
|
||||||
* Copyright (c) 2017-2025 Fabrice Bellard
|
|
||||||
* Copyright (c) 2017-2025 Charlie Gordon
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to
|
|
||||||
* deal in the Software without restriction, including without limitation the
|
|
||||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
* sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
* IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define BLOB_IMPLEMENTATION
|
#define BLOB_IMPLEMENTATION
|
||||||
#include "quickjs-internal.h"
|
#include "pit_internal.h"
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
// #define DUMP_BUDDY
|
// #define DUMP_BUDDY
|
||||||
@@ -87,7 +62,7 @@ static inline JS_BOOL JS_IsInteger (JSValue v) {
|
|||||||
JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
|
JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
|
||||||
|
|
||||||
/* === Public API wrappers (non-inline, for quickjs.h declarations) ===
|
/* === Public API wrappers (non-inline, for quickjs.h declarations) ===
|
||||||
These delegate to the static inline versions in quickjs-internal.h. */
|
These delegate to the static inline versions in pit_internal.h. */
|
||||||
|
|
||||||
JS_BOOL JS_IsStone(JSValue v) { return mist_is_stone(v); }
|
JS_BOOL JS_IsStone(JSValue v) { return mist_is_stone(v); }
|
||||||
JS_BOOL JS_IsArray(JSValue v) { return mist_is_array(v); }
|
JS_BOOL JS_IsArray(JSValue v) { return mist_is_array(v); }
|
||||||
@@ -1943,10 +1918,8 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
|
|||||||
/* Check memory limit — kill actor if heap exceeds cap */
|
/* Check memory limit — kill actor if heap exceeds cap */
|
||||||
if (ctx->heap_memory_limit > 0 && ctx->current_block_size > ctx->heap_memory_limit) {
|
if (ctx->heap_memory_limit > 0 && ctx->current_block_size > ctx->heap_memory_limit) {
|
||||||
#ifdef ACTOR_TRACE
|
#ifdef ACTOR_TRACE
|
||||||
void *crt = ctx->user_opaque;
|
fprintf(stderr, "[ACTOR_TRACE] heap %zu > limit %zu, OOM\n",
|
||||||
if (crt)
|
ctx->current_block_size, ctx->heap_memory_limit);
|
||||||
fprintf(stderr, "[ACTOR_TRACE] heap %zu > limit %zu, OOM\n",
|
|
||||||
ctx->current_block_size, ctx->heap_memory_limit);
|
|
||||||
#endif
|
#endif
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -2080,6 +2053,18 @@ JSContext *JS_NewContextRawWithHeapSize (JSRuntime *rt, size_t heap_size) {
|
|||||||
ctx->log_callback_js = JS_NULL;
|
ctx->log_callback_js = JS_NULL;
|
||||||
ctx->log_callback = NULL;
|
ctx->log_callback = NULL;
|
||||||
|
|
||||||
|
/* Register actor GCRef fields so the Cheney GC can relocate them. */
|
||||||
|
JS_AddGCRef(ctx, &ctx->idx_buffer_ref);
|
||||||
|
JS_AddGCRef(ctx, &ctx->on_exception_ref);
|
||||||
|
JS_AddGCRef(ctx, &ctx->message_handle_ref);
|
||||||
|
JS_AddGCRef(ctx, &ctx->unneeded_ref);
|
||||||
|
JS_AddGCRef(ctx, &ctx->actor_sym_ref);
|
||||||
|
ctx->idx_buffer_ref.val = JS_NULL;
|
||||||
|
ctx->on_exception_ref.val = JS_NULL;
|
||||||
|
ctx->message_handle_ref.val = JS_NULL;
|
||||||
|
ctx->unneeded_ref.val = JS_NULL;
|
||||||
|
ctx->actor_sym_ref.val = JS_NULL;
|
||||||
|
|
||||||
/* Initialize constant text pool (avoids overflow pages for common case) */
|
/* Initialize constant text pool (avoids overflow pages for common case) */
|
||||||
{
|
{
|
||||||
size_t ct_pool_size = 64 * 1024; /* 64KB initial CT pool */
|
size_t ct_pool_size = 64 * 1024; /* 64KB initial CT pool */
|
||||||
@@ -2156,11 +2141,7 @@ JSContext *JS_NewContextWithHeapSize (JSRuntime *rt, size_t heap_size) {
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *JS_GetContextOpaque (JSContext *ctx) { return ctx->user_opaque; }
|
|
||||||
|
|
||||||
void JS_SetContextOpaque (JSContext *ctx, void *opaque) {
|
|
||||||
ctx->user_opaque = opaque;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JS_SetGCScanExternal(JSContext *ctx, JS_GCScanFn fn) {
|
void JS_SetGCScanExternal(JSContext *ctx, JS_GCScanFn fn) {
|
||||||
ctx->gc_scan_external = fn;
|
ctx->gc_scan_external = fn;
|
||||||
@@ -2190,6 +2171,11 @@ void JS_FreeContext (JSContext *ctx) {
|
|||||||
|
|
||||||
cell_rt_free_native_state(ctx);
|
cell_rt_free_native_state(ctx);
|
||||||
JS_DeleteGCRef(ctx, &ctx->suspended_frame_ref);
|
JS_DeleteGCRef(ctx, &ctx->suspended_frame_ref);
|
||||||
|
JS_DeleteGCRef(ctx, &ctx->idx_buffer_ref);
|
||||||
|
JS_DeleteGCRef(ctx, &ctx->on_exception_ref);
|
||||||
|
JS_DeleteGCRef(ctx, &ctx->message_handle_ref);
|
||||||
|
JS_DeleteGCRef(ctx, &ctx->unneeded_ref);
|
||||||
|
JS_DeleteGCRef(ctx, &ctx->actor_sym_ref);
|
||||||
|
|
||||||
for (i = 0; i < ctx->class_count; i++) {
|
for (i = 0; i < ctx->class_count; i++) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,14 @@
|
|||||||
|
|
||||||
#include "stb_ds.h"
|
#include "stb_ds.h"
|
||||||
#include "cell.h"
|
#include "cell.h"
|
||||||
#include "quickjs-internal.h"
|
#include "pit_internal.h"
|
||||||
#include "cell_internal.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct actor_node {
|
typedef struct actor_node {
|
||||||
cell_rt *actor;
|
JSContext *actor;
|
||||||
struct actor_node *next;
|
struct actor_node *next;
|
||||||
} actor_node;
|
} actor_node;
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@ typedef enum {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint64_t execute_at_ns;
|
uint64_t execute_at_ns;
|
||||||
cell_rt *actor;
|
JSContext *actor;
|
||||||
uint32_t timer_id;
|
uint32_t timer_id;
|
||||||
timer_type type;
|
timer_type type;
|
||||||
uint32_t turn_gen; /* generation at registration time */
|
uint32_t turn_gen; /* generation at registration time */
|
||||||
@@ -92,7 +91,7 @@ static int has_any_work(actor_node *heads[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static pthread_mutex_t *actors_mutex;
|
static pthread_mutex_t *actors_mutex;
|
||||||
static struct { char *key; cell_rt *value; } *actors = NULL;
|
static struct { char *key; JSContext *value; } *actors = NULL;
|
||||||
|
|
||||||
#define lockless_shdel(NAME, KEY) pthread_mutex_lock(NAME##_mutex); shdel(NAME, KEY); pthread_mutex_unlock(NAME##_mutex);
|
#define lockless_shdel(NAME, KEY) pthread_mutex_lock(NAME##_mutex); shdel(NAME, KEY); pthread_mutex_unlock(NAME##_mutex);
|
||||||
#define lockless_shlen(NAME) ({ \
|
#define lockless_shlen(NAME) ({ \
|
||||||
@@ -109,7 +108,7 @@ static struct { char *key; cell_rt *value; } *actors = NULL;
|
|||||||
})
|
})
|
||||||
#define lockless_shget(NAME, KEY) ({ \
|
#define lockless_shget(NAME, KEY) ({ \
|
||||||
pthread_mutex_lock(NAME##_mutex); \
|
pthread_mutex_lock(NAME##_mutex); \
|
||||||
cell_rt *_actor = shget(NAME, KEY); \
|
JSContext *_actor = shget(NAME, KEY); \
|
||||||
pthread_mutex_unlock(NAME##_mutex); \
|
pthread_mutex_unlock(NAME##_mutex); \
|
||||||
_actor; \
|
_actor; \
|
||||||
})
|
})
|
||||||
@@ -122,10 +121,10 @@ static struct { char *key; cell_rt *value; } *actors = NULL;
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
uint32_t actor_remove_cb(cell_rt *actor, uint32_t id, uint32_t interval);
|
uint32_t actor_remove_cb(JSContext *actor, uint32_t id, uint32_t interval);
|
||||||
void actor_turn(cell_rt *actor);
|
void actor_turn(JSContext *actor);
|
||||||
|
|
||||||
void heap_push(uint64_t when, cell_rt *actor, uint32_t timer_id, timer_type type) {
|
void heap_push(uint64_t when, JSContext *actor, uint32_t timer_id, timer_type type) {
|
||||||
timer_node node = { .execute_at_ns = when, .actor = actor, .timer_id = timer_id, .type = type, .turn_gen = 0 };
|
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)
|
if (type == TIMER_PAUSE || type == TIMER_KILL)
|
||||||
node.turn_gen = atomic_load_explicit(&actor->turn_gen, memory_order_relaxed);
|
node.turn_gen = atomic_load_explicit(&actor->turn_gen, memory_order_relaxed);
|
||||||
@@ -203,10 +202,10 @@ void *timer_thread_func(void *arg) {
|
|||||||
uint32_t cur = atomic_load_explicit(&t.actor->turn_gen, memory_order_relaxed);
|
uint32_t cur = atomic_load_explicit(&t.actor->turn_gen, memory_order_relaxed);
|
||||||
if (cur == t.turn_gen) {
|
if (cur == t.turn_gen) {
|
||||||
if (t.type == TIMER_PAUSE) {
|
if (t.type == TIMER_PAUSE) {
|
||||||
JS_SetPauseFlag(t.actor->context, 1);
|
JS_SetPauseFlag(t.actor, 1);
|
||||||
} else {
|
} else {
|
||||||
t.actor->disrupt = 1;
|
t.actor->disrupt = 1;
|
||||||
JS_SetPauseFlag(t.actor->context, 2);
|
JS_SetPauseFlag(t.actor, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -216,7 +215,7 @@ void *timer_thread_func(void *arg) {
|
|||||||
JSValue cb = t.actor->timers[idx].value;
|
JSValue cb = t.actor->timers[idx].value;
|
||||||
hmdel(t.actor->timers, t.timer_id);
|
hmdel(t.actor->timers, t.timer_id);
|
||||||
actor_clock(t.actor, cb);
|
actor_clock(t.actor, cb);
|
||||||
JS_FreeValue(t.actor->context, cb);
|
JS_FreeValue(t.actor, cb);
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(t.actor->msg_mutex);
|
pthread_mutex_unlock(t.actor->msg_mutex);
|
||||||
}
|
}
|
||||||
@@ -244,7 +243,7 @@ void *timer_thread_func(void *arg) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void enqueue_actor_priority(cell_rt *actor) {
|
void enqueue_actor_priority(JSContext *actor) {
|
||||||
actor_node *n = malloc(sizeof(actor_node));
|
actor_node *n = malloc(sizeof(actor_node));
|
||||||
n->actor = actor;
|
n->actor = actor;
|
||||||
n->next = NULL;
|
n->next = NULL;
|
||||||
@@ -323,7 +322,7 @@ void actor_initialize(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void actor_free(cell_rt *actor)
|
void actor_free(JSContext *actor)
|
||||||
{
|
{
|
||||||
if (actor->is_quiescent) {
|
if (actor->is_quiescent) {
|
||||||
actor->is_quiescent = 0;
|
actor->is_quiescent = 0;
|
||||||
@@ -364,16 +363,8 @@ void actor_free(cell_rt *actor)
|
|||||||
pthread_mutex_lock(actor->msg_mutex);
|
pthread_mutex_lock(actor->msg_mutex);
|
||||||
pthread_mutex_lock(actor->mutex);
|
pthread_mutex_lock(actor->mutex);
|
||||||
|
|
||||||
JSContext *js = actor->context;
|
|
||||||
|
|
||||||
JS_DeleteGCRef(js, &actor->idx_buffer_ref);
|
|
||||||
JS_DeleteGCRef(js, &actor->on_exception_ref);
|
|
||||||
JS_DeleteGCRef(js, &actor->message_handle_ref);
|
|
||||||
JS_DeleteGCRef(js, &actor->unneeded_ref);
|
|
||||||
JS_DeleteGCRef(js, &actor->actor_sym_ref);
|
|
||||||
|
|
||||||
for (int i = 0; i < hmlen(actor->timers); i++) {
|
for (int i = 0; i < hmlen(actor->timers); i++) {
|
||||||
JS_FreeValue(js, actor->timers[i].value);
|
JS_FreeValue(actor, actor->timers[i].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
hmfree(actor->timers);
|
hmfree(actor->timers);
|
||||||
@@ -383,23 +374,25 @@ void actor_free(cell_rt *actor)
|
|||||||
if (actor->letters[i].type == LETTER_BLOB) {
|
if (actor->letters[i].type == LETTER_BLOB) {
|
||||||
blob_destroy(actor->letters[i].blob_data);
|
blob_destroy(actor->letters[i].blob_data);
|
||||||
} else if (actor->letters[i].type == LETTER_CALLBACK) {
|
} else if (actor->letters[i].type == LETTER_CALLBACK) {
|
||||||
JS_FreeValue(js, actor->letters[i].callback);
|
JS_FreeValue(actor, actor->letters[i].callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
arrfree(actor->letters);
|
arrfree(actor->letters);
|
||||||
|
|
||||||
JS_FreeContext(js);
|
|
||||||
free(actor->id);
|
free(actor->id);
|
||||||
|
|
||||||
pthread_mutex_unlock(actor->mutex);
|
pthread_mutex_t *m = actor->mutex;
|
||||||
pthread_mutex_destroy(actor->mutex);
|
pthread_mutex_t *mm = actor->msg_mutex;
|
||||||
free(actor->mutex);
|
|
||||||
pthread_mutex_unlock(actor->msg_mutex);
|
|
||||||
pthread_mutex_destroy(actor->msg_mutex);
|
|
||||||
free(actor->msg_mutex);
|
|
||||||
|
|
||||||
free(actor);
|
JS_FreeContext(actor);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(m);
|
||||||
|
pthread_mutex_destroy(m);
|
||||||
|
free(m);
|
||||||
|
pthread_mutex_unlock(mm);
|
||||||
|
pthread_mutex_destroy(mm);
|
||||||
|
free(mm);
|
||||||
|
|
||||||
int actor_count = lockless_shlen(actors);
|
int actor_count = lockless_shlen(actors);
|
||||||
if (actor_count == 0) {
|
if (actor_count == 0) {
|
||||||
@@ -467,7 +460,7 @@ int actor_exists(const char *id)
|
|||||||
return idx != -1;
|
return idx != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_actor_state(cell_rt *actor)
|
void set_actor_state(JSContext *actor)
|
||||||
{
|
{
|
||||||
if (actor->disrupt) {
|
if (actor->disrupt) {
|
||||||
#ifdef SCHEDULER_DEBUG
|
#ifdef SCHEDULER_DEBUG
|
||||||
@@ -548,7 +541,7 @@ void set_actor_state(cell_rt *actor)
|
|||||||
pthread_mutex_unlock(actor->msg_mutex);
|
pthread_mutex_unlock(actor->msg_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t actor_remove_cb(cell_rt *actor, uint32_t id, uint32_t interval)
|
uint32_t actor_remove_cb(JSContext *actor, uint32_t id, uint32_t interval)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(actor->mutex);
|
pthread_mutex_lock(actor->mutex);
|
||||||
// Check if this timer is still valid (match actor->ar)
|
// Check if this timer is still valid (match actor->ar)
|
||||||
@@ -560,8 +553,8 @@ uint32_t actor_remove_cb(cell_rt *actor, uint32_t id, uint32_t interval)
|
|||||||
actor->disrupt = 1;
|
actor->disrupt = 1;
|
||||||
|
|
||||||
if (!JS_IsNull(actor->unneeded_ref.val)) {
|
if (!JS_IsNull(actor->unneeded_ref.val)) {
|
||||||
JSValue ret = JS_Call(actor->context, actor->unneeded_ref.val, JS_NULL, 0, NULL);
|
JSValue ret = JS_Call(actor, actor->unneeded_ref.val, JS_NULL, 0, NULL);
|
||||||
uncaught_exception(actor->context, ret);
|
uncaught_exception(actor, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
int should_free = (actor->state == ACTOR_IDLE);
|
int should_free = (actor->state == ACTOR_IDLE);
|
||||||
@@ -571,7 +564,7 @@ uint32_t actor_remove_cb(cell_rt *actor, uint32_t id, uint32_t interval)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void actor_unneeded(cell_rt *actor, JSValue fn, double seconds)
|
void actor_unneeded(JSContext *actor, JSValue fn, double seconds)
|
||||||
{
|
{
|
||||||
if (actor->disrupt) return;
|
if (actor->disrupt) return;
|
||||||
|
|
||||||
@@ -589,7 +582,7 @@ void actor_unneeded(cell_rt *actor, JSValue fn, double seconds)
|
|||||||
set_actor_state(actor);
|
set_actor_state(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
cell_rt *get_actor(char *id)
|
JSContext *get_actor(char *id)
|
||||||
{
|
{
|
||||||
int idx = lockless_shgeti(actors, id);
|
int idx = lockless_shgeti(actors, id);
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
@@ -622,17 +615,14 @@ void actor_loop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cell_rt *create_actor(void *wota)
|
extern JSRuntime *g_runtime;
|
||||||
|
|
||||||
|
JSContext *create_actor(void *wota)
|
||||||
{
|
{
|
||||||
cell_rt *actor = calloc(sizeof(*actor), 1);
|
JSContext *actor = JS_NewContext(g_runtime);
|
||||||
#ifdef HAVE_MIMALLOC
|
if (!actor) return NULL;
|
||||||
actor->heap = mi_heap_new();
|
|
||||||
#endif
|
|
||||||
actor->init_wota = wota;
|
actor->init_wota = wota;
|
||||||
/* GCRef fields are registered after JSContext creation in script_startup.
|
|
||||||
For now, zero-init from calloc is sufficient (val = 0 = JS_MKVAL(JS_TAG_INT,0),
|
|
||||||
which is not a pointer so GC-safe). The actual JS_NULL assignment and
|
|
||||||
JS_AddGCRef happen in script_startup. */
|
|
||||||
|
|
||||||
arrsetcap(actor->letters, 5);
|
arrsetcap(actor->letters, 5);
|
||||||
|
|
||||||
@@ -642,9 +632,12 @@ cell_rt *create_actor(void *wota)
|
|||||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||||
pthread_mutex_init(actor->mutex, &attr);
|
pthread_mutex_init(actor->mutex, &attr);
|
||||||
actor->msg_mutex = malloc(sizeof(pthread_mutex_t));
|
actor->msg_mutex = malloc(sizeof(pthread_mutex_t));
|
||||||
pthread_mutex_init(actor->msg_mutex, &attr); // msg_mutex can be recursive too to be safe
|
pthread_mutex_init(actor->msg_mutex, &attr);
|
||||||
pthread_mutexattr_destroy(&attr);
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
|
||||||
|
JS_SetGCScanExternal(actor, actor_gc_scan);
|
||||||
|
JS_SetHeapMemoryLimit(actor, ACTOR_MEMORY_LIMIT);
|
||||||
|
|
||||||
/* Lock actor->mutex while initializing JS runtime. */
|
/* Lock actor->mutex while initializing JS runtime. */
|
||||||
pthread_mutex_lock(actor->mutex);
|
pthread_mutex_lock(actor->mutex);
|
||||||
script_startup(actor);
|
script_startup(actor);
|
||||||
@@ -654,7 +647,7 @@ cell_rt *create_actor(void *wota)
|
|||||||
return actor;
|
return actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *register_actor(const char *id, cell_rt *actor, int mainthread, double ar)
|
const char *register_actor(const char *id, JSContext *actor, int mainthread, double ar)
|
||||||
{
|
{
|
||||||
actor->main_thread_only = mainthread;
|
actor->main_thread_only = mainthread;
|
||||||
actor->id = strdup(id);
|
actor->id = strdup(id);
|
||||||
@@ -676,7 +669,7 @@ const char *register_actor(const char *id, cell_rt *actor, int mainthread, doubl
|
|||||||
|
|
||||||
const char *send_message(const char *id, void *msg)
|
const char *send_message(const char *id, void *msg)
|
||||||
{
|
{
|
||||||
cell_rt *target = get_actor(id);
|
JSContext *target = get_actor(id);
|
||||||
if (!target) {
|
if (!target) {
|
||||||
blob_destroy((blob *)msg);
|
blob_destroy((blob *)msg);
|
||||||
return "Could not get actor from id.";
|
return "Could not get actor from id.";
|
||||||
@@ -698,7 +691,7 @@ const char *send_message(const char *id, void *msg)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void actor_turn(cell_rt *actor)
|
void actor_turn(JSContext *actor)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(actor->mutex);
|
pthread_mutex_lock(actor->mutex);
|
||||||
#ifdef ACTOR_TRACE
|
#ifdef ACTOR_TRACE
|
||||||
@@ -710,21 +703,21 @@ void actor_turn(cell_rt *actor)
|
|||||||
actor->name ? actor->name : actor->id, prev_state);
|
actor->name ? actor->name : actor->id, prev_state);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!actor->trace_hook) {
|
if (!actor->actor_trace_hook) {
|
||||||
cell_hook gh = cell_trace_gethook();
|
cell_hook gh = cell_trace_gethook();
|
||||||
if (gh) actor->trace_hook = gh;
|
if (gh) actor->actor_trace_hook = gh;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actor->trace_hook)
|
if (actor->actor_trace_hook)
|
||||||
actor->trace_hook(actor, CELL_HOOK_ENTER);
|
actor->actor_trace_hook(actor, CELL_HOOK_ENTER);
|
||||||
|
|
||||||
JSValue result;
|
JSValue result;
|
||||||
|
|
||||||
if (actor->vm_suspended) {
|
if (actor->vm_suspended) {
|
||||||
/* RESUME path: continue suspended turn under kill timer only */
|
/* RESUME path: continue suspended turn under kill timer only */
|
||||||
g_crash_ctx = actor->context;
|
g_crash_ctx = actor;
|
||||||
atomic_fetch_add_explicit(&actor->turn_gen, 1, memory_order_relaxed);
|
atomic_fetch_add_explicit(&actor->turn_gen, 1, memory_order_relaxed);
|
||||||
JS_SetPauseFlag(actor->context, 0);
|
JS_SetPauseFlag(actor, 0);
|
||||||
actor->turn_start_ns = cell_ns();
|
actor->turn_start_ns = cell_ns();
|
||||||
|
|
||||||
/* Register kill timer only for resume */
|
/* Register kill timer only for resume */
|
||||||
@@ -734,7 +727,7 @@ void actor_turn(cell_rt *actor)
|
|||||||
pthread_cond_signal(&engine.timer_cond);
|
pthread_cond_signal(&engine.timer_cond);
|
||||||
pthread_mutex_unlock(&engine.lock);
|
pthread_mutex_unlock(&engine.lock);
|
||||||
|
|
||||||
result = JS_ResumeRegisterVM(actor->context);
|
result = JS_ResumeRegisterVM(actor);
|
||||||
actor->vm_suspended = 0;
|
actor->vm_suspended = 0;
|
||||||
|
|
||||||
if (JS_IsSuspended(result)) {
|
if (JS_IsSuspended(result)) {
|
||||||
@@ -743,7 +736,7 @@ void actor_turn(cell_rt *actor)
|
|||||||
goto ENDTURN;
|
goto ENDTURN;
|
||||||
}
|
}
|
||||||
if (JS_IsException(result)) {
|
if (JS_IsException(result)) {
|
||||||
if (!uncaught_exception(actor->context, result))
|
if (!uncaught_exception(actor, result))
|
||||||
actor->disrupt = 1;
|
actor->disrupt = 1;
|
||||||
}
|
}
|
||||||
actor->slow_strikes++;
|
actor->slow_strikes++;
|
||||||
@@ -778,7 +771,7 @@ void actor_turn(cell_rt *actor)
|
|||||||
pthread_mutex_unlock(actor->msg_mutex);
|
pthread_mutex_unlock(actor->msg_mutex);
|
||||||
|
|
||||||
atomic_fetch_add_explicit(&actor->turn_gen, 1, memory_order_relaxed);
|
atomic_fetch_add_explicit(&actor->turn_gen, 1, memory_order_relaxed);
|
||||||
JS_SetPauseFlag(actor->context, 0);
|
JS_SetPauseFlag(actor, 0);
|
||||||
actor->turn_start_ns = cell_ns();
|
actor->turn_start_ns = cell_ns();
|
||||||
|
|
||||||
/* Register both pause and kill timers */
|
/* Register both pause and kill timers */
|
||||||
@@ -790,35 +783,35 @@ void actor_turn(cell_rt *actor)
|
|||||||
pthread_cond_signal(&engine.timer_cond);
|
pthread_cond_signal(&engine.timer_cond);
|
||||||
pthread_mutex_unlock(&engine.lock);
|
pthread_mutex_unlock(&engine.lock);
|
||||||
|
|
||||||
g_crash_ctx = actor->context;
|
g_crash_ctx = actor;
|
||||||
|
|
||||||
if (l.type == LETTER_BLOB) {
|
if (l.type == LETTER_BLOB) {
|
||||||
size_t size = blob_length(l.blob_data) / 8;
|
size_t size = blob_length(l.blob_data) / 8;
|
||||||
JSValue arg = js_new_blob_stoned_copy(actor->context,
|
JSValue arg = js_new_blob_stoned_copy(actor,
|
||||||
(void *)blob_data(l.blob_data), size);
|
(void *)blob_data(l.blob_data), size);
|
||||||
blob_destroy(l.blob_data);
|
blob_destroy(l.blob_data);
|
||||||
result = JS_Call(actor->context, actor->message_handle_ref.val,
|
result = JS_Call(actor, actor->message_handle_ref.val,
|
||||||
JS_NULL, 1, &arg);
|
JS_NULL, 1, &arg);
|
||||||
if (JS_IsSuspended(result)) {
|
if (JS_IsSuspended(result)) {
|
||||||
actor->vm_suspended = 1;
|
actor->vm_suspended = 1;
|
||||||
actor->state = ACTOR_SLOW;
|
actor->state = ACTOR_SLOW;
|
||||||
JS_FreeValue(actor->context, arg);
|
JS_FreeValue(actor, arg);
|
||||||
goto ENDTURN_SLOW;
|
goto ENDTURN_SLOW;
|
||||||
}
|
}
|
||||||
if (!uncaught_exception(actor->context, result))
|
if (!uncaught_exception(actor, result))
|
||||||
actor->disrupt = 1;
|
actor->disrupt = 1;
|
||||||
JS_FreeValue(actor->context, arg);
|
JS_FreeValue(actor, arg);
|
||||||
} else if (l.type == LETTER_CALLBACK) {
|
} else if (l.type == LETTER_CALLBACK) {
|
||||||
result = JS_Call(actor->context, l.callback, JS_NULL, 0, NULL);
|
result = JS_Call(actor, l.callback, JS_NULL, 0, NULL);
|
||||||
if (JS_IsSuspended(result)) {
|
if (JS_IsSuspended(result)) {
|
||||||
actor->vm_suspended = 1;
|
actor->vm_suspended = 1;
|
||||||
actor->state = ACTOR_SLOW;
|
actor->state = ACTOR_SLOW;
|
||||||
JS_FreeValue(actor->context, l.callback);
|
JS_FreeValue(actor, l.callback);
|
||||||
goto ENDTURN_SLOW;
|
goto ENDTURN_SLOW;
|
||||||
}
|
}
|
||||||
if (!uncaught_exception(actor->context, result))
|
if (!uncaught_exception(actor, result))
|
||||||
actor->disrupt = 1;
|
actor->disrupt = 1;
|
||||||
JS_FreeValue(actor->context, l.callback);
|
JS_FreeValue(actor, l.callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actor->disrupt) goto ENDTURN;
|
if (actor->disrupt) goto ENDTURN;
|
||||||
@@ -830,8 +823,8 @@ ENDTURN:
|
|||||||
atomic_fetch_add_explicit(&actor->turn_gen, 1, memory_order_relaxed);
|
atomic_fetch_add_explicit(&actor->turn_gen, 1, memory_order_relaxed);
|
||||||
actor->state = ACTOR_IDLE;
|
actor->state = ACTOR_IDLE;
|
||||||
|
|
||||||
if (actor->trace_hook)
|
if (actor->actor_trace_hook)
|
||||||
actor->trace_hook(actor, CELL_HOOK_EXIT);
|
actor->actor_trace_hook(actor, CELL_HOOK_EXIT);
|
||||||
|
|
||||||
if (actor->disrupt) {
|
if (actor->disrupt) {
|
||||||
#ifdef SCHEDULER_DEBUG
|
#ifdef SCHEDULER_DEBUG
|
||||||
@@ -857,8 +850,8 @@ ENDTURN_SLOW:
|
|||||||
fprintf(stderr, "[ACTOR_TRACE] %s: suspended mid-turn -> SLOW\n",
|
fprintf(stderr, "[ACTOR_TRACE] %s: suspended mid-turn -> SLOW\n",
|
||||||
actor->name ? actor->name : actor->id);
|
actor->name ? actor->name : actor->id);
|
||||||
#endif
|
#endif
|
||||||
if (actor->trace_hook)
|
if (actor->actor_trace_hook)
|
||||||
actor->trace_hook(actor, CELL_HOOK_EXIT);
|
actor->actor_trace_hook(actor, CELL_HOOK_EXIT);
|
||||||
enqueue_actor_priority(actor);
|
enqueue_actor_priority(actor);
|
||||||
pthread_mutex_unlock(actor->mutex);
|
pthread_mutex_unlock(actor->mutex);
|
||||||
}
|
}
|
||||||
@@ -869,49 +862,48 @@ void actor_gc_scan(JSContext *ctx,
|
|||||||
uint8_t *from_base, uint8_t *from_end,
|
uint8_t *from_base, uint8_t *from_end,
|
||||||
uint8_t *to_base, uint8_t **to_free, uint8_t *to_end)
|
uint8_t *to_base, uint8_t **to_free, uint8_t *to_end)
|
||||||
{
|
{
|
||||||
cell_rt *actor = JS_GetContextOpaque(ctx);
|
|
||||||
if (!actor) return;
|
|
||||||
|
|
||||||
/* Lock msg_mutex to synchronize with the timer thread, which reads
|
/* Lock msg_mutex to synchronize with the timer thread, which reads
|
||||||
timers and writes letters under the same lock. */
|
timers and writes letters under the same lock. */
|
||||||
pthread_mutex_lock(actor->msg_mutex);
|
if (ctx->msg_mutex)
|
||||||
|
pthread_mutex_lock(ctx->msg_mutex);
|
||||||
|
|
||||||
for (int i = 0; i < arrlen(actor->letters); i++) {
|
for (int i = 0; i < arrlen(ctx->letters); i++) {
|
||||||
if (actor->letters[i].type == LETTER_CALLBACK) {
|
if (ctx->letters[i].type == LETTER_CALLBACK) {
|
||||||
actor->letters[i].callback = gc_copy_value(ctx,
|
ctx->letters[i].callback = gc_copy_value(ctx,
|
||||||
actor->letters[i].callback,
|
ctx->letters[i].callback,
|
||||||
from_base, from_end, to_base, to_free, to_end);
|
from_base, from_end, to_base, to_free, to_end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < hmlen(actor->timers); i++) {
|
for (int i = 0; i < hmlen(ctx->timers); i++) {
|
||||||
actor->timers[i].value = gc_copy_value(ctx,
|
ctx->timers[i].value = gc_copy_value(ctx,
|
||||||
actor->timers[i].value,
|
ctx->timers[i].value,
|
||||||
from_base, from_end, to_base, to_free, to_end);
|
from_base, from_end, to_base, to_free, to_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(actor->msg_mutex);
|
if (ctx->msg_mutex)
|
||||||
|
pthread_mutex_unlock(ctx->msg_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void actor_clock(cell_rt *actor, JSValue fn)
|
void actor_clock(JSContext *actor, JSValue fn)
|
||||||
{
|
{
|
||||||
letter l;
|
letter l;
|
||||||
l.type = LETTER_CALLBACK;
|
l.type = LETTER_CALLBACK;
|
||||||
l.callback = JS_DupValue(actor->context, fn);
|
l.callback = JS_DupValue(actor, fn);
|
||||||
pthread_mutex_lock(actor->msg_mutex);
|
pthread_mutex_lock(actor->msg_mutex);
|
||||||
arrput(actor->letters, l);
|
arrput(actor->letters, l);
|
||||||
pthread_mutex_unlock(actor->msg_mutex);
|
pthread_mutex_unlock(actor->msg_mutex);
|
||||||
set_actor_state(actor);
|
set_actor_state(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t actor_delay(cell_rt *actor, JSValue fn, double seconds)
|
uint32_t actor_delay(JSContext *actor, JSValue fn, double seconds)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(actor->msg_mutex);
|
pthread_mutex_lock(actor->msg_mutex);
|
||||||
|
|
||||||
static uint32_t global_timer_id = 1;
|
static uint32_t global_timer_id = 1;
|
||||||
uint32_t id = global_timer_id++;
|
uint32_t id = global_timer_id++;
|
||||||
|
|
||||||
JSValue cb = JS_DupValue(actor->context, fn);
|
JSValue cb = JS_DupValue(actor, fn);
|
||||||
hmput(actor->timers, id, cb);
|
hmput(actor->timers, id, cb);
|
||||||
|
|
||||||
uint64_t now = cell_ns();
|
uint64_t now = cell_ns();
|
||||||
@@ -928,7 +920,7 @@ uint32_t actor_delay(cell_rt *actor, JSValue fn, double seconds)
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue actor_remove_timer(cell_rt *actor, uint32_t timer_id)
|
JSValue actor_remove_timer(JSContext *actor, uint32_t timer_id)
|
||||||
{
|
{
|
||||||
JSValue cb = JS_NULL;
|
JSValue cb = JS_NULL;
|
||||||
pthread_mutex_lock(actor->msg_mutex);
|
pthread_mutex_lock(actor->msg_mutex);
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
#include "stb_ds.h"
|
#include "stb_ds.h"
|
||||||
#include "cell.h"
|
#include "cell.h"
|
||||||
#include "cell_internal.h"
|
#include "pit_internal.h"
|
||||||
|
|
||||||
// --- Data Structures ---
|
// --- Data Structures ---
|
||||||
|
|
||||||
// Simple linked list for the ready queue
|
// Simple linked list for the ready queue
|
||||||
typedef struct actor_node {
|
typedef struct actor_node {
|
||||||
cell_rt *actor;
|
JSContext *actor;
|
||||||
struct actor_node *next;
|
struct actor_node *next;
|
||||||
} actor_node;
|
} actor_node;
|
||||||
|
|
||||||
// Timer node for the min-heap
|
// Timer node for the min-heap
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint64_t execute_at_ns;
|
uint64_t execute_at_ns;
|
||||||
cell_rt *actor;
|
JSContext *actor;
|
||||||
uint32_t timer_id;
|
uint32_t timer_id;
|
||||||
int is_native; // 1 for native remove timer, 0 for JS timer
|
int is_native; // 1 for native remove timer, 0 for JS timer
|
||||||
} timer_node;
|
} timer_node;
|
||||||
@@ -24,18 +24,18 @@ static actor_node *ready_head = NULL;
|
|||||||
static actor_node *ready_tail = NULL;
|
static actor_node *ready_tail = NULL;
|
||||||
static timer_node *timer_heap = NULL; // stb_ds array
|
static timer_node *timer_heap = NULL; // stb_ds array
|
||||||
|
|
||||||
static struct { char *key; cell_rt *value; } *actors = NULL; // stb_ds hashmap
|
static struct { char *key; JSContext *value; } *actors = NULL; // stb_ds hashmap
|
||||||
|
|
||||||
static int shutting_down = 0;
|
static int shutting_down = 0;
|
||||||
|
|
||||||
// --- Forward Declarations ---
|
// --- Forward Declarations ---
|
||||||
|
|
||||||
void actor_turn(cell_rt *actor);
|
void actor_turn(JSContext *actor);
|
||||||
uint32_t actor_remove_cb(cell_rt *actor, uint32_t id, uint32_t interval);
|
uint32_t actor_remove_cb(JSContext *actor, uint32_t id, uint32_t interval);
|
||||||
|
|
||||||
// --- Heap Helpers ---
|
// --- Heap Helpers ---
|
||||||
|
|
||||||
static void heap_push(uint64_t when, cell_rt *actor, uint32_t timer_id, int is_native) {
|
static void heap_push(uint64_t when, JSContext *actor, uint32_t timer_id, int is_native) {
|
||||||
timer_node node = { .execute_at_ns = when, .actor = actor, .timer_id = timer_id, .is_native = is_native };
|
timer_node node = { .execute_at_ns = when, .actor = actor, .timer_id = timer_id, .is_native = is_native };
|
||||||
arrput(timer_heap, node);
|
arrput(timer_heap, node);
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ void actor_initialize(void)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void actor_free(cell_rt *actor)
|
void actor_free(JSContext *actor)
|
||||||
{
|
{
|
||||||
shdel(actors, actor->id);
|
shdel(actors, actor->id);
|
||||||
|
|
||||||
@@ -116,17 +116,8 @@ void actor_free(cell_rt *actor)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JSContext *js = actor->context;
|
|
||||||
|
|
||||||
JS_DeleteGCRef(js, &actor->idx_buffer_ref);
|
|
||||||
JS_DeleteGCRef(js, &actor->on_exception_ref);
|
|
||||||
JS_DeleteGCRef(js, &actor->message_handle_ref);
|
|
||||||
JS_DeleteGCRef(js, &actor->unneeded_ref);
|
|
||||||
JS_DeleteGCRef(js, &actor->actor_sym_ref);
|
|
||||||
|
|
||||||
/* Free timer callbacks stored in actor */
|
|
||||||
for (int i = 0; i < hmlen(actor->timers); i++) {
|
for (int i = 0; i < hmlen(actor->timers); i++) {
|
||||||
JS_FreeValue(js, actor->timers[i].value);
|
JS_FreeValue(actor, actor->timers[i].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
hmfree(actor->timers);
|
hmfree(actor->timers);
|
||||||
@@ -136,22 +127,21 @@ void actor_free(cell_rt *actor)
|
|||||||
if (actor->letters[i].type == LETTER_BLOB) {
|
if (actor->letters[i].type == LETTER_BLOB) {
|
||||||
blob_destroy(actor->letters[i].blob_data);
|
blob_destroy(actor->letters[i].blob_data);
|
||||||
} else if (actor->letters[i].type == LETTER_CALLBACK) {
|
} else if (actor->letters[i].type == LETTER_CALLBACK) {
|
||||||
JS_FreeValue(js, actor->letters[i].callback);
|
JS_FreeValue(actor, actor->letters[i].callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
arrfree(actor->letters);
|
arrfree(actor->letters);
|
||||||
|
|
||||||
JS_FreeContext(js);
|
|
||||||
free(actor->id);
|
free(actor->id);
|
||||||
|
|
||||||
free(actor);
|
JS_FreeContext(actor);
|
||||||
|
|
||||||
int actor_count = shlen(actors);
|
int actor_count = shlen(actors);
|
||||||
if (actor_count == 0) exit(0);
|
if (actor_count == 0) exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void actor_unneeded(cell_rt *actor, JSValue fn, double seconds)
|
void actor_unneeded(JSContext *actor, JSValue fn, double seconds)
|
||||||
{
|
{
|
||||||
if (actor->disrupt) return;
|
if (actor->disrupt) return;
|
||||||
|
|
||||||
@@ -164,12 +154,7 @@ void actor_unneeded(cell_rt *actor, JSValue fn, double seconds)
|
|||||||
actor->ar_secs = seconds;
|
actor->ar_secs = seconds;
|
||||||
|
|
||||||
END:
|
END:
|
||||||
// If there was an existing unneeded timer, it will be handled/ignored in set_actor_state logic
|
|
||||||
// or we can explicitly invalidate it if we tracked the ID.
|
|
||||||
// For now, set_actor_state will schedule a new one if idle.
|
|
||||||
if (actor->ar) {
|
if (actor->ar) {
|
||||||
// In single threaded, we can't easily remove from heap O(N),
|
|
||||||
// but we can just let it fire and check ID match.
|
|
||||||
actor->ar = 0;
|
actor->ar = 0;
|
||||||
}
|
}
|
||||||
set_actor_state(actor);
|
set_actor_state(actor);
|
||||||
@@ -195,7 +180,7 @@ int actor_exists(const char *id)
|
|||||||
return idx != -1;
|
return idx != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_actor_state(cell_rt *actor)
|
void set_actor_state(JSContext *actor)
|
||||||
{
|
{
|
||||||
if (actor->disrupt) {
|
if (actor->disrupt) {
|
||||||
actor_free(actor);
|
actor_free(actor);
|
||||||
@@ -244,7 +229,7 @@ void set_actor_state(cell_rt *actor)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t actor_remove_cb(cell_rt *actor, uint32_t id, uint32_t interval)
|
uint32_t actor_remove_cb(JSContext *actor, uint32_t id, uint32_t interval)
|
||||||
{
|
{
|
||||||
// Check if this timer is still valid (match actor->ar)
|
// Check if this timer is still valid (match actor->ar)
|
||||||
if (actor->ar != id && id != 0) {
|
if (actor->ar != id && id != 0) {
|
||||||
@@ -254,8 +239,8 @@ uint32_t actor_remove_cb(cell_rt *actor, uint32_t id, uint32_t interval)
|
|||||||
actor->disrupt = 1;
|
actor->disrupt = 1;
|
||||||
|
|
||||||
if (!JS_IsNull(actor->unneeded_ref.val)) {
|
if (!JS_IsNull(actor->unneeded_ref.val)) {
|
||||||
JSValue ret = JS_Call(actor->context, actor->unneeded_ref.val, JS_NULL, 0, NULL);
|
JSValue ret = JS_Call(actor, actor->unneeded_ref.val, JS_NULL, 0, NULL);
|
||||||
uncaught_exception(actor->context, ret);
|
uncaught_exception(actor, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
int should_free = (actor->state == ACTOR_IDLE);
|
int should_free = (actor->state == ACTOR_IDLE);
|
||||||
@@ -264,7 +249,7 @@ uint32_t actor_remove_cb(cell_rt *actor, uint32_t id, uint32_t interval)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cell_rt *get_actor(char *id)
|
JSContext *get_actor(char *id)
|
||||||
{
|
{
|
||||||
int idx = shgeti(actors, id);
|
int idx = shgeti(actors, id);
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
@@ -304,7 +289,7 @@ void actor_loop()
|
|||||||
JSValue cb = t.actor->timers[idx].value;
|
JSValue cb = t.actor->timers[idx].value;
|
||||||
hmdel(t.actor->timers, t.timer_id);
|
hmdel(t.actor->timers, t.timer_id);
|
||||||
actor_clock(t.actor, cb);
|
actor_clock(t.actor, cb);
|
||||||
JS_FreeValue(t.actor->context, cb);
|
JS_FreeValue(t.actor, cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue; // Loop again
|
continue; // Loop again
|
||||||
@@ -320,17 +305,19 @@ void actor_loop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cell_rt *create_actor(void *wota)
|
extern JSRuntime *g_runtime;
|
||||||
|
|
||||||
|
JSContext *create_actor(void *wota)
|
||||||
{
|
{
|
||||||
cell_rt *actor = calloc(sizeof(*actor), 1);
|
JSContext *actor = JS_NewContext(g_runtime);
|
||||||
|
if (!actor) return NULL;
|
||||||
|
|
||||||
actor->init_wota = wota;
|
actor->init_wota = wota;
|
||||||
/* GCRef fields are registered after JSContext creation in script_startup. */
|
|
||||||
|
|
||||||
arrsetcap(actor->letters, 5);
|
arrsetcap(actor->letters, 5);
|
||||||
|
|
||||||
// No mutexes needed
|
JS_SetGCScanExternal(actor, actor_gc_scan);
|
||||||
actor->mutex = NULL;
|
JS_SetHeapMemoryLimit(actor, ACTOR_MEMORY_LIMIT);
|
||||||
actor->msg_mutex = NULL;
|
|
||||||
|
|
||||||
script_startup(actor);
|
script_startup(actor);
|
||||||
set_actor_state(actor);
|
set_actor_state(actor);
|
||||||
@@ -338,7 +325,7 @@ cell_rt *create_actor(void *wota)
|
|||||||
return actor;
|
return actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *register_actor(const char *id, cell_rt *actor, int mainthread, double ar)
|
const char *register_actor(const char *id, JSContext *actor, int mainthread, double ar)
|
||||||
{
|
{
|
||||||
actor->main_thread_only = mainthread;
|
actor->main_thread_only = mainthread;
|
||||||
actor->id = strdup(id);
|
actor->id = strdup(id);
|
||||||
@@ -355,7 +342,7 @@ const char *register_actor(const char *id, cell_rt *actor, int mainthread, doubl
|
|||||||
|
|
||||||
const char *send_message(const char *id, void *msg)
|
const char *send_message(const char *id, void *msg)
|
||||||
{
|
{
|
||||||
cell_rt *target = get_actor(id);
|
JSContext *target = get_actor(id);
|
||||||
if (!target) {
|
if (!target) {
|
||||||
blob_destroy((blob *)msg);
|
blob_destroy((blob *)msg);
|
||||||
return "Could not get actor from id.";
|
return "Could not get actor from id.";
|
||||||
@@ -377,7 +364,7 @@ const char *send_message(const char *id, void *msg)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void actor_turn(cell_rt *actor)
|
void actor_turn(JSContext *actor)
|
||||||
{
|
{
|
||||||
|
|
||||||
actor->state = ACTOR_RUNNING;
|
actor->state = ACTOR_RUNNING;
|
||||||
@@ -394,15 +381,15 @@ void actor_turn(cell_rt *actor)
|
|||||||
if (l.type == LETTER_BLOB) {
|
if (l.type == LETTER_BLOB) {
|
||||||
// Create a JS blob from the C blob
|
// Create a JS blob from the C blob
|
||||||
size_t size = blob_length(l.blob_data) / 8; // Convert bits to bytes
|
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);
|
JSValue arg = js_new_blob_stoned_copy(actor, (void *)blob_data(l.blob_data), size);
|
||||||
blob_destroy(l.blob_data);
|
blob_destroy(l.blob_data);
|
||||||
result = JS_Call(actor->context, actor->message_handle_ref.val, JS_NULL, 1, &arg);
|
result = JS_Call(actor, actor->message_handle_ref.val, JS_NULL, 1, &arg);
|
||||||
uncaught_exception(actor->context, result);
|
uncaught_exception(actor, result);
|
||||||
JS_FreeValue(actor->context, arg);
|
JS_FreeValue(actor, arg);
|
||||||
} else if (l.type == LETTER_CALLBACK) {
|
} else if (l.type == LETTER_CALLBACK) {
|
||||||
result = JS_Call(actor->context, l.callback, JS_NULL, 0, NULL);
|
result = JS_Call(actor, l.callback, JS_NULL, 0, NULL);
|
||||||
uncaught_exception(actor->context, result);
|
uncaught_exception(actor, result);
|
||||||
JS_FreeValue(actor->context, l.callback);
|
JS_FreeValue(actor, l.callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actor->disrupt) goto ENDTURN;
|
if (actor->disrupt) goto ENDTURN;
|
||||||
@@ -414,21 +401,21 @@ void actor_turn(cell_rt *actor)
|
|||||||
set_actor_state(actor);
|
set_actor_state(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void actor_clock(cell_rt *actor, JSValue fn)
|
void actor_clock(JSContext *actor, JSValue fn)
|
||||||
{
|
{
|
||||||
letter l;
|
letter l;
|
||||||
l.type = LETTER_CALLBACK;
|
l.type = LETTER_CALLBACK;
|
||||||
l.callback = JS_DupValue(actor->context, fn);
|
l.callback = JS_DupValue(actor, fn);
|
||||||
arrput(actor->letters, l);
|
arrput(actor->letters, l);
|
||||||
set_actor_state(actor);
|
set_actor_state(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t actor_delay(cell_rt *actor, JSValue fn, double seconds)
|
uint32_t actor_delay(JSContext *actor, JSValue fn, double seconds)
|
||||||
{
|
{
|
||||||
static uint32_t global_timer_id = 1;
|
static uint32_t global_timer_id = 1;
|
||||||
uint32_t id = global_timer_id++;
|
uint32_t id = global_timer_id++;
|
||||||
|
|
||||||
JSValue cb = JS_DupValue(actor->context, fn);
|
JSValue cb = JS_DupValue(actor, fn);
|
||||||
hmput(actor->timers, id, cb);
|
hmput(actor->timers, id, cb);
|
||||||
|
|
||||||
uint64_t now = cell_ns();
|
uint64_t now = cell_ns();
|
||||||
@@ -439,7 +426,7 @@ uint32_t actor_delay(cell_rt *actor, JSValue fn, double seconds)
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue actor_remove_timer(cell_rt *actor, uint32_t timer_id)
|
JSValue actor_remove_timer(JSContext *actor, uint32_t timer_id)
|
||||||
{
|
{
|
||||||
JSValue cb = JS_NULL;
|
JSValue cb = JS_NULL;
|
||||||
int id = hmgeti(actor->timers, timer_id);
|
int id = hmgeti(actor->timers, timer_id);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "cell.h"
|
#include "cell.h"
|
||||||
#include "quickjs-internal.h"
|
#include "pit_internal.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|||||||
Reference in New Issue
Block a user