nx_session.nx source
↩ module page · 180 lines · 7304 B
1// nx_session.nx -- composite substrate session for parallel-companion scenes.
2//
3// Bundles the five core runtime primitives every parallel-companion
4// scene needs into one constructor:
5// - NxActorScheduler actor lifecycle + cooperative scheduling
6// - NxMessageBus typed inter-actor mailboxes + pub/sub
7// - NxStreamMultiplexer priority-ordered output stream interleave
8// - NxResourceArbiter VRAM / RAM / CPU budget arbitration
9// - NxInterruptBroker sub-50ms preemption for operator inputs
10//
11// Replaces the ~30 lines of wiring boilerplate that nx_companion_compose,
12// nx_dual_companion_compose, nx_triple_companion_compose, and
13// nx_quad_companion_compose each currently duplicate.
14//
15// One call -> entire substrate ready:
16//
17// let s: *NxSession = nx_session_new(
18// n_actors_capacity, mailbox_capacity, mux_capacity,
19// arb_capacity, broker_capacity,
20// starvation_threshold_us, quantum_per_actor, epoch_quantum_us,
21// now_us)
22//
23// Convenience funcs bundle common patterns (spawn-actor-and-register-
24// mailbox, subscribe-to-kind, etc.) so a 4-modality scene fits in
25// fewer lines than the wiring it composes.
26//
27// Composes feedback-build-intelligence-never-strip-features (build
28// the composite once, reuse everywhere) + feedback-no-false-ok-
29// substrate-honesty-audit (each sub-primitive's own smoke proves it
30// works; this primitive's smoke proves the WIRING composes).
31
32import "nx_syscalls.nx"
33import "nx_tier.nx"
34import "nx_actor.nx"
35import "nx_message.nx"
36import "nx_stream_multiplexer.nx"
37import "nx_resource_arbiter.nx"
38import "nx_interrupt_broker.nx"
39
40// ===== Sealed enum: NxSessionVerdict ==============================
41
42const NX_SE_V_OK: nx_int = 0
43const NX_SE_V_BAD_CAPACITY: nx_int = 1
44const NX_SE_V_BAD_QUANTUM: nx_int = 2
45const NX_SE_V_NULL: nx_int = 3
46const NX_SE_V_INVALID: nx_int = 4
47const NX_SE_V_N: nx_int = 5
48
49func nx_se_v_is_valid(v: nx_int) -> nx_int {
50 if v < 0 { return 0 }
51 if v >= NX_SE_V_N { return 0 }
52 return 1
53}
54
55// ===== Struct: NxSession ==========================================
56
57struct NxSession {
58 scheduler: *NxActorScheduler,
59 bus: *NxMessageBus,
60 multiplexer: *NxStreamMultiplexer,
61 arbiter: *NxResourceArbiter,
62 broker: *NxInterruptBroker,
63 created_at_us: nx_size,
64 verdict: nx_int,
65}
66
67const NX_SE_BYTES: nx_int = 56 // 7 fields * 8
68
69// ===== Constructor ================================================
70//
71// All capacities + scheduler params validated at the boundary. Any
72// failure leaves session.verdict set to a structured error.
73
74func nx_session_new(n_actors_capacity: nx_int,
75 mailbox_capacity: nx_int,
76 sub_capacity: nx_int,
77 per_mailbox_capacity: nx_int,
78 mux_capacity: nx_int,
79 arb_capacity: nx_int,
80 broker_capacity: nx_int,
81 epoch_quantum_us: nx_size,
82 quantum_per_actor: nx_int,
83 starvation_threshold_us: nx_size,
84 now_us: nx_size) -> *NxSession {
85 let raw: *u8 = sys_mmap(NX_SE_BYTES)
86 let s: *NxSession = raw as *NxSession
87 s.scheduler = 0 as *NxActorScheduler
88 s.bus = 0 as *NxMessageBus
89 s.multiplexer = 0 as *NxStreamMultiplexer
90 s.arbiter = 0 as *NxResourceArbiter
91 s.broker = 0 as *NxInterruptBroker
92 s.created_at_us = now_us
93 s.verdict = NX_SE_V_OK
94 if n_actors_capacity <= 0 { s.verdict = NX_SE_V_BAD_CAPACITY; return s }
95 if mailbox_capacity <= 0 { s.verdict = NX_SE_V_BAD_CAPACITY; return s }
96 if sub_capacity <= 0 { s.verdict = NX_SE_V_BAD_CAPACITY; return s }
97 if per_mailbox_capacity <= 0 { s.verdict = NX_SE_V_BAD_CAPACITY; return s }
98 if mux_capacity <= 0 { s.verdict = NX_SE_V_BAD_CAPACITY; return s }
99 if arb_capacity <= 0 { s.verdict = NX_SE_V_BAD_CAPACITY; return s }
100 if broker_capacity <= 0 { s.verdict = NX_SE_V_BAD_CAPACITY; return s }
101 if quantum_per_actor <= 0 { s.verdict = NX_SE_V_BAD_QUANTUM; return s }
102 s.scheduler = nx_ac_sched_new(n_actors_capacity, epoch_quantum_us, quantum_per_actor, now_us)
103 s.bus = nx_ms_bus_new(mailbox_capacity, sub_capacity, per_mailbox_capacity)
104 s.multiplexer = nx_sm_new(mux_capacity, starvation_threshold_us)
105 s.arbiter = nx_ra_new(arb_capacity)
106 s.broker = nx_ib_new(broker_capacity)
107 return s
108}
109
110// ===== One-call defaults ==========================================
111//
112// Sensible DND-companion defaults for callers who don't want to
113// tune individual caps. Equivalent of "give me a substrate that
114// can host 8 actors with 8-deep mailboxes and 32-deep mux."
115
116func nx_session_new_default(now_us: nx_size) -> *NxSession {
117 return nx_session_new(
118 8, // n_actors_capacity
119 8, // mailbox_capacity
120 16, // sub_capacity
121 16, // per_mailbox_capacity
122 32, // mux_capacity
123 16, // arb_capacity
124 8, // broker_capacity
125 1000000, // epoch_quantum_us (1 second)
126 16, // quantum_per_actor steps per epoch
127 50000, // starvation_threshold_us (50ms)
128 now_us)
129}
130
131// ===== Convenience: spawn-actor-and-register-mailbox ==============
132
133func nx_session_spawn_actor(s: *NxSession,
134 actor_id: nx_int,
135 role: nx_int,
136 priority: nx_int,
137 deadline_us: nx_size,
138 now_us: nx_size) -> nx_int {
139 if (s as i64) == 0 { return NX_SE_V_NULL }
140 if s.verdict != NX_SE_V_OK { return NX_SE_V_INVALID }
141 let v_spawn: nx_int = nx_ac_spawn(s.scheduler, actor_id, role, priority, deadline_us, now_us)
142 if v_spawn != NX_AC_V_STEPPED { return NX_SE_V_INVALID }
143 let v_reg: nx_int = nx_ms_register(s.bus, actor_id)
144 if v_reg != NX_MS_V_DELIVERED { return NX_SE_V_INVALID }
145 return NX_SE_V_OK
146}
147
148func nx_session_subscribe(s: *NxSession,
149 actor_id: nx_int,
150 kind: nx_int) -> nx_int {
151 if (s as i64) == 0 { return NX_SE_V_NULL }
152 if s.verdict != NX_SE_V_OK { return NX_SE_V_INVALID }
153 let v: nx_int = nx_ms_subscribe(s.bus, actor_id, kind)
154 if v != NX_MS_V_DELIVERED { return NX_SE_V_INVALID }
155 return NX_SE_V_OK
156}
157
158// ===== Accessors ==================================================
159
160func nx_session_verdict(s: *NxSession) -> nx_int {
161 if (s as i64) == 0 { return NX_SE_V_NULL }
162 return s.verdict
163}
164
165func nx_session_is_ready(s: *NxSession) -> nx_int {
166 if (s as i64) == 0 { return 0 }
167 if s.verdict != NX_SE_V_OK { return 0 }
168 if (s.scheduler as i64) == 0 { return 0 }
169 if (s.bus as i64) == 0 { return 0 }
170 if (s.multiplexer as i64) == 0 { return 0 }
171 if (s.arbiter as i64) == 0 { return 0 }
172 if (s.broker as i64) == 0 { return 0 }
173 return 1
174}
175
176func nx_session_actor_count(s: *NxSession) -> nx_int {
177 if (s as i64) == 0 { return 0 }
178 if s.verdict != NX_SE_V_OK { return 0 }
179 return nx_ac_actor_count(s.scheduler)
180}