#2383·unicorn

Flaky test: arm_movr12_hang intermittently fails on the macos-14 - x86_64 wheel job

Author: retrocpugeekCreated Aug 8, 2026Updated Aug 11, 2026

tests/regress/arm_movr12_hang.py fails intermittently in the cibuildwheel workflow, only in the Building on macos-14 - x86_64 - cp* job:

FAIL: runTest (arm_movr12_hang.MovHang)
  File ".../tests/regress/arm_movr12_hang.py", line 27, in runTest
    self.assertEqual(0x0, uc.reg_read(UC_ARM_REG_R12))
AssertionError: 0 != 291

291 is 0x123, the sentinel written to r12 at line 18 — the movw r12, #0 never executed.

Occurrences

Date PR Job
2026-08-07 #2382 92875026139
2026-07-30 #2377 90842789202
2026-07-04 #2318 85140337567

Each on changes touching neither ARM nor the Python bindings — and it is flaky, not platform-deterministic: in run 28706149643 the same job passed under cp37 and failed under cp38.

Cause

Line 25 gives a cold emu_start 500 microseconds, and the timer is started before vm_start (uc.c:1230), so that budget covers TCG code generation as well as execution. Codegen is most of it: the mapped page is zero-filled and 0x00000000 is a valid andeq r0, r0, r0, so translation runs to the 256-instruction block limit before a single instruction retires.

Natively that work measures ~82 µs (75–102 µs, x86_64 Linux, fresh process, timer disabled) — about 16% of the budget. So steady slowness does not explain the failure by itself, Rosetta 2 included, though the job does run under it (Image: macos-14-arm64, via /usr/bin/arch -x86_64 ...; no QEMU, that step is skipped here).

A scheduling stall is a better fit, and reproduces locally: under 2× CPU oversubscription the current test fails 2.5% of 1000 runs. Such stalls are not limited to loaded machines. The worst off-CPU stall measured 627 µs on an idle 16-core box, 14 ms at 2× load and 81 ms at 6×. All exceed the 500 µs budget, which would also account for the cp37-passes/cp38-fails signature.

The test was skipped for the same-looking failure until dea3c376 (#2100); that dropped QEMU from the Linux jobs, but macOS x86_64 still runs under translation.

Fix

#2385: pre-translate the block with uc_ctl_request_cache so the timeout bounds execution only (approach suggested by @PhilippTakacs below), and raise it to 10 ms for scheduling headroom. Measured 2.50% → 0% under the same load.


Edited: the native timing figure first given here (150–220 µs, "30–45% of the budget") was overstated. It included timer-thread creation, which is not charged against the timeout — the clock starts inside that thread.