mlx-native 0.10.6

Pure-Rust Metal GPU compute library for MLX-compatible inference on Apple Silicon
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
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
//! Regression for command-buffer autorelease accumulation on long-lived Rust threads.
//!
//! `-[MTLCommandQueue commandBuffer]` returns an autoreleased object.  A
//! worker thread without a local autorelease pool can therefore retain every
//! command buffer until AGX blocks the next allocation.  The production Qwen
//! serving receipt reached that ceiling with 37,999 live command-buffer
//! objects and a worker blocked in `-[MTLCommandQueue commandBuffer]`.

#![allow(clippy::expect_used, unexpected_cfgs)]

use std::io::{BufRead, BufReader, Read, Write};
use std::os::unix::net::{UnixListener, UnixStream};
use std::process::{Child, Command, Stdio};
use std::sync::mpsc;
use std::thread;
use std::time::{Duration, Instant};

use metal::ComputePipelineDescriptor;
use mlx_native::MlxDevice;
use objc::{msg_send, runtime::Object, sel, sel_impl};

const CHILD_ENV: &str = "MLX_COMMAND_BUFFER_AUTORELEASE_CHILD";
const TEST_NAME: &str = "uncommitted_command_buffers_are_reclaimed_on_poolless_workers";
const ITERATIONS: usize = 50_000;
const CHILD_TIMEOUT: Duration = Duration::from_secs(120);
const LABEL_CHILD_ENV: &str = "MLX_LABEL_CFSTRING_CHILD_MODE";
const LABEL_SOCKET_ENV: &str = "MLX_LABEL_CFSTRING_CONTROL_SOCKET";
const LABEL_TEST_NAME: &str = "labeled_commits_have_bounded_cfstring_population";
const LABEL_WARMUP_ITERATIONS: usize = 256;
const LABEL_WAVE_ITERATIONS: usize = 10_000;
const LABEL_CHILD_TIMEOUT: Duration = Duration::from_secs(120);
const HEAP_TIMEOUT: Duration = Duration::from_secs(20);
const CHECKPOINT_PREFIX: &str = "MLX_CFSTRING_CHECKPOINT";
const LABEL_MODES: [&str; 6] = [
    "sync",
    "async-drop",
    "async-wait",
    "unlabeled-async",
    "command-buffer-only",
    "compute-encoder-only",
];

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct HeapPopulation {
    cfstrings: usize,
    command_buffers: usize,
    autorelease_pool_pages: usize,
}

fn parse_count(token: &str) -> Result<usize, String> {
    token
        .replace(',', "")
        .parse::<usize>()
        .map_err(|error| format!("invalid heap count {token:?}: {error}"))
}

fn parse_heap_population(report: &str) -> Result<HeapPopulation, String> {
    let mut cfstrings = None;
    let mut command_buffers = 0usize;
    let mut autorelease_pool_pages = None;

    for line in report.lines() {
        let fields = line.split_ascii_whitespace().collect::<Vec<_>>();
        if fields.len() < 5 {
            continue;
        }
        let object_type = fields[3];
        if object_type == "CFString" && fields[4] == "ObjC" {
            let count = parse_count(fields[0])?;
            if cfstrings.replace(count).is_some() {
                return Err("duplicate exact `CFString ObjC` heap row".to_string());
            }
            continue;
        }
        if (object_type.contains("FamilyCommandBuffer")
            || object_type.starts_with("IOGPUMetalCommandBuffer"))
            && !object_type.contains("StoragePool")
        {
            command_buffers = command_buffers
                .checked_add(parse_count(fields[0])?)
                .ok_or_else(|| "command-buffer heap count overflow".to_string())?;
            continue;
        }
        if object_type == "@autoreleasepool" && fields[4] == "content" {
            let count = parse_count(fields[0])?;
            if autorelease_pool_pages.replace(count).is_some() {
                return Err("duplicate `@autoreleasepool content` heap row".to_string());
            }
        }
    }

    Ok(HeapPopulation {
        cfstrings: cfstrings.ok_or_else(|| "missing exact `CFString ObjC` heap row".to_string())?,
        command_buffers,
        autorelease_pool_pages: autorelease_pool_pages
            .ok_or_else(|| "missing exact `@autoreleasepool content` heap row".to_string())?,
    })
}

