flan/runtime
Joseph Ferano 2dd13b5ae0 The language can tell the time, and read its environment
Nothing in it could. A game got a clock from raylib and a program without a
window had none at all, so "how long did that take" was unanswerable in the
half of daily use that is a tool rather than a game.

Two clocks, because the mistake a single one invites is using it for the other
job. monotonic-ns measures: it never goes backwards, nothing adjusts it, and
its zero is arbitrary, so it is meaningless alone and correct as a difference.
unix-ns dates: nanoseconds since 1970, which is what goes in a save file, and
which jumps in either direction when somebody sets the system clock. The names
are picked so that reaching for the wrong one reads wrong.

This is Odin's shape, from core/time/time.odin and core/time/time_linux.odin:
Tick against Time, both an i64 of nanoseconds, over MONOTONIC and REALTIME,
with the seconds-valued face derived rather than a second syscall. Three C
functions here and six Flan names over them, which is the rule flan_rt.c's own
header states -- a primitive is the only thing implemented twice.

The monotonic origin is the first read of the clock in the process, not boot,
and that is the one decision worth arguing. CLOCK_MONOTONIC counts from boot,
so on a machine up a hundred days the raw value is past 2^53 nanoseconds and
monotonic-seconds would lose sub-microsecond resolution depending on the
machine's uptime rather than on anything the program did. Latched to first
read it stays integer-exact for a hundred days of process life, and it also
matches what a game already has: raylib's GetTime is seconds since
InitWindow, so the two numbers now mix without a conversion at every site.

sleep-ns loops on EINTR, because otherwise a signal cuts the wait short and a
frame loop wobbles for reasons nothing in the program explains. It is
documented as at-least and not as a frame limiter; the shape that actually
paces a loop is a deadline recomputed from monotonic-ns each turn, and the
comment says so where somebody will read it.

getenv answers an (Option [u8]) viewing the process environment, which needs
no allocator and no free and is safe precisely because nothing in this
language can call setenv or spawn a process. The absent case rides in the
length rather than in the pointer: there is no null test to write, since a
(Ptr T) here always addresses something, so flan_getenv answers -1 and a
pointer at a valid empty string and the Flan side tests arithmetic.

The runtime additions are a single block at the end of flan_rt.c, with
<time.h> inside it for the reason <errno.h> sits beside the file section.

programs/time.flan asserts invariants and never a reading -- t2 >= t1, a sleep
that did not return early, a date after 2020 and before 2100 -- because the
same file is in the corpus @x86 builds twice and diffs, so a timestamp would
fail a correct compiler on its second run.
2026-09-17 22:04:48 +07:00
..