Library
The native floor — std::sys*
What the sys* functions in std are, why compile-time code cannot call them, and which two are public.
since 0.1.0-alpha.1linuxwindowswasm
Description
Everything a program does to the outside world ends in a native: a function the runtime
provides, declared in the standard library without a body. Reading a line, opening a file,
starting a timer, connecting a socket and spawning a child process all bottom out in one. These
functions live in namespace std and every one of them has a name starting with sys
(std::sysWrite, std::sysOpen, std::sysTcpConnect, and so on). Together they are the
native floor.
The floor is deliberately plain. Its functions take and return integers, strings and
arrays; results that can fail use -1 or None; and the families it covers are
- console and files: reading and writing descriptors, opening, stat, directories;
- time: the wall clock, a monotonic clock, and timers;
- the event loop: read and write watches on descriptors;
- network: TCP sockets, name resolution, TLS sessions, RSA encryption;
- processes and terminals: spawning children, pseudo-terminals, raw mode, signals;
- randomness and the environment: entropy, environment variables;
- a windowing floor used by the Dagon GUI framework (see
lang.gui-floor); - threads and tasks: the substrate under
std::spawn,TaskGroupandawait.
The floor is not the API. Programs are meant to use the types built on top of it:
File, Timer, TcpStream, HttpClient, Process, Pty, env, term, signal and the rest of
the library. Those wrap the natives, give them types and error behavior, and are the surface
that stays stable. Every sys* function other than the two listed below is an implementation
detail of the library, not part of the public API. Its name, its arguments, and even its
existence can change between releases, and the library documentation hides it.
Rules
- Two natives are public:
std::sysReadLine(fd)andstd::sysTimerStart(delayMs, intervalMs, callback). They are the smallest way to read a line of input and to schedule a callback. sysReadLine(fd)returns the next line of the descriptor with the trailing newline removed, and returns""at the end of input. A blank line is also"", so the function cannot tell a blank line from the end; call it in a loop that you bound by other means if that matters. Descriptor0is standard input.sysTimerStart(delayMs, intervalMs, callback)schedulescallback(tickNumber)afterdelayMsmilliseconds, and then everyintervalMsmilliseconds whenintervalMsis above zero (0means one shot). It returns a timer id. To be able to cancel a timer, create it withstd::afterorstd::everyand callTimer.cancel(). The callback must be a closure. A callback that is not one (for example a function-typed global that is read before its initializer has run) is rejected when the timer is registered: nothing is scheduled and the call throws a catchableRuntimeException. Timers that fire together run in due-time order, then creation order, and callbacks never run concurrently. Most programs usestd::afterandstd::everyinstead (seestd.Timer).- Compile-time code cannot call the floor. A
comptimeexpression is evaluated while the program is being compiled, and a build must never read the clock, the environment, the file system, the network or a terminal. Everysys*function is therefore denied during compile-time evaluation. The compiler stops with the name of the native. - Some floors are not available everywhere. For the browser target the windowing floor and the
file system functions are rejected at compile time, and child processes (
Process) are rejected at compile time for Windows targets. Where a native is unavailable the compiler reports which one instead of producing a program that misbehaves. - A function that reports optional results distinguishes absent from empty:
Noneand""(or[]) are different answers.
Examples
Reading input line by line. A blank line and the end of input both read as "":
Reading lines from standard input
string line = std::sysReadLine(0);
console.writeln("first: ${line}");
string second = std::sysReadLine(0);
console.writeln("second: ${second}");
string end = std::sysReadLine(0);
console.writeln("at end: [${end}]");
alpha
beta
first: alpha
second: beta
at end: []
Timers start in due-time order, not in the order they were registered:
Timers fire in due-time order
std::sysTimerStart(20, 0, (n) => console.writeln("later"));
std::sysTimerStart(10, 0, (n) => console.writeln("sooner"));
console.writeln("scheduled");
scheduled
sooner
later
A callback that is not a closure is rejected at registration, and the error can be caught:
A non-closure callback is rejected
try {
std::sysTimerStart(1, 0, handler);
} catch (RuntimeException e) {
console.writeln(e.message);
}
(int) => void handler = (n) => console.writeln("tick");
sysTimerStart: callback must be a closure
Compile-time code cannot reach the floor:
comptime int now = std::sysNow();
console.writeln(now);
The compiler rejects this program with comptime code may not perform I/O ('sysNow').
Notes
Functions that take a Block instead of a string (a fixed-length byte buffer) are overloads of
the same read and write natives; the overload is chosen by argument types.
See also
- File — An open file.
- Timer — A source of ticks on the event loop.
- env — The running process: its command-line arguments, its environment variables, and how it exits.
- term — Control of the terminal the program runs in: raw input mode and the window size.
- signal — Operating-system signals delivered to the program as streams.
- TcpStream — A connected network socket that reads and writes text.
- Process — Runs another program as a child process and gives you its standard input, standard output and standard error as streams.