fn wait_for_process(child: &mut Child, timeout: Duration) -> Result<(), String> {
    let deadline = Instant::now() + timeout;
    loop {
        match child
            .try_wait()
            .map_err(|error| format!("poll child process: {error}"))?
        {
            Some(status) if status.success() => return Ok(()),
            Some(status) => return Err(format!("child process failed with {status}")),
            None if Instant::now() < deadline => thread::sleep(Duration::from_millis(10)),
            None => {
                let _ = child.kill();
                let _ = child.wait();
                return Err(format!("child process exceeded {timeout:?}"));
            }
        }
    }
}

fn heap_population(pid: u32) -> Result<HeapPopulation, String> {
    let mut heap = Command::new("/usr/bin/heap")
        .args(["-q", &pid.to_string()])
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .map_err(|error| format!("spawn /usr/bin/heap for pid {pid}: {error}"))?;
    let mut stdout = heap
        .stdout
        .take()
        .ok_or_else(|| "heap stdout pipe missing".to_string())?;
    let mut stderr = heap
        .stderr
        .take()
        .ok_or_else(|| "heap stderr pipe missing".to_string())?;
    let stdout_reader = thread::spawn(move || {
        let mut bytes = Vec::new();
        stdout.read_to_end(&mut bytes).map(|_| bytes)
    });
    let stderr_reader = thread::spawn(move || {
        let mut bytes = Vec::new();
        stderr.read_to_end(&mut bytes).map(|_| bytes)
    });
    let process_result = wait_for_process(&mut heap, HEAP_TIMEOUT);
    let stdout = stdout_reader
        .join()
        .map_err(|_| "heap stdout reader panicked".to_string())?
        .map_err(|error| format!("read heap stdout: {error}"))?;
    let stderr = stderr_reader
        .join()
        .map_err(|_| "heap stderr reader panicked".to_string())?
        .map_err(|error| format!("read heap stderr: {error}"))?;
    process_result?;
    if !stderr.is_empty() {
        return Err(format!(
            "/usr/bin/heap emitted stderr: {}",
            String::from_utf8_lossy(&stderr)
        ));
    }
    let report =
        String::from_utf8(stdout).map_err(|error| format!("heap stdout is not UTF-8: {error}"))?;
    parse_heap_population(&report)
}

fn emit_checkpoint(control: &mut UnixStream, phase: usize) {
    let pid = std::process::id();
    writeln!(control, "{CHECKPOINT_PREFIX}\tv1\t{phase}\t{pid}").expect("write heap checkpoint");
    control.flush().expect("flush heap checkpoint");
    let mut response = String::new();
    BufReader::new(control.try_clone().expect("clone heap control socket"))
        .read_line(&mut response)
        .expect("read heap checkpoint continuation");
    assert_eq!(
        response.trim_end(),
        format!("CONTINUE\tv1\t{phase}"),
        "parent must acknowledge the exact heap checkpoint"
    );
}

fn drain_queue(device: &MlxDevice, ordinal: usize) {
    let mut drain = device
        .command_encoder()
        .unwrap_or_else(|error| panic!("create label-lifetime drain {ordinal}: {error}"));
    drain
        .commit_and_wait()
        .unwrap_or_else(|error| panic!("drain label-lifetime queue {ordinal}: {error}"));
}

