Invert event loop

This commit is contained in:
2026-10-08 08:53:11 +00:00
parent c4d0bab3d9
commit 2f21347088
12 changed files with 142 additions and 33 deletions
+20 -5
View File
@@ -2,9 +2,10 @@
// io_uring (see uring.h)
//
// A `__native_io_queue_<op>` queues an op and gives its handle. It makes no
// syscall. `__native_io_wait` puts queued ops into the SQ, submits them,
// waits until one op is done, and gives its handle. The getters then read
// that op's result, until the next wait
// syscall, and no native waits. The host's loop (`main` below) puts queued
// ops into the SQ, submits them, waits until one op is done, and gives its
// handle to `jsrt_complete`. The getters read that op's result while
// `jsrt_complete` runs
//
// At most `cq_entries` ops are in flight, so the CQ cannot overflow. The rest
// stay queued until a later wait. So a queue never fails, and the program
@@ -392,8 +393,8 @@ int32_t __native_io_queue_cancel(int32_t target) {
}
// The handle of an op that is done, or -1 if no op is queued or in flight.
// The op that the last wait gave is freed now
int32_t __native_io_wait(void) {
// The op that the last call gave is freed now
static int32_t jsrt_io_next(void) {
if (jsrt_io.given >= 0) {
jsrt_io_release(jsrt_io.given);
jsrt_io.given = -1;
@@ -452,4 +453,18 @@ double __native_io_result_number(int32_t h) {
return op->res;
}
#ifndef JSRT_NO_HOST
// The Linux flavor's host: run the program, then give it each op as it
// completes, until none is left
int main(void) {
jsrt_start();
for (int32_t h; (h = jsrt_io_next()) >= 0;) {
jsrt_complete(h);
}
return 0;
}
#endif
#endif // JSRT_IMPLEMENTATION
+15 -1
View File
@@ -10,7 +10,15 @@
// str.h strings and the string natives
// number.h the number natives
// any.h any_t and JS's operators on it
// io.h IO natives, async on io_uring (uring.h)
// io.h IO natives, async on io_uring (uring.h), and the host's loop
//
// The host owns the event loop. The program exports two fns, which the host
// calls, and a flavor of the runtime is the host. This one, for Linux, is the
// `main` in io.h: define JSRT_NO_HOST to bring another
//
// jsrt_start() runs the program's top level, then its microtasks
// jsrt_complete(h) op `h` is done: runs its callback, then the microtasks.
// The op's result can be read only until this returns
#pragma once
// io.h needs Linux's syscall() and mmap(), which -std=c11 hides. This must
@@ -20,6 +28,12 @@
#endif
#include "core.h"
#include <stdint.h>
void jsrt_start(void);
void jsrt_complete(int32_t h);
#include "str.h"
#include "number.h"
#include "any.h"
+3 -2
View File
@@ -1,2 +1,3 @@
// Runs after the program: the event loop (see loop.js)
__run_loop();
// Runs after the program's top level: its microtasks. The host then runs
// the event loop (see loop.js)
__drain_microtasks();
+13 -16
View File
@@ -1,8 +1,8 @@
// The event loop. A macrotask is an IO op or a timer, which completes in C
// (see lib/rt/io.h): `__native_io_wait()` gives the handle of the next one
// that completes. A microtask is a promise job, in `__microtasks`. After the
// program, end.js runs the loop: all microtasks, then one macrotask, then all
// microtasks again, until nothing is pending
// The event loop's JS side. The host owns the loop (see lib/rt/jsrt.h). A
// macrotask is an IO op or a timer, which the host runs: it calls
// `__complete(h)` when op `h` is done. A microtask is a promise job, in
// `__microtasks`. After the program's top level, end.js runs all microtasks,
// and after each macrotask, `__complete` does
// What to do when IO op `h` completes: `__pending[h]`, a fn that gets `h`.
// Handles are small ints, and a handle is used again after its op completes
@@ -32,15 +32,12 @@ function __drain_microtasks() {
q.head = 0;
}
function __run_loop() {
for (;;) {
__drain_microtasks();
const h = __native_io_wait();
if (h < 0) return;
const done = __pending[h];
__pending[h] = undefined;
if (done !== undefined) done(h);
}
// What the host calls when op `h` is done: its callback, then the microtasks
function __complete(h) {
const done = __pending[h];
__pending[h] = undefined;
if (done !== undefined) done(h);
__drain_microtasks();
}
__link_host_complete(__complete);