CNA-VGAP-058: No dedicated test drives the production audio shutdown path (static destruction of the SDL3 device at exit with callbacks in flight): the teardown tests call DestroyMixer, and plan item AUD-15-016 is unchecked
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
DestroyMixer has no production caller, so a game's audio ends through g_audioDevice's static destructor at exit, but the dedicated teardown tests drive DestroyMixer, and plans/plan_audio.md item AUD-15-016 (shutdown with callbacks in flight) is unchecked.
- Identifier
CNA-VGAP-058- Category
- Verification gap
- Subsystem
- Testing & evidence
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- Shutdown of the SDL3 audio device at process exit (Sdl3AudioDevice::Close from static destruction) with mixer callbacks in flight
Expected behaviour
The shutdown a shipped game performs (returning from main with the SDL3 mixer open and voices playing, so that static destruction closes the device while the mixer thread may still call back, or after a host SDL_Quit that IPlatform.hpp says applications may call) has a test showing no late access to static destruction order, which is the acceptance CNA's own plan sets for AUD-15-016.
Actual behaviour at TARGET
AudioMixer.hpp states that DestroyMixer has no production caller and that native mixer state and the device are reclaimed by the OS at exit; at exit Sdl3AudioDevice::Close runs from the static g_audioDevice destructor. Sdl3AudioDeviceTests pin Close's callback barrier in isolation, but AudioMixerTests and the two mixer_destroy_active harnesses drive DestroyMixer, a route no game takes (both harnesses destroy the mixer before main returns), and audio_no_hardware_harness covers only a failed open. No dedicated test returns from main with a live device and playing voices; the exit path runs only implicitly when a CnaTests process ends. plan_audio.md lists 'Test process shutdown with audio callbacks in flight' (AUD-15-016) unchecked, beside two open, undiagnosed crash records that CNA-VGAP-034 covers: AUD-15-021 (an intermittent segfault at the start of one mixer test late in the audio-scoped run, not at exit) and AUD-15-022 (a heap corruption at process exit, during the test environment's tear-down). By reading, Close stops the stream behind SDL's stream lock before destroying it and the callback is shared-owned; the ordering against other statics is not exercised by any dedicated test.
Source locations
modules/audio/include/CNA/Internal/Audio/AudioMixer.hpp— DestroyMixer comment: no production caller, reclaimed at exitmodules/audio/src/Backend/Sdl3Mixer/AudioMixer.cpp— g_audioDevice static and DestroyMixermodules/audio/src/Platform/Sdl3/Sdl3AudioDevice.cpp— Close and StopLockedmodules/audio/tests/CNA/Audio/Platform/Sdl3AudioDeviceTests.cpp— device-level barrier testsmodules/audio/tests/CNA/Internal/Audio/AudioMixerTests.cpp— repeated DestroyMixer cyclestools/audio/mixer_destroy_active_static_voice_harness.cpp— calls DestroyMixer with a live voicetools/audio/audio_no_hardware_harness.cpp— failed-open path onlyplans/plan_audio.md— AUD-15-016 unchecked
Evidence
Read at 009d40f5; nothing executed. Read the mixer and device sources, the device tests' names, the harness headers, a search of the audio tests for exit, in-flight and static-destruction coverage (none) and the AUD-15 items in plan_audio.md, which date from 2026-07-18 and were not updated. The missing test is established by search, not by a run.
Focused reproduction
No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.
Current tests
Sdl3AudioDeviceTests pins the callback barrier of Close in isolation; AudioMixerTests and the two mixer_destroy_active harnesses drive DestroyMixer, which no shipped game calls; audio_no_hardware_harness covers a failed open only.
Regression test
A harness that opens the SDL3 mixer, starts voices and returns from main (and one that calls SDL_Quit first) under AddressSanitizer, asserting no late access during static destruction — the acceptance CNA's plan sets for AUD-15-016.
Blast radius
Process exit of games that use the SDL3 mixer while sounds are playing; the failure mode, if any, would be a crash or a sanitizer report at exit rather than a wrong result.
Workaround
None is needed by reading (Close stops the stream behind SDL's stream lock and the callback is shared-owned); the gap is that no dedicated test shows it.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Internals
- Threading: audio callbacks
- Deep dives
- Sound effects and streaming
- Known issues
- Verification gap index