fn run_label_iterations(
    mode: &str,
    device: &MlxDevice,
    pipeline: &metal::ComputePipelineStateRef,
    start: usize,
    count: usize,
) {
    for ordinal in start..start + count {
        let mut encoder = device
            .command_encoder()
            .unwrap_or_else(|error| panic!("create {mode} command buffer {ordinal}: {error}"));
        if mode == "command-buffer-only" {
            objc::rc::autoreleasepool(|| {
                encoder
                    .metal_command_buffer()
                    .set_label("autorelease.label.population");
            });
            encoder
                .commit_and_wait()
                .unwrap_or_else(|error| panic!("commit command-buffer label {ordinal}: {error}"));
            drop(encoder);
            continue;
        }
        if mode == "compute-encoder-only" {
            let active_encoder = objc::rc::autoreleasepool(|| {
                let active_encoder = encoder.metal_command_buffer().new_compute_command_encoder();
                // Mirror CommandEncoder's production borrowed +0 -> explicit
                // +1 lifetime extension across the factory pool.
                let _: *mut Object = unsafe { msg_send![active_encoder, retain] };
                active_encoder as *const metal::ComputeCommandEncoderRef
            });
            objc::rc::autoreleasepool(|| unsafe {
                (&*active_encoder).set_label("autorelease.label.population");
                (&*active_encoder).end_encoding();
                let nil_label: *const Object = std::ptr::null();
                let _: () = msg_send![active_encoder, setLabel: nil_label];
                let _: () = msg_send![active_encoder, release];
            });
            encoder
                .commit_and_wait()
                .unwrap_or_else(|error| panic!("commit compute-encoder label {ordinal}: {error}"));
            drop(encoder);
            continue;
        }
        encoder.set_pipeline(pipeline);
        match mode {
            "sync" => encoder
                .commit_and_wait_labeled("autorelease.label.population")
                .unwrap_or_else(|error| panic!("sync labeled commit {ordinal}: {error}")),
            "async-drop" => encoder.commit_labeled("autorelease.label.population"),
            "async-wait" => {
                encoder.commit_labeled("autorelease.label.population");
                encoder
                    .wait_until_completed()
                    .unwrap_or_else(|error| panic!("wait labeled commit {ordinal}: {error}"));
            }
            "unlabeled-async" => encoder.commit(),
            other => panic!("unknown label-lifetime mode {other}"),
        }
        drop(encoder);
        if !matches!(mode, "sync" | "async-wait") && ordinal % 32 == 31 {
            drain_queue(device, ordinal);
        }
    }
    drain_queue(device, start + count);
}

fn run_label_population_child(mode: &str) {
    let worker_mode = mode.to_string();
    thread::spawn(move || {
        let socket_path = std::env::var(LABEL_SOCKET_ENV)
            .expect("parent must provide the heap control socket path");
        let mut control = UnixStream::connect(&socket_path)
            .unwrap_or_else(|error| panic!("connect heap control socket {socket_path}: {error}"));
        control
            .set_read_timeout(Some(LABEL_CHILD_TIMEOUT))
            .expect("set heap control read timeout");
        let device = MlxDevice::new().expect("create Metal device");
        let pipeline = build_noop_pipeline(device.metal_device());
        if worker_mode == "negative-control" {
            emit_checkpoint(&mut control, 0);
            let encoder = device
                .command_encoder()
                .expect("create negative-control command buffer");
            for _ in 0..2_048 {
                encoder
                    .metal_command_buffer()
                    .set_label("autorelease.label.negative_control");
            }
            emit_checkpoint(&mut control, 2_048);
            drop(encoder);
            return;
        }

        run_label_iterations(&worker_mode, &device, &pipeline, 0, LABEL_WARMUP_ITERATIONS);
        emit_checkpoint(&mut control, 0);
        run_label_iterations(
            &worker_mode,
            &device,
            &pipeline,
            LABEL_WARMUP_ITERATIONS,
            LABEL_WAVE_ITERATIONS,
        );
        emit_checkpoint(&mut control, LABEL_WAVE_ITERATIONS);
        run_label_iterations(
            &worker_mode,
            &device,
            &pipeline,
            LABEL_WARMUP_ITERATIONS + LABEL_WAVE_ITERATIONS,
            LABEL_WAVE_ITERATIONS,
        );
        emit_checkpoint(&mut control, 2 * LABEL_WAVE_ITERATIONS);
    })
    .join()
    .expect("label-population worker panicked");
}

fn wait_for_checkpoint(
    receiver: &mpsc::Receiver<String>,
    expected_phase: usize,
    expected_pid: u32,
) -> Result<(), String> {
    let deadline = Instant::now() + LABEL_CHILD_TIMEOUT;
    loop {
        if Instant::now() >= deadline {
            return Err(format!(
                "heap checkpoint {expected_phase} exceeded {LABEL_CHILD_TIMEOUT:?}"
            ));
        }
        let remaining = deadline.saturating_duration_since(Instant::now());
        let line = receiver
            .recv_timeout(remaining)
            .map_err(|error| format!("wait for heap checkpoint {expected_phase}: {error}"))?;
        if line.starts_with("READ_ERROR:") {
            return Err(format!("read heap control socket: {line}"));
        }
        if !line.starts_with(CHECKPOINT_PREFIX) {
            continue;
        }
        let fields = line.trim_end().split('\t').collect::<Vec<_>>();
        if fields.len() != 4
            || fields[0] != CHECKPOINT_PREFIX
            || fields[1] != "v1"
            || fields[2] != expected_phase.to_string()
            || fields[3] != expected_pid.to_string()
        {
            return Err(format!(
                "malformed/out-of-order heap checkpoint: expected phase={expected_phase} pid={expected_pid}, got {line:?}"
            ));
        }
        return Ok(());
    }
}

