1use std::sync::Arc;
14use std::thread;
15
16use subms_mpsc_queue::{MpscQueue, PopResult};
17
18const GATEWAYS: usize = 4;
19const ORDERS_PER_GATEWAY: usize = 1_000;
20
21fn order_id(gateway: usize, seq: usize) -> u64 {
22 ((gateway as u64) << 32) | seq as u64
23}
24
25fn main() {
26 base_order_fan_in();
27
28 #[cfg(feature = "mpmc")]
29 mpmc_sharded_match();
30
31 #[cfg(feature = "bounded")]
32 bounded_inbox_backpressure();
33
34 #[cfg(feature = "batch")]
35 batch_drain_per_tick();
36
37 #[cfg(feature = "metrics")]
38 metrics_health_snapshot();
39
40 #[cfg(feature = "affinity")]
41 affinity_pin_match_loop();
42}
43
44fn base_order_fan_in() {
50 println!("== base: order-entry gateways fan in to one matching engine ==");
51 let q: Arc<MpscQueue<u64>> = Arc::new(MpscQueue::new());
52
53 let gateways: Vec<_> = (0..GATEWAYS)
54 .map(|g| {
55 let q = Arc::clone(&q);
56 thread::spawn(move || {
57 for seq in 0..ORDERS_PER_GATEWAY {
58 q.push(order_id(g, seq));
59 }
60 })
61 })
62 .collect();
63 for h in gateways {
64 h.join().unwrap();
65 }
66
67 let mut q = Arc::into_inner(q).expect("all gateway handles dropped");
70
71 let total = GATEWAYS * ORDERS_PER_GATEWAY;
72 println!(" inbox depth before the match loop starts: {}", q.len());
73 let first = *q.peek().expect("the inbox is not empty");
74 println!(
75 " head of book: gateway {} seq {}",
76 first >> 32,
77 first & 0xffff_ffff
78 );
79
80 let mut per_gateway = [0usize; GATEWAYS];
81 let mut last_seq = [None::<u64>; GATEWAYS];
82 let mut matched = 0usize;
83 loop {
84 match q.try_pop() {
85 PopResult::Some(order) => {
86 let g = (order >> 32) as usize;
87 let seq = order & 0xffff_ffff;
88 if let Some(prev) = last_seq[g] {
89 assert!(seq > prev, "orders from one gateway stay in FIFO order");
90 }
91 last_seq[g] = Some(seq);
92 per_gateway[g] += 1;
93 matched += 1;
94 }
95 PopResult::Inconsistent => continue,
96 PopResult::Empty => break,
97 }
98 }
99
100 println!(" {GATEWAYS} gateways x {ORDERS_PER_GATEWAY} orders -> matched {matched}");
101 println!(" per-gateway tally: {per_gateway:?}");
102 assert_eq!(matched, total, "no order dropped, none duplicated");
103 for count in per_gateway {
104 assert_eq!(count, ORDERS_PER_GATEWAY, "every gateway fully drained");
105 }
106
107 for seq in 0..32 {
110 q.push(order_id(0, seq));
111 }
112 let voided = q.clear();
113 println!(
114 " kill switch voided {voided} queued orders, inbox empty: {}",
115 q.is_empty()
116 );
117 assert_eq!(voided, 32);
118 assert!(q.is_empty());
119}
120
121#[cfg(feature = "mpmc")]
126fn mpmc_sharded_match() {
127 use std::sync::atomic::{AtomicUsize, Ordering};
128
129 use subms_mpsc_queue::MpmcQueue;
130
131 println!("\n== mpmc: shard the match loop across several consumers ==");
132 let shards = 3usize;
133 let ring: Arc<MpmcQueue<u64>> = Arc::new(MpmcQueue::new(1_024));
134 let total = GATEWAYS * ORDERS_PER_GATEWAY;
135
136 let gateways: Vec<_> = (0..GATEWAYS)
137 .map(|g| {
138 let ring = Arc::clone(&ring);
139 thread::spawn(move || {
140 for seq in 0..ORDERS_PER_GATEWAY {
141 let mut order = order_id(g, seq);
142 while let Err(rejected) = ring.try_enqueue(order) {
143 order = rejected;
144 std::hint::spin_loop();
145 }
146 }
147 })
148 })
149 .collect();
150
151 let matched = Arc::new(AtomicUsize::new(0));
152 let consumers: Vec<_> = (0..shards)
153 .map(|_| {
154 let ring = Arc::clone(&ring);
155 let matched = Arc::clone(&matched);
156 thread::spawn(move || {
157 let mut local = 0usize;
158 loop {
159 if ring.try_dequeue().is_some() {
160 local += 1;
161 matched.fetch_add(1, Ordering::Relaxed);
162 } else if matched.load(Ordering::Relaxed) >= total {
163 break;
164 } else {
165 std::hint::spin_loop();
166 }
167 }
168 local
169 })
170 })
171 .collect();
172
173 for h in gateways {
174 h.join().unwrap();
175 }
176 let drained: usize = consumers.into_iter().map(|c| c.join().unwrap()).sum();
177 println!(
180 " {shards} shards drained {drained} orders, ring empty: {}",
181 ring.is_empty()
182 );
183 assert_eq!(
184 drained, total,
185 "shards together drain every order exactly once"
186 );
187 assert_eq!(
188 ring.producer_index(),
189 ring.consumer_index(),
190 "every claimed slot was consumed"
191 );
192}
193
194#[cfg(feature = "bounded")]
199fn bounded_inbox_backpressure() {
200 use subms_mpsc_queue::BoundedMpscQueue;
201
202 println!("\n== bounded: a fixed-capacity inbox that pushes back ==");
203 let mut inbox: BoundedMpscQueue<u64> = BoundedMpscQueue::new(4);
204 let cap = inbox.capacity();
205
206 let mut accepted = 0usize;
207 let mut rejected = 0usize;
208 for seq in 0..cap + 2 {
209 match inbox.try_enqueue(order_id(0, seq)) {
210 Ok(()) => accepted += 1,
211 Err(_order) => rejected += 1,
212 }
213 }
214 println!(" capacity {cap}: accepted {accepted}, shed {rejected} while full");
215 assert_eq!(accepted, cap, "accepts exactly one full ring");
216 assert_eq!(rejected, 2, "the overflow is handed back, not queued");
217 assert!(inbox.is_full());
218
219 assert!(
221 inbox.try_dequeue().is_some(),
222 "match loop consumes one order"
223 );
224 assert!(
225 inbox.try_enqueue(order_id(0, 99)).is_ok(),
226 "a freed slot reopens the inbox"
227 );
228
229 println!(
232 " producer index {} - consumer index {} = lag {}",
233 inbox.producer_index(),
234 inbox.consumer_index(),
235 inbox.len()
236 );
237 assert_eq!(inbox.producer_index() - inbox.consumer_index(), inbox.len());
238}
239
240#[cfg(feature = "batch")]
245fn batch_drain_per_tick() {
246 use subms_mpsc_queue::BatchMpscQueue;
247
248 println!("\n== batch: publish and drain a whole tick in one pass ==");
249 const TICK: usize = 256;
250 const BURST: usize = 50;
251 let mut q: BatchMpscQueue<u64> = BatchMpscQueue::new();
252 let total = 1_000usize;
253
254 let mut published = 0usize;
257 while published < total {
258 let base = published;
259 published += q.push_batch((base..base + BURST).map(|seq| order_id(0, seq)));
260 }
261 println!(" {published} orders published in {} swaps", total / BURST);
262 assert_eq!(published, total);
263
264 let mut buf: Vec<Option<u64>> = (0..TICK).map(|_| None).collect();
265 let mut ticks = 0usize;
266 let mut matched = 0usize;
267 loop {
268 let n = q.try_dequeue_batch(&mut buf);
269 if n == 0 {
270 break;
271 }
272 ticks += 1;
273 for slot in buf.iter_mut().take(n) {
274 let _ = slot.take();
275 matched += 1;
276 }
277 }
278 println!(" drained {matched} orders across {ticks} ticks of up to {TICK}");
279 assert_eq!(matched, total, "every queued order is drained");
280 assert_eq!(
281 ticks,
282 total.div_ceil(TICK),
283 "each tick drains a full buffer until the tail"
284 );
285
286 q.push_batch((0..64).map(|seq| order_id(1, seq)));
289 let mut notional = 0u64;
290 let handled = q.drain(TICK, |order| notional += order & 0xffff_ffff);
291 println!(" drain callback handled {handled} orders, notional {notional}");
292 assert_eq!(handled, 64);
293 assert_eq!(notional, (0..64u64).sum::<u64>());
294 assert!(q.is_empty());
295}
296
297#[cfg(feature = "metrics")]
301fn metrics_health_snapshot() {
302 use subms_mpsc_queue::MetricsMpscQueue;
303
304 println!("\n== metrics: a health snapshot of the inbox ==");
305 let mut q: MetricsMpscQueue<u64> = MetricsMpscQueue::new();
306 for seq in 0..500 {
307 q.push(order_id(0, seq));
308 }
309 let mut matched = 0usize;
310 while matched < 500 {
311 match q.try_pop() {
312 PopResult::Some(_) => matched += 1,
313 PopResult::Inconsistent => continue,
314 PopResult::Empty => break,
315 }
316 }
317 let _ = q.try_pop();
319
320 let snap = q.snapshot();
321 println!(
322 " enqueue_ok={} dequeue_ok={} dequeue_fail={}",
323 snap.enqueue_ok, snap.dequeue_ok, snap.dequeue_fail
324 );
325 assert_eq!(snap.enqueue_ok, 500, "every push is counted");
326 assert_eq!(snap.dequeue_ok, 500, "every matched order is counted");
327 assert!(
328 snap.dequeue_fail >= 1,
329 "the miss on the drained queue is counted"
330 );
331}
332
333#[cfg(feature = "affinity")]
337fn affinity_pin_match_loop() {
338 use subms_mpsc_queue::{AffinityError, set_affinity};
339
340 println!("\n== affinity: pin the match loop to a core ==");
341 match set_affinity(&[0]) {
342 Ok(()) => println!(" match loop pinned to core 0"),
343 Err(e) => println!(" pinning unavailable: {e}"),
344 }
345 assert!(
346 matches!(set_affinity(&[]), Err(AffinityError::InvalidCore(0))),
347 "an empty core set is always rejected"
348 );
349}