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

CNA snapshot 009d40f5  ·  Known Issues › Verification gaps  ·  source links pinned to 009d40f5

✓

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

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.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.