fn signed_delta(after: usize, before: usize) -> i64 {
    i64::try_from(after).expect("heap count fits i64")
        - i64::try_from(before).expect("heap count fits i64")
}

fn run_heap_child(mode: &str, phases: &[usize]) -> Result<Vec<HeapPopulation>, String> {
    let executable =
        std::env::current_exe().map_err(|error| format!("locate test binary: {error}"))?;
    let socket_path = format!("/tmp/mlx-label-cfstring-{}-{mode}.sock", std::process::id());
    let _ = std::fs::remove_file(&socket_path);
    let listener = UnixListener::bind(&socket_path)
        .map_err(|error| format!("bind heap control socket {socket_path}: {error}"))?;
    listener
        .set_nonblocking(true)
        .map_err(|error| format!("set heap control listener nonblocking: {error}"))?;
    let mut child = match Command::new(executable)
        .args([
            "--exact",
            LABEL_TEST_NAME,
            "--nocapture",
            "--test-threads=1",
        ])
        .env(LABEL_CHILD_ENV, mode)
        .env(LABEL_SOCKET_ENV, &socket_path)
        .env_remove("MLX_PROFILE_CB")
        .env_remove("MLX_PROFILE_DISPATCH")
        .stdin(Stdio::null())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .spawn()
    {
        Ok(child) => child,
        Err(error) => {
            let _ = std::fs::remove_file(&socket_path);
            return Err(format!("spawn label-population child for {mode}: {error}"));
        }
    };
    let pid = child.id();
    let accept_deadline = Instant::now() + LABEL_CHILD_TIMEOUT;
    let mut control = loop {
        match listener.accept() {
            Ok((stream, _)) => break stream,
            Err(error)
                if error.kind() == std::io::ErrorKind::WouldBlock
                    && Instant::now() < accept_deadline =>
            {
                match child.try_wait() {
                    Ok(Some(status)) => {
                        let _ = std::fs::remove_file(&socket_path);
                        return Err(format!(
                            "label-population child exited before socket accept with {status}"
                        ));
                    }
                    Ok(None) => {}
                    Err(poll_error) => {
                        let _ = child.kill();
                        let _ = child.wait();
                        let _ = std::fs::remove_file(&socket_path);
                        return Err(format!(
                            "poll label-population child before socket accept: {poll_error}"
                        ));
                    }
                }
                thread::sleep(Duration::from_millis(10));
            }
            Err(error) => {
                let _ = child.kill();
                let _ = child.wait();
                let _ = std::fs::remove_file(&socket_path);
                return Err(format!("accept heap control socket: {error}"));
            }
        }
    };
    let _ = std::fs::remove_file(&socket_path);
    let control_reader = match (|| {
        control
            .set_nonblocking(false)
            .map_err(|error| format!("restore blocking heap control socket: {error}"))?;
        control
            .set_write_timeout(Some(LABEL_CHILD_TIMEOUT))
            .map_err(|error| format!("set heap control write timeout: {error}"))?;
        control
            .try_clone()
            .map_err(|error| format!("clone parent heap control socket: {error}"))
    })() {
        Ok(reader) => reader,
        Err(error) => {
            let _ = child.kill();
            let _ = child.wait();
            return Err(error);
        }
    };
    let (sender, receiver) = mpsc::channel();
    let reader = thread::spawn(move || {
        for line in BufReader::new(control_reader).lines() {
            if sender
                .send(line.unwrap_or_else(|error| format!("READ_ERROR:{error}")))
                .is_err()
            {
                break;
            }
        }
    });

    let result = (|| {
        let mut populations = Vec::with_capacity(phases.len());
        for &phase in phases {
            wait_for_checkpoint(&receiver, phase, pid)?;
            populations.push(heap_population(pid)?);
            writeln!(control, "CONTINUE\tv1\t{phase}")
                .map_err(|error| format!("continue child at phase {phase}: {error}"))?;
            control
                .flush()
                .map_err(|error| format!("flush child continuation at phase {phase}: {error}"))?;
        }
        drop(control);
        wait_for_process(&mut child, LABEL_CHILD_TIMEOUT)?;
        Ok(populations)
    })();

    if result.is_err() {
        let _ = child.kill();
        let _ = child.wait();
    }
    let _ = reader.join();
    result
}

