nx_arena_scope_test.nx source
↩ module page · 111 lines · 4271 B
1// nx_arena_scope_test.nx -- smoke for mark/reset-to-mark + ceiling.
2//
3// Verifies the SSS-class behaviour that closes the arena issue from
4// NISHI_HONEST_TRADE_OFFS.md: nested workloads can scope their
5// allocations without accumulating across loop iterations.
6//
7// Construct the NxArena manually from a caller-provided buffer
8// (per user directive "user owns every bit") rather than calling
9// nx_arena_new which mmaps internally. This keeps the smoke
10// cross-target portable -- the test doesn't depend on a particular
11// syscall set.
12//
13// expect_exit: 0
14//
15// license_tier: ORIGINAL
16
17// Caller-chosen syscall layer (x86_64 native exec). Composes against
18// nx_arena_types.nx which carries NO syscall import. This is the
19// four-pillar FIX 2026-05-16 for the F-meta-4 cross-target gap: types
20// + alloc/reset/inspect live in nx_arena_types.nx (no deps), the
21// caller picks whichever syscall layer matches its target ABI.
22import "nx_syscalls_x86_64.nx"
23import "nx_arena_types.nx"
24import "nx_arena_scope.nx"
25
26func main() -> i64 {
27 // ---- Sealed-enum gate -----------------------------------------
28 if nx_arena_verdict_is_valid(NX_ARENA_OK) != 1 { return 1 }
29 if nx_arena_verdict_is_valid(NX_ARENA_OOM_CEILING) != 1 { return 2 }
30 if nx_arena_verdict_is_valid(NX_ARENA_OOM_CAP) != 1 { return 3 }
31 if nx_arena_verdict_is_valid(NX_ARENA_BAD_MARK) != 1 { return 4 }
32 if nx_arena_verdict_is_valid(NX_ARENA_BAD_ARG) != 1 { return 5 }
33 if nx_arena_verdict_is_valid(NX_ARENA_N) != 0 { return 6 }
34 if nx_arena_verdict_is_valid(-1) != 0 { return 7 }
35
36 // ---- Construct arena from user-owned buffer -------------------
37 // User-controlled allocation: 64 KB backing buffer via x86_64
38 // sys_mmap. The substrate doesn't reserve; the user requests.
39 let backing: *u8 = sys_mmap(65536)
40 let arena_raw: *u8 = sys_mmap(NX_ARENA_BYTES)
41 let a: *NxArena = arena_raw as *NxArena
42 a.base = backing
43 a.cap = 65536
44 a.off = 0
45 a.n_alloc = 0
46 a.n_oom = 0
47
48 if nx_arena_used(a) != 0 { return 11 }
49 if nx_arena_headroom_bytes(a) != 65536 { return 12 }
50
51 // ---- Mark + reset-to-mark cycle ------------------------------
52 let mark0: i64 = nx_arena_mark(a)
53 if mark0 != 0 { return 20 }
54
55 // Bump the offset manually (simulating an allocation)
56 a.off = 1024
57
58 let mark1: i64 = nx_arena_mark(a)
59 if mark1 != 1024 { return 21 }
60
61 let v: *i64 = sys_mmap(8) as *i64
62
63 // Reset to mark0: rewinds to 0
64 nx_arena_reset_to_mark(a, mark0, v)
65 if v[0] != NX_ARENA_OK { return 22 }
66 if nx_arena_used(a) != 0 { return 23 }
67
68 // ---- Daemon-style nested-scope simulation --------------------
69 // "Daemon startup" -- allocate 1 KB of long-lived state.
70 a.off = 1024
71 let after_startup: i64 = nx_arena_used(a)
72
73 // Tick loop: 100 iterations, each does +4 KB then resets to mark
74 var tick: i64 = 0
75 while tick < 100 {
76 let mark: i64 = nx_arena_mark(a)
77 if mark != after_startup { return 30 + tick }
78 // simulate tick-scoped 4 KB scratch
79 a.off = a.off + 4096
80 // reset to mark
81 nx_arena_reset_to_mark(a, mark, v)
82 if v[0] != NX_ARENA_OK { return 400 + tick }
83 if nx_arena_used(a) != mark { return 600 + tick }
84 tick = tick + 1
85 }
86
87 // After 100 ticks: arena offset still equals after_startup
88 if nx_arena_used(a) != after_startup { return 800 }
89
90 // ---- BAD_MARK paths -------------------------------------------
91 // mark > current off (would expose stale): refuse
92 let bad_mark: i64 = nx_arena_used(a) + 1000
93 nx_arena_reset_to_mark(a, bad_mark, v)
94 if v[0] != NX_ARENA_BAD_MARK { return 930 }
95
96 // negative mark: refuse
97 nx_arena_reset_to_mark(a, -1, v)
98 if v[0] != NX_ARENA_BAD_MARK { return 931 }
99
100 // ---- Ceiling check (would_fit observer) ----------------------
101 if nx_arena_would_fit(a, 1024, 8, 8192) != 1 { return 940 }
102 if nx_arena_would_fit(a, 8192, 8, 1024) != 0 { return 941 }
103 // observer didn't mutate
104 if nx_arena_used(a) != after_startup { return 942 }
105
106 // ---- Headroom observer ---------------------------------------
107 let head: i64 = nx_arena_headroom_bytes(a)
108 if head != 65536 - after_startup { return 950 }
109
110 return 0
111}