Runtime Lifecycle
Every qzjs program follows the same lifecycle: create → use → destroy.
Creating a Runtime
c
qz_config_t config = {
.initial_script = "postMessage('ready');", // eval'd on qzjs's thread at create
.message_cb = on_message, // outbound messages
.debug = 0, // Enable debug output (0 or 1)
};
qz_t *rt = qz_create(&config);
if (!rt) {
// Creation failed — initial_script threw, or thread/loop init failed
}qz_create does the following:
- Starts qzjs's internal thread and initializes the embedded libuv loop
- Creates the
JSRuntimeand initial context - Registers the build-time extension set (the
QZ_EXTENSIONStable — built-ins like compress/crypto/textcodec/wamr when theirQZ_WITH_*is on, plus any user extensions added viaQZ_EXTRA_SOURCES) - Injects the WinterTC-compatible runtime into the initial context
- Eval's
initial_scripton the internal thread — a throw makesqz_createreturnNULL
qz_create blocks until the internal thread is ready and initial_script has been eval'd. The runtime owns all of its resources. to keep alive.
Destroying a Runtime
c
qz_destroy(rt); // graceful shutdown, host thread only, NULL-safeqz_destroy:
- Requests the internal thread to exit and joins it
- Destroys all contexts (calls extension
destroyhooks) - Frees the
JSRuntimeand the embedded libuv loop - Frees the runtime
qz_destroy(NULL) is safe (no-op).
Thread Safety
- All JS runs on qzjs's internal thread — the host thread never calls into JS
qz_post_messageis thread-safe — call it from any thread; the JSON is copiedmessage_cbfires on the qzjs thread — your callback must be thread-safeqz_destroyis host-thread-only — call it from the thread that calledqz_create
Memory Model
- All per-runtime state lives on
qz_t— there is zero mutable file-scope state - Class IDs are runtime-scoped (shared across contexts within one
qz_t) - Recover
qz_t*from aJSContext*viaqz_get_rt_from_ctx(ctx)(internal)