Skip to content

Event loop

Generated from crates/niso-sched/include/event-loop.h when the site was built at 7aa23e2.

This is the event-loop section of niso_runtime.h, owned by niso-sched and kept in crates/niso-sched/include/event-loop.h. The build composes it with the other sections in the order crates/niso-compose/sections.txt gives.

Functions: niso_dom_run, niso_dom_needs_frame, niso_dom_next_timer, niso_dom_set_wake_hook

The event loop.

The Window’s setTimeout, setInterval and requestAnimationFrame (niso_window_or_worker_global_scope_* and niso_animation_frame_provider_* in niso.h) schedule callbacks in the DOM, from any language. niso_dom_run runs every task due by until, in ms on the niso_dom_now clock, each followed by the task hook: the tasks the DOM queues (events such as popstate and hashchange, session history traversal), the networking task source’s tasks as they arrive (a WebSocket’s open, message, error and close events, its connections read on the DOM’s thread), the rendering update’s script steps whenever they have work (scroll and media events, resources that finished loading, resize observations), and timers in the order they are due. Stylesheets and other resources the network loaded are delivered only there, never while a task runs. Animation frame callbacks run in the rendering update (niso_render_frame), all of those requested before it with the frame’s time; when none comes by the first frame boundary after the first request (boundaries are 16 ms apart, from the last rendering update), niso_dom_run gives one itself at that boundary’s time, so runs that present no frames see them too, at the same times however fast the machine is. The frame’s time is also what CSS transitions and animations and the document timeline read, through the frame’s paint, until the next frame, except that a later turn (a task with its microtasks, a task of the rendering steps outside a frame such as a stylesheet’s delivery, a resize observation or a scroll event, input the host delivers (niso_dom_tap, niso_dom_pointer, niso_dom_wheel, niso_dom_key and the niso_dom_*_input and niso_dom_text_commit calls each start one), or what the host does between a paint or a run and the next run, scripts it evaluates included) that reads it a frame or more after it moves it to the latest boundary; it then holds still for the rest of that turn, as in browsers. A task due after now skips the clock ahead to it, so an until past now is virtual time, except while a connection is open: then it waits for the network in real time, until the task is due or until, whichever comes first. Tasks scheduled while it runs wait for the next call when they are due after until. It may return with work left: what native modules sent while it ran waits for the next run, and once until has passed and it has run for a frame’s time (16 ms), it returns between batches of tasks that keep queueing more (IndexedDB requests whose handlers make more), after the rendering steps, so a run to now holds a host’s frame for at most about one frame. Whatever it leaves is a run’s work: niso_dom_needs_frame holds for the tasks and messages left, and niso_dom_next_timer is now for those and for a timer that came due meanwhile, so a host checks both after every run rather than taking a return for an idle page. It collects the DOM first and after each task: what a caller holds across calls must be retained.

A host presenting frames runs the page on a display frame:

niso_dom_run(dom, niso_dom_now(dom));
niso_render_frame(dom, &frame);

and on the next one while niso_dom_needs_frame says so: an animation runs (CSS or Web Animations), an animation frame callback waits, or there is other work a run would do: tasks the DOM queued (an IndexedDB request’s events, one a run left for the next), what a native module, another event loop or the network delivered, a scroll, media or media query change event the rendering steps fire. It holds exactly when a run or a frame has work, and niso_dom_next_timer is now exactly when a run can make progress. Otherwise it runs the page again when the user acts, at niso_dom_next_timer (ms on the niso_dom_now clock: now while a run has work, 16 ms from now at the latest while a secure WebSocket is open, which cannot wake the host, infinity with no timer set), or when the wake hook says a native module, a worker, another event loop or the network delivered something (niso_dom_needs_frame also holds while such a delivery waits). The hook may be called from any thread until niso_dom_free, so it must only schedule a run and never call niso. niso_dom_set_wake_hook and niso_dom_free wait for the calls other threads have in flight, so once either returns the old hook’s userdata may go.

int32_t niso_dom_run(niso_dom* dom, double until);
bool niso_dom_needs_frame(niso_dom* dom);
double niso_dom_next_timer(niso_dom* dom);

The C surface of niso-host’s Wake trait (waker.rs); a null hook is none.

typedef void (*niso_wake_fn)(void* userdata);
void niso_dom_set_wake_hook(niso_dom* dom, niso_wake_fn hook, void* userdata);