mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-23 08:44:04 +02:00
Experimental fix a what appears to be a threading error.
This commit is contained in:
+32
-29
@@ -16866,7 +16866,7 @@ MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob)
|
|||||||
/*
|
/*
|
||||||
Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors
|
Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors
|
||||||
*/
|
*/
|
||||||
ma_result result;
|
ma_result result = MA_SUCCESS;
|
||||||
ma_uint64 slot;
|
ma_uint64 slot;
|
||||||
ma_uint64 tail;
|
ma_uint64 tail;
|
||||||
ma_uint64 next;
|
ma_uint64 next;
|
||||||
@@ -16875,46 +16875,49 @@ MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob)
|
|||||||
return MA_INVALID_ARGS;
|
return MA_INVALID_ARGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We need a new slot. */
|
|
||||||
result = ma_slot_allocator_alloc(&pQueue->allocator, &slot);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* At this point we should have a slot to place the job. */
|
|
||||||
MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity);
|
|
||||||
|
|
||||||
/* We need to put the job into memory before we do anything. */
|
|
||||||
pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob;
|
|
||||||
pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */
|
|
||||||
pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */
|
|
||||||
pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */
|
|
||||||
|
|
||||||
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
|
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
|
||||||
ma_spinlock_lock(&pQueue->lock);
|
ma_spinlock_lock(&pQueue->lock);
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
/* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */
|
/* We need a new slot. */
|
||||||
for (;;) {
|
result = ma_slot_allocator_alloc(&pQueue->allocator, &slot);
|
||||||
tail = c89atomic_load_64(&pQueue->tail);
|
if (result == MA_SUCCESS) {
|
||||||
next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next);
|
/* At this point we should have a slot to place the job. */
|
||||||
|
MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity);
|
||||||
|
|
||||||
if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->tail))) {
|
/* We need to put the job into memory before we do anything. */
|
||||||
if (ma_job_extract_slot(next) == 0xFFFF) {
|
pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob;
|
||||||
if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) {
|
pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */
|
||||||
break;
|
pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */
|
||||||
|
pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */
|
||||||
|
|
||||||
|
/* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */
|
||||||
|
for (;;) {
|
||||||
|
tail = c89atomic_load_64(&pQueue->tail);
|
||||||
|
next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next);
|
||||||
|
|
||||||
|
if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->tail))) {
|
||||||
|
if (ma_job_extract_slot(next) == 0xFFFF) {
|
||||||
|
if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ma_job_queue_cas(&pQueue->tail, tail, slot);
|
||||||
|
} else {
|
||||||
|
/* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */
|
||||||
}
|
}
|
||||||
ma_job_queue_cas(&pQueue->tail, tail, slot);
|
|
||||||
}
|
}
|
||||||
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
|
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
|
||||||
ma_spinlock_unlock(&pQueue->lock);
|
ma_spinlock_unlock(&pQueue->lock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* Signal the semaphore as the last step if we're using synchronous mode. */
|
/* Signal the semaphore as the last step if we're using synchronous mode. */
|
||||||
if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
|
if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
|
||||||
@@ -16991,13 +16994,13 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ma_slot_allocator_free(&pQueue->allocator, head);
|
||||||
}
|
}
|
||||||
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
|
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
|
||||||
ma_spinlock_unlock(&pQueue->lock);
|
ma_spinlock_unlock(&pQueue->lock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ma_slot_allocator_free(&pQueue->allocator, head);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We
|
If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We
|
||||||
could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as
|
could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as
|
||||||
|
|||||||
Reference in New Issue
Block a user