feat: time.ticks builtin — CLOCK_MONOTONIC us Int (id 84)

- iteration 22's honest clock: monotone, never wall time; time.now
  stays ms. One types.ml row, sysio case, explicit dispatch arm
  (sys range gate stops at PROC_RUN)
- corpus run/time-ticks (RED WO-E406 first); oop-e2e 104/0, full
  battery green; surface doc row (07-systems-stdlib absent, disclosed)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
shoney.arickathil 2026-08-21 16:13:10 +02:00
parent 555a836d90
commit 146d0e27f3
8 changed files with 57 additions and 12 deletions

View file

@ -290,6 +290,7 @@ let stdlib_members : stdlib_member list =
(* time *)
m "time" "now" 0 0 (Some (TScalar "Int")) None;
m "time" "sleep" 1 46 None None;
m "time" "ticks" 0 84 (Some (TScalar "Int")) None;
m "time" "local" 1 47 (Some (TScalar time_record_name)) (Some time_record_name);
m "time" "iso" 1 48 (Some (TScalar "Text")) None;
(* env *)

View file

@ -273,6 +273,7 @@ unset `env.get` are nil.
| `time.sleep(ms)` | — | |
| `time.local(ms)` | `-> TimeParts` | `{ year, month, day, hour, minute, second, dow }`, dow 0 = Sunday |
| `time.iso(ms)` | `-> Text` | UTC, second precision |
| `time.ticks()` | `-> Int` | CLOCK_MONOTONIC microseconds (id 84, iteration 22's bench clock) — monotone, never wall time; only differences mean anything |
| `env.get(name)` | `-> ?Text` | unset is nil |
| `env.stopping()` | `-> Bool` | SIGTERM/SIGINT latch, handlers installed on first use |
| `net.listen(host, port)` | `-> Int` | IPv4, SO_REUSEADDR, backlog 64; returns an fd |

View file

@ -67,16 +67,16 @@ there). Story:
the difference of two calls is a duration). Later tasks time every
operation with it.
- [ ] Corpus fixture first: two `time.ticks()` calls around a spin loop;
assert the difference is non-negative and the second call is >= the
first (exact values are machine noise — the fixture asserts ordering
and that the builtin exists). Verify it FAILS today (unknown builtin).
- [ ] Wire the id (84), the sysio case (clock_gettime MONOTONIC,
seconds*1e6 + nsec/1e3, as Int), the compiler table rows, the two
contract-doc rows (name, arity 0, return Int µs, monotonic-not-wall
wording).
- [ ] Verify: fixture green under `just oop-e2e`; full battery green.
Commit.
- [x] Corpus fixture first (`run/time-ticks`): RED as WO-E406, then
green — asserts monotonicity + nonzero, values stay machine noise.
- [x] Wired: wob.h id 84 + WO_B_MAX bump; the sysio case; ONE compiler
table row (types.ml is the single stdlib table — the plan's guess of
sibling tables in owner/emit was wrong, no mirror needed); the sys
dispatch range needed an explicit `C == WO_B_TIME_TICKS` arm (the
range gate stops at PROC_RUN). DEVIATION: `07-systems-stdlib.md`
does not exist (a planned doc never written) — the row went into
`08-builtin-surface.md` alone.
- [x] Verified: oop-e2e 104/0 (was 103); full battery green. Commit.
## Task 2 — db-bench sample: tables, serial modes, per-op stats

View file

@ -170,7 +170,8 @@ int wo_builtin(wo_vm *vm, uint64_t *R, uint32_t ins, const char **msg) {
both ranges (WO_B_MAP_GET_OPT and anything added after it) stay here */
if (C == WO_B_JSON_ENCODE || C == WO_B_JSON_DECODE)
return wo_builtin_json(vm, R, ins, msg);
if (C >= WO_B_SYS_FIRST && C <= WO_B_PROC_RUN) return wo_builtin_sys(vm, R, ins, msg);
if ((C >= WO_B_SYS_FIRST && C <= WO_B_PROC_RUN) || C == WO_B_TIME_TICKS)
return wo_builtin_sys(vm, R, ins, msg);
if (C >= WO_B_DB_INSERT && C <= WO_B_DB_PROBE) {
/* arc stage 3: the database is an actor on shard 0. A worker shard
* has no engine by design — its statement marshals, parks, resumes

View file

@ -284,6 +284,14 @@ int wo_builtin_sys(wo_vm *vm, uint64_t *R, uint32_t ins, const char **msg) {
vm->cur->park_done = 1;
return WO_SYS_PARKED;
}
case WO_B_TIME_TICKS: { /* the bench clock: CLOCK_MONOTONIC µs as Int.
* Never wall time — only differences mean
* anything (iteration 22's honest percentiles) */
struct timespec mts;
clock_gettime(CLOCK_MONOTONIC, &mts);
R[A] = (uint64_t)((int64_t)mts.tv_sec * 1000000 + mts.tv_nsec / 1000);
return 0;
}
case WO_B_TIME_LOCAL: { /* Parts: 0 year, 1 month (1..12), 2 day, 3 hour,
* 4 minute, 5 second, 6 dow (0 = Sunday) */
time_t secs = (time_t)((int64_t)R[B] / 1000);

View file

@ -430,9 +430,13 @@ enum {
WO_B_TEXT_OF_BYTES = 83, /* (bytes) -> fresh Text, verbatim. The caller
* asserts the bytes are text; no validation,
* because Unicode is explicitly out of scope */
WO_B_TIME_TICKS = 84, /* () -> Int, CLOCK_MONOTONIC microseconds —
* the bench clock (iteration 22). Monotone,
* never wall time: immune to NTP steps; only
* differences mean anything. */
};
#define WO_B_MAX 83u
#define WO_B_MAX 84u
/* ids at or above this one live in sysio.c, not builtin.c */
#define WO_B_SYS_FIRST WO_B_FS_EXISTS

View file

@ -0,0 +1,2 @@
ticks: monotone
ticks: nonzero

View file

@ -0,0 +1,28 @@
use time
-- time.ticks: CLOCK_MONOTONIC microseconds as Int. Values are machine
-- noise; the fixture asserts existence and ordering — a later reading
-- never precedes an earlier one, and a spin's elapsed is non-negative.
fn spin(rounds: Int) {
let i = 0;
while i < rounds {
i = i + 1;
}
}
fn main() -> Int {
let t0 = time.ticks();
spin(100000);
let t1 = time.ticks();
if t1 >= t0 {
print("ticks: monotone");
} else {
print("ticks: BACKWARDS ${t1 - t0}");
}
if t0 > 0 {
print("ticks: nonzero");
} else {
print("ticks: zero start");
}
return 0;
}