code wiki / (root) / nx_session.nx

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}