mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
Add ma_fence object in preparation for changes to async notifications.
This commit is contained in:
@@ -1127,6 +1127,34 @@ MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint6
|
||||
MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Fence
|
||||
=====
|
||||
This locks while the counter is larger than 0. Counter can be incremented and decremented by any
|
||||
thread, but care needs to be taken when waiting. It is possible for one thread to acquire the
|
||||
fence just as another thread returns from ma_fence_wait().
|
||||
|
||||
The idea behind a fence is to allow you to wait for a group of operations to complete. When an
|
||||
operation starts, the counter is incremented which locks the fence. When the operation completes,
|
||||
the fence will be released which decrements the counter. ma_fence_wait() will block until the
|
||||
counter hits zero.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ma_event e;
|
||||
ma_uint32 counter;
|
||||
} ma_fence;
|
||||
|
||||
MA_API ma_result ma_fence_init(ma_fence* pFence);
|
||||
MA_API void ma_fence_uninit(ma_fence* pFence);
|
||||
MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */
|
||||
MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */
|
||||
MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */
|
||||
|
||||
|
||||
|
||||
|
||||
/* Notification codes for ma_async_notification. Used to allow some granularity for notification callbacks. */
|
||||
#define MA_NOTIFICATION_COMPLETE 0 /* Operation has fully completed. */
|
||||
#define MA_NOTIFICATION_FAILED 1 /* Failed to initialize. */
|
||||
@@ -5753,6 +5781,129 @@ MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64
|
||||
|
||||
|
||||
|
||||
#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF
|
||||
|
||||
MA_API ma_result ma_fence_init(ma_fence* pFence)
|
||||
{
|
||||
ma_result result;
|
||||
|
||||
if (pFence == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
MA_ZERO_OBJECT(pFence);
|
||||
pFence->counter = 0;
|
||||
|
||||
result = ma_event_init(&pFence->e);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
MA_API void ma_fence_uninit(ma_fence* pFence)
|
||||
{
|
||||
if (pFence == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ma_event_uninit(&pFence->e);
|
||||
|
||||
MA_ZERO_OBJECT(pFence);
|
||||
}
|
||||
|
||||
MA_API ma_result ma_fence_acquire(ma_fence* pFence)
|
||||
{
|
||||
if (pFence == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter);
|
||||
ma_uint32 newCounter = oldCounter + 1;
|
||||
|
||||
/* Make sure we're not about to exceed our maximum value. */
|
||||
if (newCounter > MA_FENCE_COUNTER_MAX) {
|
||||
return MA_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
|
||||
return MA_SUCCESS;
|
||||
} else {
|
||||
if (oldCounter == MA_FENCE_COUNTER_MAX) {
|
||||
return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Should never get here. */
|
||||
/*return MA_SUCCESS;*/
|
||||
}
|
||||
|
||||
MA_API ma_result ma_fence_release(ma_fence* pFence)
|
||||
{
|
||||
if (pFence == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter);
|
||||
ma_uint32 newCounter = oldCounter - 1;
|
||||
|
||||
if (oldCounter == 0) {
|
||||
return MA_INVALID_OPERATION; /* Acquire/release mismatch. */
|
||||
}
|
||||
|
||||
if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
|
||||
if (newCounter == 0) {
|
||||
ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
} else {
|
||||
if (oldCounter == 0) {
|
||||
return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Should never get here. */
|
||||
/*return MA_SUCCESS;*/
|
||||
}
|
||||
|
||||
MA_API ma_result ma_fence_wait(ma_fence* pFence)
|
||||
{
|
||||
if (pFence == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ma_result result;
|
||||
ma_uint32 counter;
|
||||
|
||||
counter = c89atomic_load_32(&pFence->counter);
|
||||
if (counter == 0) {
|
||||
/*
|
||||
Counter has hit zero. By the time we get here some other thread may have acquired the
|
||||
fence again, but that is where the caller needs to take care with how they se the fence.
|
||||
*/
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
/* Getting here means the counter is > 0. We'll need to wait for something to happen. */
|
||||
result = ma_event_wait(&pFence->e);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Should never get here. */
|
||||
/*return MA_INVALID_OPERATION;*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification, int code)
|
||||
{
|
||||
ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification;
|
||||
|
||||
Reference in New Issue
Block a user