mirror of
https://github.com/mackron/miniaudio.git
synced 2026-07-22 12:52:43 +02:00
resource_manager: fix data buffer teardown racing async load job tail
ma_resource_manager_data_buffer_uninit takes a synchronous teardown path as soon as it observes result == MA_SUCCESS, freeing the buffer. But the load job (ma_job_process__resource_manager__load_data_buffer) publishes that result code via a CAS several instructions before it finishes referencing the buffer: it still reads has_connector() and does the executionPointer increment afterward. A concurrent uninit that observes MA_SUCCESS therefore frees the buffer out from under the job tail, which then reads and atomically increments freed (and possibly recycled) memory. The asynchronous free path is safe because it waits on the order / executionPointer ticket scheme; the synchronous shortcut bypassed it. Make the synchronous path wait for executionPointer to catch up to executionCounter before tearing down, so no job is still referencing the buffer. Caught by ThreadSanitizer.
This commit is contained in:
committed by
David Reid
parent
a98cc2a810
commit
22fd206db9
+13
-1
@@ -71470,7 +71470,19 @@ MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) {
|
if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) {
|
||||||
/* The data buffer can be deleted synchronously. */
|
/*
|
||||||
|
The data buffer can be deleted synchronously. Before doing so we must wait for any
|
||||||
|
in-flight load job to fully retire. The load job publishes its result code (which is what
|
||||||
|
makes us take this branch) a few instructions before it finishes referencing pDataBuffer
|
||||||
|
and increments the execution pointer. Tearing the buffer down the instant we observe
|
||||||
|
MA_SUCCESS therefore races the tail of the load job, which then reads and writes freed
|
||||||
|
memory. Wait for the execution pointer to catch up to the execution counter so that no job
|
||||||
|
is still referencing the buffer, exactly as the asynchronous path relies on for ordering.
|
||||||
|
*/
|
||||||
|
while (ma_atomic_load_32(&pDataBuffer->executionPointer) != ma_atomic_load_32(&pDataBuffer->executionCounter)) {
|
||||||
|
ma_yield();
|
||||||
|
}
|
||||||
|
|
||||||
return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
|
return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user