fn build_noop_pipeline(device: &metal::DeviceRef) -> metal::ComputePipelineState {
    let source = r#"
        #include <metal_stdlib>
        using namespace metal;
        kernel void command_buffer_autorelease_noop() {}
    "#;
    let library = device
        .new_library_with_source(source, &metal::CompileOptions::new())
        .expect("compile autorelease no-op kernel");
    let function = library
        .get_function("command_buffer_autorelease_noop", None)
        .expect("load autorelease no-op kernel");
    let descriptor = ComputePipelineDescriptor::new();
    descriptor.set_compute_function(Some(&function));
    device
        .new_compute_pipeline_state(&descriptor)
        .expect("create autorelease no-op pipeline")
}

/// Exercise both production lifetime shapes far beyond the observed cliff.
///
/// The child process is load-bearing: a regression blocks inside Objective-C
/// before Rust can return an error, so an in-process timeout would leave a
/// wedged Metal thread behind in the test harness.  The parent kills the
/// isolated child and fails on a generous hosted-runner deadline instead.
#[test]
fn uncommitted_command_buffers_are_reclaimed_on_poolless_workers() {
    if std::env::var_os(CHILD_ENV).is_some() {
        let device = MlxDevice::new().expect("create Metal device");
        let pipeline = build_noop_pipeline(device.metal_device());
        for ordinal in 0..ITERATIONS {
            let mut encoder = device
                .command_encoder()
                .unwrap_or_else(|error| panic!("create command buffer {ordinal}: {error}"));
            // `set_pipeline` lazily opens the production concurrent compute
            // encoder. Dropping it without committing covers the raw retained
            // pointer crossing the local autorelease-pool drain.
            encoder.set_pipeline(&pipeline);
            drop(encoder);
        }

        // Exercise the Metal label bridge at the same cumulative scale. The
        // async commits preserve throughput; a periodic synchronous sentinel
        // drains the queue and keeps this a lifetime test rather than an
        // in-flight-depth test.
        for ordinal in 0..ITERATIONS {
            let mut encoder = device
                .command_encoder()
                .unwrap_or_else(|error| panic!("create labeled command buffer {ordinal}: {error}"));
            // Keep a compute encoder active so `commit_labeled` exercises
            // both command-buffer and compute-encoder label setters.
            encoder.set_pipeline(&pipeline);
            encoder.commit_labeled("autorelease.label.churn");
            if ordinal % 32 == 31 {
                let mut drain = device
                    .command_encoder()
                    .unwrap_or_else(|error| panic!("create label drain {ordinal}: {error}"));
                drain
                    .commit_and_wait()
                    .unwrap_or_else(|error| panic!("drain labeled commands {ordinal}: {error}"));
            }
        }

        // Reproduce hf2q's exact former final-layer lifecycle: drain the
        // session, rotate to a fresh empty CB, then drop without submitting
        // that replacement.  Before the scoped pools, the autoreleased +0
        // object survived every Rust-owned +1 drop on this pool-less thread.
        for ordinal in 0..ITERATIONS {
            let mut session = device
                .encoder_session()
                .unwrap_or_else(|error| panic!("create encoder session {ordinal}: {error}"))
                .expect("child enables encoder sessions");
            session
                .commit_and_wait()
                .unwrap_or_else(|error| panic!("commit encoder session {ordinal}: {error}"));
            session
                .reset_for_next_stage()
                .unwrap_or_else(|error| panic!("reset encoder session {ordinal}: {error}"));
            drop(session);
        }

        let mut sentinel = device
            .command_encoder()
            .expect("create sentinel command buffer");
        sentinel
            .commit_and_wait()
            .expect("commit sentinel command buffer");
        return;
    }

    let executable = std::env::current_exe().expect("locate test executable");
    let mut child = Command::new(executable)
        .args(["--exact", TEST_NAME, "--nocapture", "--test-threads=1"])
        .env(CHILD_ENV, "1")
        .env("HF2Q_ENCODER_SESSION", "1")
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .expect("spawn isolated command-buffer regression child");

    let deadline = Instant::now() + CHILD_TIMEOUT;
    loop {
        if let Some(status) = child.try_wait().expect("poll regression child") {
            assert!(status.success(), "regression child failed with {status}");
            break;
        }
        if Instant::now() >= deadline {
            child.kill().expect("kill wedged regression child");
            let _ = child.wait();
            panic!("command-buffer allocation wedged after autorelease accumulation");
        }
        thread::sleep(Duration::from_millis(10));
    }
}

