nx_event_queue.nx source
↩ module page · 164 lines · 5264 B
1// nx_event_queue.nx -- substrate-level event dispatch primitive.
2//
3// FIFO event queue with kind-based dispatching. Many primitives I've
4// shipped emit events (xenocell intrusion, treaty breach, health
5// transition, scam pattern). nx_event_queue is the generic substrate
6// for accumulating + dispatching those events to subscribers.
7//
8// Composes:
9// nx_xenocell -- intrusion observations flow into events
10// nx_homeostasis -- migration signals flow into events
11// nx_health_check -- RED transitions flow into events
12// nx_book -- queue-drained events get journaled
13//
14// V1 ships a single global queue per caller; multiplexing across
15// subscribers is the caller's job (V1 returns the next event by
16// kind filter).
17
18import "nx_syscalls.nx"
19import "nx_tier.nx"
20
21// ===== Sealed enum: NxEventKind ====================================
22//
23// Generic event kinds the substrate emits. Caller-extensible:
24// values >= NX_EV_N_KINDS may be defined by callers for app-specific
25// events; substrate does not validate them.
26
27const NX_EV_NONE: nx_int = 0
28const NX_EV_XENO_OBSERVATION: nx_int = 1
29const NX_EV_HOMEOSTASIS_SIGNAL: nx_int = 2
30const NX_EV_HEALTH_TRANSITION: nx_int = 3
31const NX_EV_TREATY_BREACHED: nx_int = 4
32const NX_EV_SCAM_FLAGGED: nx_int = 5
33const NX_EV_RECOVERY_TRIGGERED: nx_int = 6
34const NX_EV_TWIN_KEY_DECISION: nx_int = 7
35const NX_EV_VESICLE_DELIVERED: nx_int = 8
36const NX_EV_N_KINDS: nx_int = 9
37
38const NX_EQ_OK: nx_int = 0
39const NX_EQ_ERR_FULL: nx_int = 1
40const NX_EQ_ERR_EMPTY: nx_int = 2
41const NX_EQ_ERR_BAD_KIND: nx_int = 3
42
43// ===== Struct: NxEvent =============================================
44
45struct NxEvent {
46 ts_us: nx_size,
47 kind: nx_int,
48 subject_id: nx_int, // who/what the event concerns
49 payload: nx_size, // arbitrary payload value
50 priority: nx_int, // 0=low, 100=normal, 200=high
51}
52
53// ===== Struct: NxEventQueue ========================================
54
55struct NxEventQueue {
56 events: *NxEvent,
57 capacity: nx_size,
58 head: nx_size,
59 tail: nx_size,
60 count: nx_size,
61 total_emitted: nx_int,
62}
63
64const NX_EV_BYTES: nx_size = 40
65
66func nx_ev_kind_is_known(k: nx_int) -> nx_int {
67 if k < 0 { return 0 }
68 if k >= NX_EV_N_KINDS { return 0 }
69 return 1
70}
71
72func nx_event_queue_new(capacity: nx_size) -> *NxEventQueue {
73 let q: *NxEventQueue = (sys_mmap(48)) as *NxEventQueue
74 let bytes: nx_size = capacity * NX_EV_BYTES
75 q.events = (sys_mmap(bytes)) as *NxEvent
76 q.capacity = capacity
77 q.head = 0
78 q.tail = 0
79 q.count = 0
80 q.total_emitted = 0
81 return q
82}
83
84func _ev_at(q: *NxEventQueue, idx: nx_size) -> *NxEvent {
85 return (q.events as i64 + (idx as i64) * NX_EV_BYTES) as *NxEvent
86}
87
88// ===== nx_event_queue_emit ========================================
89
90func nx_event_queue_emit(q: *NxEventQueue,
91 ts_us: nx_size,
92 kind: nx_int,
93 subject_id: nx_int,
94 payload: nx_size,
95 priority: nx_int) -> nx_int {
96 if q.count >= q.capacity { return NX_EQ_ERR_FULL }
97 let e: *NxEvent = _ev_at(q, q.head)
98 e.ts_us = ts_us
99 e.kind = kind
100 e.subject_id = subject_id
101 e.payload = payload
102 e.priority = priority
103 q.head = q.head + 1
104 if q.head >= q.capacity { q.head = 0 }
105 q.count = q.count + 1
106 q.total_emitted = q.total_emitted + 1
107 return NX_EQ_OK
108}
109
110// ===== nx_event_queue_drain_one ===================================
111//
112// Pop oldest event into caller-supplied out_kind/out_subject. Returns
113// NX_EQ_OK or NX_EQ_ERR_EMPTY.
114
115func nx_event_queue_drain_one(q: *NxEventQueue,
116 out_kind: *i64,
117 out_subject: *i64,
118 out_payload: *i64,
119 out_ts_us: *i64) -> nx_int {
120 if q.count == 0 { return NX_EQ_ERR_EMPTY }
121 let e: *NxEvent = _ev_at(q, q.tail)
122 out_kind[0] = e.kind as i64
123 out_subject[0] = e.subject_id as i64
124 out_payload[0] = e.payload as i64
125 out_ts_us[0] = e.ts_us as i64
126 q.tail = q.tail + 1
127 if q.tail >= q.capacity { q.tail = 0 }
128 q.count = q.count - 1
129 return NX_EQ_OK
130}
131
132// ===== nx_event_queue_peek_kind ===================================
133//
134// Return the kind of the next event without removing it. -1 if empty.
135
136func nx_event_queue_peek_kind(q: *NxEventQueue) -> nx_int {
137 if q.count == 0 { return -1 }
138 let e: *NxEvent = _ev_at(q, q.tail)
139 return e.kind
140}
141
142func nx_event_queue_count(q: *NxEventQueue) -> nx_size {
143 return q.count
144}
145
146func nx_event_queue_total_emitted(q: *NxEventQueue) -> nx_int {
147 return q.total_emitted
148}
149
150func nx_event_queue_count_by_kind(q: *NxEventQueue, kind: nx_int) -> nx_int {
151 var hits: nx_int = 0
152 var live: nx_size = q.count
153 if live > q.capacity { live = q.capacity }
154 var i: nx_size = 0
155 var idx: nx_size = q.tail
156 while i < live {
157 let e: *NxEvent = _ev_at(q, idx)
158 if e.kind == kind { hits = hits + 1 }
159 idx = idx + 1
160 if idx >= q.capacity { idx = 0 }
161 i = i + 1
162 }
163 return hits
164}