mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-24 09:14:04 +02:00
Fix bugs in the resource manager.
This commit is contained in:
+52
-30
@@ -412,9 +412,11 @@ MA_API ma_result ma_resource_manager_data_source_get_looping(ma_resource_manager
|
||||
|
||||
/* Job management. */
|
||||
MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob);
|
||||
MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */
|
||||
MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob);
|
||||
MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob);
|
||||
|
||||
|
||||
/*
|
||||
Engine
|
||||
======
|
||||
@@ -848,9 +850,10 @@ MA_API ma_result ma_job_queue_init(ma_uint32 flags, ma_job_queue* pQueue)
|
||||
just a dummy item for giving us the first item in the list which is stored in the "next" member.
|
||||
*/
|
||||
ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */
|
||||
pQueue->jobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE;
|
||||
pQueue->tail = pQueue->head;
|
||||
|
||||
pQueue->jobs[pQueue->head].next = MA_JOB_ID_NONE;
|
||||
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
@@ -900,8 +903,8 @@ MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob)
|
||||
tail = pQueue->tail;
|
||||
next = pQueue->jobs[ma_job_extract_slot(tail)].next;
|
||||
|
||||
if (tail == pQueue->tail) {
|
||||
if (next == MA_JOB_ID_NONE) {
|
||||
if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(pQueue->tail)) {
|
||||
if (ma_job_extract_slot(next) == 0xFFFF) {
|
||||
if (c89atomic_compare_and_swap_64(&pQueue->jobs[ma_job_extract_slot(tail)].next, next, slot) == next) {
|
||||
break;
|
||||
}
|
||||
@@ -942,9 +945,9 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob)
|
||||
tail = pQueue->tail;
|
||||
next = pQueue->jobs[ma_job_extract_slot(head)].next;
|
||||
|
||||
if (head == pQueue->head) {
|
||||
if (head == tail) {
|
||||
if (next == MA_JOB_ID_NONE) {
|
||||
if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(pQueue->head)) {
|
||||
if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(tail)) {
|
||||
if (ma_job_extract_slot(next) == 0xFFFF) {
|
||||
return MA_NO_DATA_AVAILABLE;
|
||||
}
|
||||
c89atomic_compare_and_swap_64(&pQueue->tail, tail, next);
|
||||
@@ -1545,7 +1548,6 @@ static void ma_resource_manager_delete_all_data_buffers(ma_resource_manager* pRe
|
||||
|
||||
MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager)
|
||||
{
|
||||
ma_job quitJob;
|
||||
ma_uint32 iJobThread;
|
||||
|
||||
if (pResourceManager == NULL) {
|
||||
@@ -1556,8 +1558,7 @@ MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager)
|
||||
Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the
|
||||
queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it.
|
||||
*/
|
||||
quitJob = ma_job_init(MA_JOB_QUIT);
|
||||
ma_resource_manager_post_job(pResourceManager, &quitJob);
|
||||
ma_resource_manager_post_job_quit(pResourceManager);
|
||||
|
||||
/* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */
|
||||
for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
|
||||
@@ -2545,7 +2546,7 @@ static ma_result ma_resource_manager_data_source_map(ma_data_source* pDataSource
|
||||
}
|
||||
}
|
||||
|
||||
/* The frame cursor is increment in unmap(). */
|
||||
/* The frame cursor is incremented in unmap(). */
|
||||
return ma_data_source_map(ma_resource_manager_data_source_get_buffer_connector(pRMDataSource), ppFramesOut, pFrameCount);
|
||||
}
|
||||
|
||||
@@ -2920,6 +2921,12 @@ MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceMana
|
||||
return ma_job_queue_post(&pResourceManager->jobQueue, pJob);
|
||||
}
|
||||
|
||||
MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager)
|
||||
{
|
||||
ma_job job = ma_job_init(MA_JOB_QUIT);
|
||||
return ma_resource_manager_post_job(pResourceManager, &job);
|
||||
}
|
||||
|
||||
MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob)
|
||||
{
|
||||
if (pResourceManager == NULL) {
|
||||
@@ -2934,6 +2941,11 @@ static ma_result ma_resource_manager_process_job__load_data_buffer(ma_resource_m
|
||||
{
|
||||
ma_result result = MA_SUCCESS;
|
||||
ma_resource_manager_data_buffer* pDataBuffer;
|
||||
ma_decoder* pDecoder = NULL; /* Malloc'd here, and then free'd on the last page decode. */
|
||||
ma_uint64 totalFrameCount = 0;
|
||||
void* pData = NULL;
|
||||
ma_uint64 dataSizeInBytes = 0;
|
||||
ma_uint64 framesRead = 0; /* <-- Keeps track of how many frames we read for the first page. */
|
||||
|
||||
MA_ASSERT(pResourceManager != NULL);
|
||||
MA_ASSERT(pJob != NULL);
|
||||
@@ -2956,7 +2968,6 @@ static ma_result ma_resource_manager_process_job__load_data_buffer(ma_resource_m
|
||||
|
||||
if (pDataBuffer->data.type == ma_resource_manager_data_buffer_encoding_encoded) {
|
||||
/* No decoding. Just store the file contents in memory. */
|
||||
void* pData;
|
||||
size_t sizeInBytes;
|
||||
|
||||
result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pJob->loadDataBuffer.pFilePath, &pData, &sizeInBytes, &pResourceManager->config.allocationCallbacks, MA_ALLOCATION_TYPE_ENCODED_BUFFER);
|
||||
@@ -2966,15 +2977,9 @@ static ma_result ma_resource_manager_process_job__load_data_buffer(ma_resource_m
|
||||
}
|
||||
} else {
|
||||
/* Decoding. */
|
||||
ma_decoder* pDecoder; /* Malloc'd here, and then free'd on the last page decode. */
|
||||
ma_decoder_config config;
|
||||
ma_uint64 totalFrameCount;
|
||||
void* pData;
|
||||
ma_uint64 dataSizeInBytes;
|
||||
ma_uint64 dataSizeInFrames;
|
||||
ma_uint64 pageSizeInFrames;
|
||||
ma_uint64 framesRead; /* <-- Keeps track of how many frames we read for the first page. */
|
||||
ma_job pageDataBufferJob;
|
||||
|
||||
/*
|
||||
With the file initialized we now need to initialize the decoder. We need to pass this decoder around on the job queue so we'll need to
|
||||
@@ -3056,11 +3061,41 @@ static ma_result ma_resource_manager_process_job__load_data_buffer(ma_resource_m
|
||||
c89atomic_thread_fence(c89atomic_memory_order_acquire);
|
||||
pDataBuffer->data.decoded.decodedFrameCount = framesRead;
|
||||
|
||||
ma_decoder_uninit(pDecoder);
|
||||
ma__free_from_callbacks(pDecoder, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODER*/);
|
||||
|
||||
result = MA_SUCCESS;
|
||||
goto done;
|
||||
} else {
|
||||
/* We've still got more to decode. We just set the result to MA_BUSY which will tell the next section below to post a paging event. */
|
||||
result = MA_BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
ma__free_from_callbacks(pJob->loadDataBuffer.pFilePath, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
|
||||
|
||||
/*
|
||||
We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads
|
||||
are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY
|
||||
because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then
|
||||
immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any
|
||||
other error code would cause the buffer to look like it's in a state that it's not.
|
||||
*/
|
||||
c89atomic_compare_and_swap_32(&pDataBuffer->result, MA_BUSY, result);
|
||||
|
||||
/*
|
||||
If our result is MA_BUSY we need to post a job to start loading. It's important that we do this after setting the result to the buffer so that the
|
||||
decoding process happens at the right time. If we don't, there's a window where the MA_JOB_PAGE_DATA_BUFFER event will see a status of something
|
||||
other than MA_BUSY and then abort the decoding process with an error.
|
||||
*/
|
||||
if (result == MA_BUSY && pDecoder != NULL) {
|
||||
/* We've still got more to decode. We need to post a job to continue decoding. */
|
||||
ma_job pageDataBufferJob;
|
||||
|
||||
MA_ASSERT(pDecoder != NULL);
|
||||
MA_ASSERT(pData != NULL);
|
||||
|
||||
pageDataBufferJob = ma_job_init(MA_JOB_PAGE_DATA_BUFFER);
|
||||
pageDataBufferJob.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
|
||||
pageDataBufferJob.pageDataBuffer.pDataBuffer = pDataBuffer;
|
||||
@@ -3107,19 +3142,6 @@ static ma_result ma_resource_manager_process_job__load_data_buffer(ma_resource_m
|
||||
/* We want to make sure we don't signal the event here. It needs to be delayed until the last page. */
|
||||
pJob->loadDataBuffer.pEvent = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
ma__free_from_callbacks(pJob->loadDataBuffer.pFilePath, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
|
||||
|
||||
/*
|
||||
We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads
|
||||
are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY
|
||||
because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then
|
||||
immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any
|
||||
other error code would cause the buffer to look like it's in a state that it's not.
|
||||
*/
|
||||
c89atomic_compare_and_swap_32(&pDataBuffer->result, MA_BUSY, result);
|
||||
|
||||
/* Only signal the other threads after the result has been set just for cleanliness sake. */
|
||||
if (pJob->loadDataBuffer.pEvent != NULL) {
|
||||
|
||||
@@ -2510,6 +2510,10 @@ static ma_result ma_mixer_mix_data_source_mmap(ma_mixer* pMixer, ma_data_source*
|
||||
if (pEffect == NULL) {
|
||||
/* Fast path. Mix directly from the data source and don't bother applying an effect. */
|
||||
result = ma_data_source_map(pDataSource, &pMappedBuffer, &framesToProcess);
|
||||
if (result != MA_SUCCESS) {
|
||||
break; /* Failed to map. Abort. */
|
||||
}
|
||||
|
||||
if (framesToProcess == 0) {
|
||||
break; /* Wasn't able to map any data. Abort. */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user