1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
//! Parameterised end-to-end demonstration of `CapacityAdaptiveRing`
//! across the full Shape x Locale x Size matrix.
//!
//! Invocation:
//! cargo run --release --example capacity_morph_matrix -- <shape> <locale> <n_items>
//!
//! Where:
//! shape ∈ {spsc, mpsc4, mpsc8, mpmc4, mpmc8, vyukov4, vyukov8}
//! locale ∈ {anon, file, shmfs}
//! n_items: positive integer (default 100000)
//!
//! Each variant runs n_items total messages through the requested
//! shape at the requested locale, with a morph thread cycling
//! through a set of pow2 capacities at 200us cadence so the morph
//! race window is densely sampled.
//!
//! Integrity checks per shape:
//!
//! - SPSC (1P/1C): every (producer_id=0, item_idx) appears exactly
//! once; per-producer FIFO holds globally.
//! - MPSC (NP/1C): every (producer_id, item_idx) appears exactly
//! once; for each producer, its items appear in the single
//! consumer's pop log in send-order (per-producer FIFO).
//! - MPMC (NP/NC): every (producer_id, item_idx) appears exactly
//! once across the consumer pool's union; within each consumer's
//! own pop log, items from any given producer appear in send-
//! order (per-producer per-consumer FIFO; cross-consumer
//! interleave is per-shape contract).
//! - Vyukov MPMC (NP/NC global FIFO): same set check; additionally,
//! the merge of all consumer pop logs (sorted by global pop
//! sequence) preserves producer-side send-order across producers
//! (Vyukov elevates MPMC's per-producer FIFO to a strict global
//! FIFO via per-slot sequence atomics).
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::thread;
use std::time::{Duration, Instant};
use subetha_cxc::adaptive_ring::RingShape;
use subetha_cxc::capacity_adaptive_ring::CapacityAdaptiveRing;
const INITIAL_CAPACITY: usize = 256;
const MORPH_TARGETS: [usize; 6] = [1024, 4096, 1024, 256, 64, 512];
const MORPH_INTERVAL_US: u64 = 200;
fn main() {
let args: Vec<String> = std::env::args().collect();
let shape = args.get(1).cloned().unwrap_or_else(|| "spsc".to_owned());
let locale = args.get(2).cloned().unwrap_or_else(|| "anon".to_owned());
let n_items: u64 = args.get(3).and_then(|s| s.parse().ok()).unwrap_or(100_000);
let (n_producers, n_consumers, ring_shape) = parse_shape(&shape);
let items_per_producer = n_items / n_producers as u64;
let total_items = items_per_producer * n_producers as u64;
println!("=== CapacityAdaptiveRing Matrix ===");
println!(" shape: {shape} ({n_producers}P/{n_consumers}C, {ring_shape:?})");
println!(" locale: {locale}");
println!(" n_items target: {n_items}");
println!(" items per producer: {items_per_producer}");
println!(" total items expected: {total_items}");
println!(" morph targets: {MORPH_TARGETS:?}");
println!(" morph cadence: {MORPH_INTERVAL_US}us");
println!();
let ring = construct(&locale, &shape, n_producers, n_consumers);
// Register producers / consumers and capture id assignments.
let producer_ids: Vec<usize> = (0..n_producers)
.map(|_| ring.register_producer().expect("register producer"))
.collect();
let consumer_ids: Vec<usize> = (0..n_consumers)
.map(|_| ring.register_consumer().expect("register consumer"))
.collect();
// Shape-morph the inner AdaptiveRing to the target shape.
// SPSC is the default; non-SPSC shapes need the morph_to call
// before the first try_send.
if ring_shape != RingShape::Spsc {
ring.ring_handle()
.morph_to(ring_shape)
.expect("inner shape morph");
}
let morphs_completed = Arc::new(AtomicU64::new(0));
let t0 = Instant::now();
// Spawn producer threads.
let mut producer_handles = Vec::new();
for &pid in &producer_ids {
let r = Arc::clone(&ring);
let h = thread::spawn(move || {
for i in 0..items_per_producer {
let mut payload = [0u8; 56];
payload[..8].copy_from_slice(&(pid as u64).to_le_bytes());
payload[8..16].copy_from_slice(&i.to_le_bytes());
while r.try_send(pid, &payload).is_err() {
std::hint::spin_loop();
}
}
});
producer_handles.push(h);
}
// Spawn consumer threads. Each consumer collects its own pop
// log of (producer_id, item_idx) pairs. The main thread joins
// and merges the logs for integrity verification.
//
// Consumers stop when the total drained item count across the
// pool reaches total_items. A shared AtomicU64 counts drains.
let drained = Arc::new(AtomicU64::new(0));
let mut consumer_handles = Vec::new();
for &cid in &consumer_ids {
let r = Arc::clone(&ring);
let drained = Arc::clone(&drained);
let h = thread::spawn(move || {
let mut log: Vec<(u64, u64)> = Vec::new();
let mut buf = [0u8; 64];
loop {
match r.try_recv(cid, &mut buf) {
Ok(_) => {
let pid = u64::from_le_bytes(buf[..8].try_into().unwrap());
let idx = u64::from_le_bytes(buf[8..16].try_into().unwrap());
log.push((pid, idx));
let now = drained.fetch_add(1, Ordering::AcqRel) + 1;
if now >= total_items {
return log;
}
}
Err(_) => {
if drained.load(Ordering::Acquire) >= total_items {
return log;
}
std::hint::spin_loop();
}
}
}
});
consumer_handles.push(h);
}
// Morph thread: loop MORPH_TARGETS at tight cadence until the
// workload finishes (signalled by Arc strong_count dropping
// below our reference + 1 active producer/consumer threads).
let r_morph = Arc::clone(&ring);
let morphs_c = Arc::clone(&morphs_completed);
let drained_check = Arc::clone(&drained);
let morpher = thread::spawn(move || {
loop {
for target in MORPH_TARGETS {
thread::sleep(Duration::from_micros(MORPH_INTERVAL_US));
r_morph
.morph_capacity_to(target)
.expect("morph succeeds");
morphs_c.fetch_add(1, Ordering::Relaxed);
if drained_check.load(Ordering::Acquire) >= total_items {
return;
}
}
}
});
for h in producer_handles {
h.join().expect("producer thread");
}
let consumer_logs: Vec<Vec<(u64, u64)>> = consumer_handles
.into_iter()
.map(|h| h.join().expect("consumer thread"))
.collect();
morpher.join().expect("morph thread");
let elapsed = t0.elapsed();
// Integrity check 1: every (producer_id, item_idx) appears
// exactly once across the union of consumer logs.
let mut union: Vec<(u64, u64)> = consumer_logs
.iter()
.flatten()
.copied()
.collect();
union.sort();
let expected: Vec<(u64, u64)> = (0..n_producers as u64)
.flat_map(|p| (0..items_per_producer).map(move |i| (p, i)))
.collect();
let mut expected_sorted = expected.clone();
expected_sorted.sort();
let no_loss_no_dup = union == expected_sorted;
// Integrity check 2: per-consumer per-producer FIFO. Within
// each consumer's own pop log, items from any given producer
// must appear in strictly-increasing send-order (idx 0, 1, 2,
// ...; never out of order, never repeating). First item from
// each producer establishes the baseline; any subsequent item
// must be strictly greater.
let mut per_consumer_fifo_violations = 0u64;
for log in &consumer_logs {
let mut last_per_producer: std::collections::HashMap<u64, u64> = Default::default();
for &(pid, idx) in log {
if let Some(&last) = last_per_producer.get(&pid)
&& idx <= last
{
per_consumer_fifo_violations += 1;
}
last_per_producer.insert(pid, idx);
}
}
// Integrity check 3 (SPSC 1P/1C only): strict global FIFO
// across the consumer's single pop log. SPSC has exactly one
// producer AND one consumer, so the pop log MUST equal
// (0,0), (0,1), (0,2), ... in lockstep with the producer's
// sends. This is the strongest FIFO contract any shape
// promises and only SPSC's 1P/1C construction can deliver it.
//
// MPSC (NP/1C) interleaves N producers' streams into the one
// consumer; the consumer sees per-producer FIFO but NOT
// global tuple-ordered FIFO (a pop sequence like
// (3,0),(0,0),(1,0),(0,1),(2,0)... is valid MPSC behaviour).
//
// Vyukov MPMC promises global FIFO via per-slot sequence
// atomics, but verifying it requires the consumer to capture
// a per-pop sequence number that ties back to the producer's
// slot claim - the (pid, idx) tuples we record do not encode
// that, so multi-producer Vyukov cannot be globally-checked
// from this harness's logs even at NC=1. Per-consumer-per-
// producer FIFO (check 2) IS verified for Vyukov.
let global_fifo_checkable =
matches!(ring_shape, RingShape::Spsc) && n_producers == 1 && n_consumers == 1;
let mut global_fifo_violations = 0u64;
if global_fifo_checkable {
let mut prev: Option<(u64, u64)> = None;
for &cur in &consumer_logs[0] {
if let Some(p) = prev
&& cur < p
{
global_fifo_violations += 1;
}
prev = Some(cur);
}
}
println!("=== Result ===");
println!(" elapsed: {elapsed:?}");
println!(
" throughput: {:.2} M items/s",
total_items as f64 / elapsed.as_secs_f64() / 1_000_000.0
);
println!(
" morphs completed: {}",
morphs_completed.load(Ordering::Relaxed)
);
println!(" items produced: {total_items}");
println!(
" items consumed: {}",
consumer_logs.iter().map(|l| l.len()).sum::<usize>()
);
println!(
" per-consumer-per-prod FIFO violations: {per_consumer_fifo_violations} (expected 0; bug if non-zero)"
);
if global_fifo_checkable {
println!(
" strict global FIFO violations: {global_fifo_violations} (expected 0 for SPSC 1P/1C; bug if non-zero)"
);
} else {
println!(" strict global FIFO check: skipped (only SPSC 1P/1C is globally-checkable from per-consumer (pid, idx) logs without pop-sequence timestamps)");
}
println!(
" integrity: {}",
if no_loss_no_dup {
"PASS - every (producer_id, item_idx) appeared exactly once"
} else {
"FAIL - id-set mismatch"
}
);
assert_eq!(
per_consumer_fifo_violations, 0,
"per-consumer-per-producer FIFO must hold across morphs",
);
if global_fifo_checkable {
assert_eq!(
global_fifo_violations, 0,
"strict global FIFO must hold across morphs for SPSC 1P/1C",
);
}
assert!(no_loss_no_dup, "integrity (no loss + no dup) failed");
}
fn parse_shape(s: &str) -> (usize, usize, RingShape) {
match s {
"spsc" => (1, 1, RingShape::Spsc),
"mpsc4" => (4, 1, RingShape::Mpsc),
"mpsc8" => (8, 1, RingShape::Mpsc),
"mpmc4" => (4, 4, RingShape::Mpmc),
"mpmc8" => (8, 8, RingShape::Mpmc),
"vyukov4" => (4, 4, RingShape::Vyukov),
"vyukov8" => (8, 8, RingShape::Vyukov),
other => panic!(
"unknown shape: {other}; expected one of spsc, mpsc4, mpsc8, mpmc4, mpmc8, vyukov4, vyukov8"
),
}
}
fn construct(
locale: &str,
shape: &str,
n_producers: usize,
n_consumers: usize,
) -> Arc<CapacityAdaptiveRing> {
match locale {
"anon" => Arc::new(
CapacityAdaptiveRing::create_anon(n_producers, n_consumers, INITIAL_CAPACITY)
.expect("create_anon"),
),
"file" => {
let path = std::env::temp_dir()
.join(format!("cap_morph_{shape}_{}.bin", std::process::id()));
Arc::new(
CapacityAdaptiveRing::create(&path, n_producers, n_consumers, INITIAL_CAPACITY)
.expect("create file-backed"),
)
}
"shmfs" => {
let name = format!("cap_morph_{shape}_{}", std::process::id());
Arc::new(
CapacityAdaptiveRing::create_shmfs(
&name,
n_producers,
n_consumers,
INITIAL_CAPACITY,
)
.expect("create shmfs"),
)
}
other => panic!("unknown locale: {other}; expected anon, file, or shmfs"),
}
}