Library
The event loop and program lifetime
When a program is finished, what keeps it alive after the last statement, and how callbacks are dispatched.
since 0.1.0-alpha.1linuxwindows
Description
Every program has an event loop. The statements at the top of your file run first, in order. When they finish, the loop takes over: it waits for timers, sockets and other sources of events, and runs your callbacks as they become ready. You never start the loop; it is implicit.
The program stays alive for as long as live work remains, and it exits on its own when there is none:
- a pending timer is live work; a one-shot timer releases itself after it fires, and
cancel()releases a repeating one; - an open socket with a read watch, a listener that is accepting, a subscription to a signal and a running child whose exit is being waited for are live work too;
- a program with no live work exits as soon as its top-level statements are done.
Callbacks run on a single thread, one at a time. Two callbacks never run at the same instant, so they cannot race on the program's data. When more than one timer is due, they fire in due-time order, and for equal due times in the order the timers were created.
A repeating timer keeps the program alive until it is cancelled
Timer once = std::after(100);
once.subscribe((n) => console.writeln("one-shot fired, tick ${n}"));
Timer repeat = std::every(10);
repeat.subscribe((n) => {
console.writeln("repeat ${n}");
if (n == 3) { repeat.cancel(); }
});
console.writeln("top level done");
top level done
repeat 1
repeat 2
repeat 3
one-shot fired, tick 1
The top-level statements finish first, so top level done is printed before any callback runs.
Rules
- The loop starts after the top-level statements finish; there is nothing to call.
- The program exits when no live work remains. A repeating timer that is never cancelled keeps it running forever.
- Dispatch is single-threaded: callbacks never run concurrently. Worker threads started with
std::spawnhave their own loop; seelang.threads. - Timers fire in (due time, creation order).
- An uncaught exception in a callback stops the loop and reports the error as usual, ending the
program with status
1. - To end the program early, call
env::exit; seelang.exit-codes.
Examples
Timers fire in due-time order, whatever order they were created in:
Dispatch order of timers
Timer a = std::after(30);
Timer b = std::after(10);
Timer c = std::after(20);
a.subscribe((n) => console.writeln("a (30ms)"));
b.subscribe((n) => console.writeln("b (10ms)"));
c.subscribe((n) => console.writeln("c (20ms)"));
Timer never = std::every(5);
never.cancel();
b (10ms)
c (20ms)
a (30ms)
The timer never was cancelled before the loop started, so it does not keep the program alive and
never fires.
Notes
The loop is the same one that drives sockets, signals, child processes and timers, so a program
that mixes them needs nothing more than callbacks and await.