Repository navigation
Conversation
…s still decoding. A page job that is decoding when the node is freed posts its follow-up page job with an execution order after the free job, so the follow-up runs on the freed node (and owns the decoder, which leaked). When the next node is allocated at the same address its execution pointer is advanced past its execution counter and the synchronous teardown path spins forever. The free job now re-posts itself so that it always runs last. Adds a resource manager test that loads and unloads the same file asynchronously in a loop.
This branch has not been deployed
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.
Hi,
I use miniaudio in a game engine called NonVisual Gaming Toolkit (NVGT), in a private fork with my own modifications. This PR to the resource manager was made entirely by AI. Here is the problem:
When a decoded data buffer is uninitialized while its node is still paging, the node can be freed while a page job
for it is still queued.
Sequence:
PAGE_DATA_BUFFER_NODEjob (orderk-1) is decoding a page.ma_resource_manager_data_source_uninit().ma_resource_manager_data_buffer_node_unacquire()seesMA_BUSY, sets the node toMA_UNAVAILABLEand postsFREE_DATA_BUFFER_NODEwith orderk.k+1.uninitialized.
In practice the next node is often allocated at the same address. The stale job then advances the new node's
executionPointerpast itsexecutionCounter, and the wait added in22fd206d(
while (executionPointer != executionCounter) ma_yield();) spins forever. Without that wait it is a silentuse-after-free and a leaked decoder.
The fix makes
ma_job_process__resource_manager__free_data_buffer_node()check whether jobs were posted after it(
executionCounter != order + 1). If so, it re-posts itself with a fresh execution order and advances the executionpointer, so the pending page job runs first (it sees
MA_UNAVAILABLE, frees its decoder and finishes) and the freealways happens last. The node has already been removed, so no other thread can post jobs for it, and none of its
jobs can run concurrently with the free job, so the counter is stable at that point.
This PR also adds
tests/resource_manager, registered with CTest. It loads, waits for and unloads the same 100 ms WAV5000 times with
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC, for decoded and encoded buffers and streams, with theinit or done fence, and with 1 and 4 job threads. A watchdog thread fails the test if an iteration doesn't complete
within 5 seconds instead of letting it hang. It builds with
-std=c89 -Wall -Wextra -Wpedanticwithout warnings.Results on Windows 11 (GCC 16.2, MinGW-w64), 30 runs each, 4 processes in parallel:
dev(631161e4)dev+ this fixThis was found while investigating hangs reported by players of an audio game built on NVGT. The investigation,
the fix and the test were done by an AI assistant (Claude).
I release this contribution into the public domain / under MIT No Attribution, as in miniaudio's license.
🤖 Generated with Claude Code