#[test]
fn labeled_commits_have_bounded_cfstring_population() {
    if let Ok(mode) = std::env::var(LABEL_CHILD_ENV) {
        run_label_population_child(&mode);
        return;
    }

    let negative = run_heap_child("negative-control", &[0, 2_048])
        .expect("heap must observe the deliberately unpooled label control");
    let negative_delta = signed_delta(negative[1].cfstrings, negative[0].cfstrings);
    assert!(
        negative_delta >= 1_024,
        "heap detector is blind to the known unpooled label leak: {negative:?}, delta={negative_delta}"
    );

    let mut violations = Vec::new();
    for mode in LABEL_MODES {
        let populations =
            run_heap_child(mode, &[0, LABEL_WAVE_ITERATIONS, 2 * LABEL_WAVE_ITERATIONS])
                .unwrap_or_else(|error| panic!("run {mode} label-population child: {error}"));
        let first_delta = signed_delta(populations[1].cfstrings, populations[0].cfstrings);
        let second_delta = signed_delta(populations[2].cfstrings, populations[1].cfstrings);
        let total_delta = signed_delta(populations[2].cfstrings, populations[0].cfstrings);
        let first_pool_delta = signed_delta(
            populations[1].autorelease_pool_pages,
            populations[0].autorelease_pool_pages,
        );
        let second_pool_delta = signed_delta(
            populations[2].autorelease_pool_pages,
            populations[1].autorelease_pool_pages,
        );
        eprintln!(
            "label lifetime mode={mode} populations={populations:?} cfstring_deltas=[{first_delta},{second_delta}] pool_page_deltas=[{first_pool_delta},{second_pool_delta}]"
        );

        if !populations.iter().all(|sample| sample.command_buffers == 0) {
            violations.push(format!(
                "{mode} retained live Metal command buffers at a drained checkpoint: {populations:?}"
            ));
        }
        if first_delta > 256 || second_delta > 256 || total_delta > 512 {
            violations.push(format!(
                "{mode} retained workload-linear CFStrings: populations={populations:?}, deltas=[{first_delta},{second_delta},{total_delta}]"
            ));
        }
        if first_pool_delta > 8 || second_pool_delta > 8 {
            violations.push(format!(
                "{mode} retained workload-linear autorelease-pool pages: populations={populations:?}, deltas=[{first_pool_delta},{second_pool_delta}]"
            ));
        }
    }
    assert!(violations.is_empty(), "{}", violations.join("\n"));
}

#[test]
fn heap_population_parser_is_fail_closed() {
    let report = r#"
      101,820    4,837,312      47.5   CFString                                          ObjC    CoreFoundation
          210      860,160    4096.0   @autoreleasepool content                          C       libobjc.A.dylib
            4          224      56.0   CFString (Storage)                                C       CoreFoundation
            2        1,792     896.0   AGXG17XFamilyCommandBuffer                       ObjC    AGXMetalG17X
            1          896     896.0   AGXG17XFamilyCommandBuffer._impl                 C++     AGXMetalG17X
            1          128     128.0   AGXG17CDevice._commandBufferStoragePool          C++     AGXMetalG17X
    "#;
    assert_eq!(
        parse_heap_population(report).expect("parse representative heap rows"),
        HeapPopulation {
            cfstrings: 101_820,
            command_buffers: 3,
            autorelease_pool_pages: 210,
        }
    );

    let missing = "4 224 56.0 CFString (Storage) C CoreFoundation";
    assert!(
        parse_heap_population(missing).is_err(),
        "storage-only CFString row must not satisfy the exact ObjC population gate"
    );
    let duplicate =
        "1 48 48.0 CFString ObjC CoreFoundation\n2 96 48.0 CFString ObjC CoreFoundation";
    assert!(
        parse_heap_population(duplicate).is_err(),
        "duplicate exact CFString rows must fail closed"
    );
    let missing_pool = "1 48 48.0 CFString ObjC CoreFoundation";
    assert!(
        parse_heap_population(missing_pool).is_err(),
        "missing autorelease-pool population row must fail closed"
    );
}