Skip to content

Event Loop ​

qzjs owns its own event loop on an internal thread. and no host-driven loop — the host does not pump anything.

Who Runs the Loop ​

qz_create starts a dedicated internal thread (uv_thread_t) that runs a libuv loop (uv_loop_t embedded in the runtime). That loop drives all async work — HTTP, file I/O, timers — and all JS runs on the same thread, so Promise microtasks are flushed naturally between loop iterations. The host thread never touches the loop.

How the Host Drives Work ​

The host cannot eval and does not tick. It drives the runtime by posting JSON messages and receiving replies:

c
#include <qzjs/qzjs.h>
#include <stdio.h>

static void on_message(qz_t *rt, const char *json, size_t len, void *data) {
    (void)rt; (void)data;
    printf("received: %.*s\n", (int)len, json);
}

int main(void) {
    qz_config_t cfg = {0};
    cfg.initial_script =
        "globalThis.onmessage = function (e) { postMessage('pong'); };";
    cfg.message_cb = on_message;
    qz_t *rt = qz_create(&cfg);
    if (!rt) return 1;

    qz_post_message(rt, "{\"cmd\":\"ping\"}", 14);
    // on_message fires on the qzjs thread when the reply is ready.
    // The host is free to do its own work meanwhile — never blocked by qzjs.

    qz_destroy(rt);
    return 0;
}

qz_post_message is thread-safe (the JSON is copied), so it may be called from any thread. message_cb fires on the qzjs thread — your callback must be thread-safe.

Why This Design ​

  • qzjs runs itself, and the host thread stays free for its own work.
  • All async events and JS callbacks are serialized on qzjs's single internal thread — no locks, no races inside the runtime.
  • Microtasks are flushed automatically between loop iterations.

MIT Licensed