fix(ebsd): BandSimulator uploads take the device lock; wizard tests pin CPU - #125
Merged
Conversation
…in CPU Three identical macOS-CI SIGABRTs landed at the first test_ebsd_wizard test: the only file in the suite that ever resolves MPS (every sibling pins cpu), and BandSimulator.__init__ uploaded the detector geometry OUTSIDE accelerator_lock — constructed by simulate_dictionary and refine_orientations BEFORE their per-chunk locks, so it submitted to Metal concurrent with the band-overlay engine's locked matching. One unlocked participant re-opens the crash, exactly as the device_lock contract documents; also a real-app crash on a Mac wizard rebuild (old overlay matches while the new build's ctor submits). Roster tests pin simulate_dictionary, the ctor and refine_orientations so the next unlocked call site fails on every platform. test_ebsd_wizard gets an autouse SPYDE_EBSD_DEVICE=cpu pin (wiring tests; accelerator work under pytest belongs in a subprocess) with an env escape hatch for a Mac repro: SPYDE_EBSD_DEVICE=mps uv run pytest spyde/tests/migrated/test_ebsd_refine.py spyde/tests/migrated/test_ebsd_wizard.py -v -x
CSSFrancis
marked this pull request as ready for review
August 8, 2026 00:57
GHzytp
pushed a commit
to GHzytp/spyde
that referenced
this pull request
Aug 9, 2026
…t, fold duplicate builds
- the two fixture time.sleep(0.8)s (ebsd_session / lazy_session) become
conftest._settle -- the comment ('let the selector debounce timers fire')
is literally _settle's use case.
- test_eager_data_still_gets_a_bounded_chunking rendered two 64x64-nav
scans (~9 s) to feed pure bytes/shape math; a 16x16 grid proves the same
three properties, with the budget shrunk to 1 MiB so both row counts stay
bytes-derived rather than ny-clamped.
- merges (assertion union, same _build + payload): PC-adoption +
caret-announce ride test_builds_a_wizard_with_a_resident_dictionary; the
ebsd_match stream assert rides the band-draw call that produces it;
hide/show rides the different-position overlay flow; the two-grain colour
check rides test_indexes_the_scan_into_an_ipf_window's identical run.
Kept minimal around the file top / device handling (PR CSSFrancis#125 touches those
regions). File now 22.8 s locally.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the reproducible macOS-CI crash: three identical
Fatal Python error: Aborted(SIGABRT) today (py3.10/3.12/3.13), all at the firsttest_ebsd_wizardtest.Mechanism:
test_ebsd_wizard.pyis the only file in the migrated suite that ever resolves MPS (test_ebsd_refine/indexing/crystal_mappindevice="cpu"on every call; this file pinned nothing, sodefault_device()-> mps on the M-series runners).BandSimulator.__init__uploads the detector geometry to the device with noaccelerator_lock, and both callers (simulate_dictionary,refine_orientations) construct it before their per-chunk locks — so it submitted to Metal concurrently with the band-overlay engine's locked matching (whose worker threadstop()never joins, carrying in-flight compute across the test boundary). One unlocked participant re-opens the crash, exactly as thedevice_lock.pycontract documents. This is also a real-app crash path on a Mac wizard rebuild (the old overlay actively matches while the new build's ctor submits). Green on Aug 3 → fatal now is consistent with amacos-latestimage roll turning the same corruption from latent into a caught Metal assertion.Changes:
spyde/ebsd/refine.py: the four ctor uploads run underaccelerator_lock(device)(null context off MPS; CUDA byte-for-byte unchanged).spyde/tests/migrated/test_device_lock.py: roster additions —simulate_dictionary, theBandSimulatorctor (asserted through the real call chain),refine_orientations.spyde/tests/migrated/test_ebsd_wizard.py: autouseSPYDE_EBSD_DEVICE=cpu(wiring tests, same policy as every sibling and the Windows-CUDA subprocess pattern), overridable for a Mac repro.Validated on Windows/CPU:
test_device_lock.py+test_ebsd_refine.py37 passed; crash-bracketing wizard tests +TestWiring6 passed.Mac confirmation (one command):
Unfixed tree: dies at/after the first
TestBuildDictionarytest. Fixed tree: runs clean — that flip confirms the locked ctor (not the CPU pin) removed the race.Deliberately NOT included: joining the overlay engine thread in
LiveOverlayEngine.stop()— that changes teardown semantics for every overlay and needs a reproduced bug on a Mac first. If the repro command still aborts at teardown with the ctor locked, a boundedjoin(timeout=5)is the next candidate.