CNA-BUG-213: audio.h and AUDIO.md call cna_dynamic_sound_effect_instance_submit_buffer safe from a producer thread, but every call off the creation thread returns CNA_RESULT_THREAD
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.
The submission routes resolve the instance through HandleRegistry::Get, which refuses any thread other than the handle's creation thread, so the documented canonical producer-thread usage fails; AUDIO.md's own threading section states the stricter rule.
- Identifier
CNA-BUG-213- Category
- Bug
- Subsystem
- C API & bindings
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Low (a triage suggestion, not a project priority)
- Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- cna_dynamic_sound_effect_instance_submit_buffer, cna_dynamic_sound_effect_instance_submit_float_buffer_ext
Expected behaviour
The Doxygen of cna_dynamic_sound_effect_instance_submit_buffer in audio.h says copying the bytes “is what makes this route safe to call from a producer thread while playback runs, which is the canonical usage”; AUDIO.md repeats it for streaming instances.
Actual behaviour at TARGET
Both routes in CnaCApiAudio.cpp call BorrowDynamicInstance → GetSoundEffectInstance → HandleRegistry::Get, and Get in CnaCApiDetail.hpp returns CNA_RESULT_THREAD whenever the calling thread is not the slot's creation thread. AUDIO.md's “Threads, hardware and shutdown” section says every audio call runs on the game creation thread, so the documents contradict each other and the code follows the stricter statement.
Source locations
modules/c-api/src/CnaCApiAudio.cpp— cna_dynamic_sound_effect_instance_submit_buffer, BorrowDynamicInstance, GetSoundEffectInstancemodules/c-api/src/CnaCApiDetail.hpp— HandleRegistry::Get creation-thread checkmodules/c-api/include/CNA/C/audio.h— producer-thread statement in the submit_buffer Doxygendocs/c-api/AUDIO.md— streaming paragraph versus the threading sectionmodules/c-api/tests/pure_c/AudioStreamingSmoke.c— streaming test, single-threaded
Evidence
Checked by reading at 009d40f5; not executed.
Focused reproduction
/* Illustrative C; not compiled. 'instance' was created on the game thread. */
static int producer(void* arg) {
CNA_Handle instance = *(CNA_Handle*)arg;
uint8_t pcm[4096] = {0};
return (int)cna_dynamic_sound_effect_instance_submit_buffer(instance, pcm, sizeof pcm, 0, 4096);
}
/* Expected by audio.h: CNA_RESULT_SUCCESS. Actual by reading: CNA_RESULT_THREAD. */
Current tests
AudioStreamingSmoke.c submits from the creation thread only.
Regression test
A pure-C test that submits from a second thread and asserts whichever contract is chosen; then align audio.h and AUDIO.md (or relax the thread check for these two routes, if the native instance is safe for it).
Blast radius
C and binding code that follows the header and feeds a streaming instance from an audio producer thread. Single-threaded submission is unaffected.
Workaround
Marshal submissions to the game creation thread.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- User guide
- C API: limits
- Internals
- C API internals: audio
- Known issues
- Bug index