Invert event loop
This commit is contained in:
@@ -97,6 +97,36 @@ impl<'a> Emitter<'a> {
|
|||||||
let value = operand(self, FIRST_ARG as usize, self.rep(&self.cache.protos[kind as usize]));
|
let value = operand(self, FIRST_ARG as usize, self.rep(&self.cache.protos[kind as usize]));
|
||||||
(format!("({} = {value}, (undefined_t){{0}})", proto_global(kind)), Type::Undefined)
|
(format!("({} = {value}, (undefined_t){{0}})", proto_global(kind)), Type::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keeps the fn where `jsrt_complete` finds it
|
||||||
|
Code::HostLink => {
|
||||||
|
let value = operand(self, FIRST_ARG as usize, self.rep(&self.cache.host_complete));
|
||||||
|
(format!("({HOST_COMPLETE_GLOBAL} = {value}, (undefined_t){{0}})"), Type::Undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The body of `jsrt_complete(h)`: a call of the fn that the stdlib links
|
||||||
|
/// for the host, with op `h` (see `infer::host_calls()`)
|
||||||
|
pub(super) fn host_complete(&mut self) -> String {
|
||||||
|
let ty = self.cache.host_complete.clone();
|
||||||
|
let mut out = String::new();
|
||||||
|
|
||||||
|
if ty == Type::Unknown {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rep = self.rep(&ty);
|
||||||
|
let operands = [(HOST_COMPLETE_GLOBAL.to_string(), rep), ("(undefined_t){0}".into(), Rep::Undefined), ("h".into(), Rep::Int)];
|
||||||
|
|
||||||
|
self.dispatch(HOST_COMPLETE_GLOBAL, &ty, "jsrt_type_error();", &mut out, |e, member, callee, rep| {
|
||||||
|
let Type::Obj(cell) = member else { return None };
|
||||||
|
let mut operands = operands.clone();
|
||||||
|
operands[0] = (callee.into(), rep);
|
||||||
|
let arg_types = [member.clone(), Type::Undefined, Type::Int];
|
||||||
|
Some(format!("{};", e.call_code(cell.code()?, HOST_COMPLETE, &arg_types, &operands).0))
|
||||||
|
});
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ pub(super) fn cell_sig(cell: &Cell, class: &HashMap<Cell, CellNum>, sig: &mut Ve
|
|||||||
Code::Fn(id) => [1, id as u32],
|
Code::Fn(id) => [1, id as u32],
|
||||||
Code::Native(id) => [2, id as u32],
|
Code::Native(id) => [2, id as u32],
|
||||||
Code::ProtoLink(kind) => [3, kind as u32],
|
Code::ProtoLink(kind) => [3, kind as u32],
|
||||||
|
Code::HostLink => [5, 0],
|
||||||
}),
|
}),
|
||||||
ObjKind::Array { elems } => {
|
ObjKind::Array { elems } => {
|
||||||
sig.push(4);
|
sig.push(4);
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ impl<'a> Emitter<'a> {
|
|||||||
JsValue::String(s) => self.string_lit(&js_string_text(s)),
|
JsValue::String(s) => self.string_lit(&js_string_text(s)),
|
||||||
JsValue::NativeFn(id) => format!("&{}", builtin_name(self.num(self.cache.builtin(Code::Native(*id))))),
|
JsValue::NativeFn(id) => format!("&{}", builtin_name(self.num(self.cache.builtin(Code::Native(*id))))),
|
||||||
JsValue::ProtoLink(kind) => format!("&{}", builtin_name(self.num(self.cache.builtin(Code::ProtoLink(*kind))))),
|
JsValue::ProtoLink(kind) => format!("&{}", builtin_name(self.num(self.cache.builtin(Code::ProtoLink(*kind))))),
|
||||||
|
JsValue::HostLink => format!("&{}", builtin_name(self.num(self.cache.builtin(Code::HostLink)))),
|
||||||
_ => format!("({}){{0}}", self.c_type(rep)),
|
_ => format!("({}){{0}}", self.c_type(rep)),
|
||||||
},
|
},
|
||||||
Undefined | Null => format!("({}){{0}}", self.c_type(rep)),
|
Undefined | Null => format!("({}){{0}}", self.c_type(rep)),
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use std::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
infer::{
|
infer::{
|
||||||
HOST, InferResult, Place, TypeCache,
|
HOST, HOST_COMPLETE, InferResult, Place, TypeCache,
|
||||||
cells::{Cell, Code, LENGTH_FIELD, ObjKind, read_value},
|
cells::{Cell, Code, LENGTH_FIELD, ObjKind, read_value},
|
||||||
types::Type,
|
types::Type,
|
||||||
},
|
},
|
||||||
@@ -94,11 +94,16 @@ pub fn emit(fns: &[RegFn], cache: &TypeCache, atoms: &[Atom]) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The walks that the code calls, from the entry. The host calls the entry
|
if cache.host_complete != Type::Unknown {
|
||||||
// with nothing: its env and `this` are undefined
|
let _ = writeln!(statics, "static {} {HOST_COMPLETE_GLOBAL};", e.c_type(e.rep(&cache.host_complete)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The walks that the code calls, from the host's calls. The host calls the
|
||||||
|
// entry with nothing: its env and `this` are undefined
|
||||||
let entry = cache.find(fns.len() - 1, HOST, &[], fns);
|
let entry = cache.find(fns.len() - 1, HOST, &[], fns);
|
||||||
let entry_args = vec!["(undefined_t){0}"; cache.walks[entry].req.arg_types.len()].join(", ");
|
let entry_args = vec!["(undefined_t){0}"; cache.walks[entry].req.arg_types.len()].join(", ");
|
||||||
e.use_walk(entry);
|
e.use_walk(entry);
|
||||||
|
let complete = e.host_complete();
|
||||||
|
|
||||||
let mut fn_protos = String::new();
|
let mut fn_protos = String::new();
|
||||||
let mut bodies = String::new();
|
let mut bodies = String::new();
|
||||||
@@ -127,7 +132,9 @@ pub fn emit(fns: &[RegFn], cache: &TypeCache, atoms: &[Atom]) -> String {
|
|||||||
e.helper_protos,
|
e.helper_protos,
|
||||||
e.helper_defs
|
e.helper_defs
|
||||||
);
|
);
|
||||||
let _ = writeln!(out, "int main(void) {{\n{init} {}({entry_args});\n return 0;\n}}", e.fn_name(entry));
|
// What the host calls (see `lib/rt/jsrt.h`)
|
||||||
|
let _ = writeln!(out, "void jsrt_start(void) {{\n{init} {}({entry_args});\n}}\n", e.fn_name(entry));
|
||||||
|
let _ = writeln!(out, "void jsrt_complete(int32_t h) {{\n{complete}}}");
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +294,9 @@ fn builtin_name(id: CellNum) -> String {
|
|||||||
format!("b{id}")
|
format!("b{id}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The global that holds what the host calls when an IO op completes
|
||||||
|
const HOST_COMPLETE_GLOBAL: &str = "jsrt_host_complete";
|
||||||
|
|
||||||
fn proto_global(kind: ProtoOf) -> String {
|
fn proto_global(kind: ProtoOf) -> String {
|
||||||
format!("proto_{kind:?}").to_lowercase()
|
format!("proto_{kind:?}").to_lowercase()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ pub enum Code {
|
|||||||
Fn(usize),
|
Fn(usize),
|
||||||
Native(usize),
|
Native(usize),
|
||||||
ProtoLink(ProtoOf),
|
ProtoLink(ProtoOf),
|
||||||
|
HostLink,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What the objects of a cell are, besides objects with named fields
|
/// What the objects of a cell are, besides objects with named fields
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ pub type Place = (usize, usize);
|
|||||||
/// The call place of the entry fn, which the host calls
|
/// The call place of the entry fn, which the host calls
|
||||||
pub const HOST: Place = (usize::MAX, 0);
|
pub const HOST: Place = (usize::MAX, 0);
|
||||||
|
|
||||||
|
/// The call place of the fn that the host calls when an IO op completes (see
|
||||||
|
/// `Code::HostLink`)
|
||||||
|
pub const HOST_COMPLETE: Place = (usize::MAX, 1);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct InferRequest {
|
pub struct InferRequest {
|
||||||
pub fn_id: usize,
|
pub fn_id: usize,
|
||||||
@@ -60,6 +64,7 @@ pub struct TypeCache {
|
|||||||
objects: BTreeMap<(usize, usize, Place), Cell>, // the cell of each name: the fn and instr that make the objects, and the call place
|
objects: BTreeMap<(usize, usize, Place), Cell>, // the cell of each name: the fn and instr that make the objects, and the call place
|
||||||
builtins: Vec<Cell>, // the cells of the natives, then of the proto links
|
builtins: Vec<Cell>, // the cells of the natives, then of the proto links
|
||||||
pub protos: Protos, // the prototype of each `ProtoOf`, as the stdlib links it
|
pub protos: Protos, // the prototype of each `ProtoOf`, as the stdlib links it
|
||||||
|
pub host_complete: Type, // what the host calls when an IO op completes, as the stdlib links it
|
||||||
version: usize, // goes up on every change of a cell, a return or a proto, and on every new cell or walk
|
version: usize, // goes up on every change of a cell, a return or a proto, and on every new cell or walk
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +88,7 @@ impl TypeCache {
|
|||||||
match code {
|
match code {
|
||||||
Code::Native(id) => &self.builtins[id],
|
Code::Native(id) => &self.builtins[id],
|
||||||
Code::ProtoLink(kind) => &self.builtins[NATIVES.len() + kind as usize],
|
Code::ProtoLink(kind) => &self.builtins[NATIVES.len() + kind as usize],
|
||||||
|
Code::HostLink => &self.builtins[NATIVES.len() + ProtoOf::ALL.len()],
|
||||||
Code::Fn(_) => unreachable!(),
|
Code::Fn(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,6 +190,28 @@ fn call_code(cache: &mut TypeCache, code: Code, arg_types: &[Type], fns: &[RegFn
|
|||||||
cache.protos[kind as usize] = joined;
|
cache.protos[kind as usize] = joined;
|
||||||
Type::Undefined
|
Type::Undefined
|
||||||
}
|
}
|
||||||
|
Code::HostLink => {
|
||||||
|
let joined = join(&cache.host_complete, args.first().unwrap_or(&Type::Undefined));
|
||||||
|
cache.changed(joined != cache.host_complete);
|
||||||
|
cache.host_complete = joined;
|
||||||
|
Type::Undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The calls that the host makes: the entry, with no args, then the fn that
|
||||||
|
/// it calls when an IO op completes, with the op's handle. Each member of its
|
||||||
|
/// type is its own env, and `this` is undefined
|
||||||
|
fn host_calls(cache: &mut TypeCache, fns: &[RegFn]) {
|
||||||
|
// The synthetic program entry is appended last by `flatten`
|
||||||
|
cache.call(fns.len() - 1, HOST, &[], fns);
|
||||||
|
|
||||||
|
for member in cache.host_complete.clone().members() {
|
||||||
|
if let Type::Obj(cell) = member
|
||||||
|
&& let Some(Code::Fn(fn_id)) = cell.code()
|
||||||
|
{
|
||||||
|
cache.call(fn_id, HOST_COMPLETE, &[member.clone(), Type::Undefined, Type::Int], fns);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,19 +234,18 @@ pub fn infer_types(fns: &[RegFn]) -> TypeCache {
|
|||||||
objects: BTreeMap::new(),
|
objects: BTreeMap::new(),
|
||||||
builtins: vec![],
|
builtins: vec![],
|
||||||
protos: std::array::from_fn(|_| Type::Unknown),
|
protos: std::array::from_fn(|_| Type::Unknown),
|
||||||
|
host_complete: Type::Unknown,
|
||||||
version: 0,
|
version: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let codes = (0..NATIVES.len()).map(Code::Native).chain(ProtoOf::ALL.map(Code::ProtoLink));
|
let codes = (0..NATIVES.len()).map(Code::Native).chain(ProtoOf::ALL.map(Code::ProtoLink)).chain([Code::HostLink]);
|
||||||
cache.builtins = codes.map(|code| Cell::new(ObjKind::Fn(code))).collect();
|
cache.builtins = codes.map(|code| Cell::new(ObjKind::Fn(code))).collect();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let version = cache.version;
|
let version = cache.version;
|
||||||
cache.walks.iter_mut().for_each(|w| w.result = None);
|
cache.walks.iter_mut().for_each(|w| w.result = None);
|
||||||
|
|
||||||
// The synthetic program entry is appended last by `flatten`. It is
|
host_calls(&mut cache, fns);
|
||||||
// called by the host with no args
|
|
||||||
cache.call(fns.len() - 1, HOST, &[], fns);
|
|
||||||
|
|
||||||
if cache.version == version {
|
if cache.version == version {
|
||||||
return cache;
|
return cache;
|
||||||
@@ -302,6 +329,7 @@ fn walk_fn(w: usize, reg_fns: &[RegFn], cache: &mut TypeCache) -> InferResult {
|
|||||||
JsValue::Undefined => Type::Undefined,
|
JsValue::Undefined => Type::Undefined,
|
||||||
JsValue::NativeFn(id) => Type::Obj(cache.builtin(Code::Native(id)).clone()),
|
JsValue::NativeFn(id) => Type::Obj(cache.builtin(Code::Native(id)).clone()),
|
||||||
JsValue::ProtoLink(kind) => Type::Obj(cache.builtin(Code::ProtoLink(kind)).clone()),
|
JsValue::ProtoLink(kind) => Type::Obj(cache.builtin(Code::ProtoLink(kind)).clone()),
|
||||||
|
JsValue::HostLink => Type::Obj(cache.builtin(Code::HostLink).clone()),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -173,6 +173,8 @@ pub enum JsValue {
|
|||||||
NativeFn(usize),
|
NativeFn(usize),
|
||||||
/// Makes its arg the prototype of a primitive kind (see `native`)
|
/// Makes its arg the prototype of a primitive kind (see `native`)
|
||||||
ProtoLink(crate::native::ProtoOf),
|
ProtoLink(crate::native::ProtoOf),
|
||||||
|
/// `__link_host_complete`: its arg is what the host calls when an op completes
|
||||||
|
HostLink,
|
||||||
Module(Module),
|
Module(Module),
|
||||||
TypedArray,
|
TypedArray,
|
||||||
ArrayBuffer,
|
ArrayBuffer,
|
||||||
|
|||||||
+10
-1
@@ -58,7 +58,6 @@ pub const NATIVES: &[NativeFn] = &[
|
|||||||
native!("__native_io_queue_close"(Int) -> Int),
|
native!("__native_io_queue_close"(Int) -> Int),
|
||||||
native!("__native_io_queue_stat"(String) -> Int),
|
native!("__native_io_queue_stat"(String) -> Int),
|
||||||
native!("__native_io_queue_cancel"(Int) -> Int),
|
native!("__native_io_queue_cancel"(Int) -> Int),
|
||||||
native!("__native_io_wait"() -> Int),
|
|
||||||
native!("__native_io_status"(Int) -> Int),
|
native!("__native_io_status"(Int) -> Int),
|
||||||
native!("__native_io_result_string"(Int) -> String),
|
native!("__native_io_result_string"(Int) -> String),
|
||||||
native!("__native_io_result_number"(Int) -> Float),
|
native!("__native_io_result_number"(Int) -> Float),
|
||||||
@@ -110,6 +109,12 @@ static PROTO_LINKS: &[(&str, ProtoOf)] = &[
|
|||||||
("__link_proto_array", ProtoOf::Array),
|
("__link_proto_array", ProtoOf::Array),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/// `__link_host_complete(f)` makes `f` what the host calls when an IO op
|
||||||
|
/// completes, with the op's handle (see `jsrt_complete` in `lib/rt/jsrt.h`).
|
||||||
|
/// The host owns the event loop: a flavor of the runtime runs it, and the
|
||||||
|
/// program only queues ops and handles their completions
|
||||||
|
pub const HOST_LINK: &str = "__link_host_complete";
|
||||||
|
|
||||||
/// The value of the global `name`. The only globals are the ones the stdlib
|
/// The value of the global `name`. The only globals are the ones the stdlib
|
||||||
/// may read, anything else is an error
|
/// may read, anything else is an error
|
||||||
pub fn global(name: AtomRef, atoms: &[Atom]) -> JsValue {
|
pub fn global(name: AtomRef, atoms: &[Atom]) -> JsValue {
|
||||||
@@ -124,6 +129,10 @@ pub fn global(name: AtomRef, atoms: &[Atom]) -> JsValue {
|
|||||||
return JsValue::NativeFn(idx);
|
return JsValue::NativeFn(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if name == HOST_LINK {
|
||||||
|
return JsValue::HostLink;
|
||||||
|
}
|
||||||
|
|
||||||
match PROTO_LINKS.iter().find(|(link, _)| name == *link) {
|
match PROTO_LINKS.iter().find(|(link, _)| name == *link) {
|
||||||
Some(&(_, kind)) => JsValue::ProtoLink(kind),
|
Some(&(_, kind)) => JsValue::ProtoLink(kind),
|
||||||
None => panic!("unknown global `{name}`"),
|
None => panic!("unknown global `{name}`"),
|
||||||
|
|||||||
+20
-5
@@ -2,9 +2,10 @@
|
|||||||
// io_uring (see uring.h)
|
// io_uring (see uring.h)
|
||||||
//
|
//
|
||||||
// A `__native_io_queue_<op>` queues an op and gives its handle. It makes no
|
// 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,
|
// syscall, and no native waits. The host's loop (`main` below) puts queued
|
||||||
// waits until one op is done, and gives its handle. The getters then read
|
// ops into the SQ, submits them, waits until one op is done, and gives its
|
||||||
// that op's result, until the next wait
|
// 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
|
// 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
|
// 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 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
|
// The op that the last call gave is freed now
|
||||||
int32_t __native_io_wait(void) {
|
static int32_t jsrt_io_next(void) {
|
||||||
if (jsrt_io.given >= 0) {
|
if (jsrt_io.given >= 0) {
|
||||||
jsrt_io_release(jsrt_io.given);
|
jsrt_io_release(jsrt_io.given);
|
||||||
jsrt_io.given = -1;
|
jsrt_io.given = -1;
|
||||||
@@ -452,4 +453,18 @@ double __native_io_result_number(int32_t h) {
|
|||||||
return op->res;
|
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
|
#endif // JSRT_IMPLEMENTATION
|
||||||
|
|||||||
+15
-1
@@ -10,7 +10,15 @@
|
|||||||
// str.h strings and the string natives
|
// str.h strings and the string natives
|
||||||
// number.h the number natives
|
// number.h the number natives
|
||||||
// any.h any_t and JS's operators on it
|
// 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
|
#pragma once
|
||||||
|
|
||||||
// io.h needs Linux's syscall() and mmap(), which -std=c11 hides. This must
|
// io.h needs Linux's syscall() and mmap(), which -std=c11 hides. This must
|
||||||
@@ -20,6 +28,12 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void jsrt_start(void);
|
||||||
|
void jsrt_complete(int32_t h);
|
||||||
|
|
||||||
#include "str.h"
|
#include "str.h"
|
||||||
#include "number.h"
|
#include "number.h"
|
||||||
#include "any.h"
|
#include "any.h"
|
||||||
|
|||||||
+3
-2
@@ -1,2 +1,3 @@
|
|||||||
// Runs after the program: the event loop (see loop.js)
|
// Runs after the program's top level: its microtasks. The host then runs
|
||||||
__run_loop();
|
// the event loop (see loop.js)
|
||||||
|
__drain_microtasks();
|
||||||
|
|||||||
+10
-13
@@ -1,8 +1,8 @@
|
|||||||
// The event loop. A macrotask is an IO op or a timer, which completes in C
|
// The event loop's JS side. The host owns the loop (see lib/rt/jsrt.h). A
|
||||||
// (see lib/rt/io.h): `__native_io_wait()` gives the handle of the next one
|
// macrotask is an IO op or a timer, which the host runs: it calls
|
||||||
// that completes. A microtask is a promise job, in `__microtasks`. After the
|
// `__complete(h)` when op `h` is done. A microtask is a promise job, in
|
||||||
// program, end.js runs the loop: all microtasks, then one macrotask, then all
|
// `__microtasks`. After the program's top level, end.js runs all microtasks,
|
||||||
// microtasks again, until nothing is pending
|
// and after each macrotask, `__complete` does
|
||||||
|
|
||||||
// What to do when IO op `h` completes: `__pending[h]`, a fn that gets `h`.
|
// 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
|
// Handles are small ints, and a handle is used again after its op completes
|
||||||
@@ -32,15 +32,12 @@ function __drain_microtasks() {
|
|||||||
q.head = 0;
|
q.head = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function __run_loop() {
|
// What the host calls when op `h` is done: its callback, then the microtasks
|
||||||
for (;;) {
|
function __complete(h) {
|
||||||
__drain_microtasks();
|
|
||||||
|
|
||||||
const h = __native_io_wait();
|
|
||||||
if (h < 0) return;
|
|
||||||
|
|
||||||
const done = __pending[h];
|
const done = __pending[h];
|
||||||
__pending[h] = undefined;
|
__pending[h] = undefined;
|
||||||
if (done !== undefined) done(h);
|
if (done !== undefined) done(h);
|
||||||
}
|
__drain_microtasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__link_host_complete(__complete);
|
||||||
|
|||||||
Reference in New Issue
Block a user