Commit 3ede2b1f authored by David Reid's avatar David Reid

API CHANGE: Expose and improve mutex APIs.

parent c8cd85eb
...@@ -292,13 +292,37 @@ typedef void* mal_handle; ...@@ -292,13 +292,37 @@ typedef void* mal_handle;
typedef void* mal_ptr; typedef void* mal_ptr;
typedef void (* mal_proc)(); typedef void (* mal_proc)();
typedef struct mal_context mal_context;
typedef struct mal_device mal_device;
typedef struct
{
mal_context* pContext;
union
{
#ifdef MAL_WIN32
struct
{
/*HANDLE*/ mal_handle hMutex;
} win32;
#endif
#ifdef MAL_POSIX
struct
{
pthread_mutex_t mutex;
} posix;
#endif
int _unused;
};
} mal_mutex;
#ifdef MAL_WIN32 #ifdef MAL_WIN32
typedef mal_handle mal_thread; typedef mal_handle mal_thread;
typedef mal_handle mal_mutex;
typedef mal_handle mal_event; typedef mal_handle mal_event;
#else #else
typedef pthread_t mal_thread; typedef pthread_t mal_thread;
typedef pthread_mutex_t mal_mutex;
typedef struct typedef struct
{ {
pthread_mutex_t mutex; pthread_mutex_t mutex;
...@@ -393,9 +417,6 @@ typedef int mal_result; ...@@ -393,9 +417,6 @@ typedef int mal_result;
#define MAL_WINMM_FAILED_TO_GET_DEVICE_CAPS -4096 #define MAL_WINMM_FAILED_TO_GET_DEVICE_CAPS -4096
#define MAL_WINMM_FAILED_TO_GET_SUPPORTED_FORMATS -4097 #define MAL_WINMM_FAILED_TO_GET_SUPPORTED_FORMATS -4097
typedef struct mal_context mal_context;
typedef struct mal_device mal_device;
typedef void (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, const char* message); typedef void (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, const char* message);
typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples); typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples);
typedef mal_uint32 (* mal_send_proc)(mal_device* pDevice, mal_uint32 frameCount, void* pSamples); typedef mal_uint32 (* mal_send_proc)(mal_device* pDevice, mal_uint32 frameCount, void* pSamples);
...@@ -1332,10 +1353,19 @@ mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 chann ...@@ -1332,10 +1353,19 @@ mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 chann
// //
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
mal_bool32 mal_mutex_create(mal_context* pContext, mal_mutex* pMutex); // Creates a mutex.
void mal_mutex_delete(mal_context* pContext, mal_mutex* pMutex); //
void mal_mutex_lock(mal_context* pContext, mal_mutex* pMutex); // A mutex must be created from a valid context. A mutex is initially unlocked.
void mal_mutex_unlock(mal_context* pContext, mal_mutex* pMutex); mal_result mal_mutex_init(mal_context* pContext, mal_mutex* pMutex);
// Deletes a mutex.
void mal_mutex_uninit(mal_mutex* pMutex);
// Locks a mutex with an infinite timeout.
void mal_mutex_lock(mal_mutex* pMutex);
// Unlocks a mutex.
void mal_mutex_unlock(mal_mutex* pMutex);
...@@ -1938,37 +1968,31 @@ void mal_sleep__win32(mal_uint32 milliseconds) ...@@ -1938,37 +1968,31 @@ void mal_sleep__win32(mal_uint32 milliseconds)
} }
mal_bool32 mal_mutex_create__win32(mal_context* pContext, mal_mutex* pMutex) mal_result mal_mutex_init__win32(mal_context* pContext, mal_mutex* pMutex)
{ {
(void)pContext; (void)pContext;
*pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); pMutex->win32.hMutex = CreateEventA(NULL, FALSE, TRUE, NULL);
if (*pMutex == NULL) { if (pMutex->win32.hMutex == NULL) {
return MAL_FALSE; return MAL_FAILED_TO_CREATE_MUTEX;
} }
return MAL_TRUE; return MAL_SUCCESS;
} }
void mal_mutex_delete__win32(mal_context* pContext, mal_mutex* pMutex) void mal_mutex_uninit__win32(mal_mutex* pMutex)
{ {
(void)pContext; CloseHandle(pMutex->win32.hMutex);
CloseHandle(*pMutex);
} }
void mal_mutex_lock__win32(mal_context* pContext, mal_mutex* pMutex) void mal_mutex_lock__win32(mal_mutex* pMutex)
{ {
(void)pContext; WaitForSingleObject(pMutex->win32.hMutex, INFINITE);
WaitForSingleObject(*pMutex, INFINITE);
} }
void mal_mutex_unlock__win32(mal_context* pContext, mal_mutex* pMutex) void mal_mutex_unlock__win32(mal_mutex* pMutex)
{ {
(void)pContext; SetEvent(pMutex->win32.hMutex);
SetEvent(*pMutex);
} }
...@@ -2035,24 +2059,29 @@ void mal_sleep__posix(mal_uint32 milliseconds) ...@@ -2035,24 +2059,29 @@ void mal_sleep__posix(mal_uint32 milliseconds)
} }
mal_bool32 mal_mutex_create__posix(mal_context* pContext, mal_mutex* pMutex) mal_result mal_mutex_init__posix(mal_context* pContext, mal_mutex* pMutex)
{ {
return ((mal_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(pMutex, NULL) == 0; int result = ((mal_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pMutex->posix.mutex, NULL);
if (result != 0) {
return MAL_FAILED_TO_CREATE_MUTEX;
}
return MAL_SUCCESS;
} }
void mal_mutex_delete__posix(mal_context* pContext, mal_mutex* pMutex) void mal_mutex_uninit__posix(mal_mutex* pMutex)
{ {
((mal_pthread_mutex_destroy_proc)pContext->posix.pthread_mutex_destroy)(pMutex); ((mal_pthread_mutex_destroy_proc)pMutex->pContext->posix.pthread_mutex_destroy)(&pMutex->posix.mutex);
} }
void mal_mutex_lock__posix(mal_context* pContext, mal_mutex* pMutex) void mal_mutex_lock__posix(mal_mutex* pMutex)
{ {
((mal_pthread_mutex_lock_proc)pContext->posix.pthread_mutex_lock)(pMutex); ((mal_pthread_mutex_lock_proc)pMutex->pContext->posix.pthread_mutex_lock)(&pMutex->posix.mutex);
} }
void mal_mutex_unlock__posix(mal_context* pContext, mal_mutex* pMutex) void mal_mutex_unlock__posix(mal_mutex* pMutex)
{ {
((mal_pthread_mutex_unlock_proc)pContext->posix.pthread_mutex_unlock)(pMutex); ((mal_pthread_mutex_unlock_proc)pMutex->pContext->posix.pthread_mutex_unlock)(&pMutex->posix.mutex);
} }
...@@ -2139,51 +2168,53 @@ void mal_sleep(mal_uint32 milliseconds) ...@@ -2139,51 +2168,53 @@ void mal_sleep(mal_uint32 milliseconds)
} }
mal_bool32 mal_mutex_create(mal_context* pContext, mal_mutex* pMutex) mal_result mal_mutex_init(mal_context* pContext, mal_mutex* pMutex)
{ {
if (pMutex == NULL) return MAL_FALSE; if (pContext == NULL || pMutex == NULL) return MAL_INVALID_ARGS;
pMutex->pContext = pContext;
#ifdef MAL_WIN32 #ifdef MAL_WIN32
return mal_mutex_create__win32(pContext, pMutex); return mal_mutex_init__win32(pContext, pMutex);
#endif #endif
#ifdef MAL_POSIX #ifdef MAL_POSIX
return mal_mutex_create__posix(pContext, pMutex); return mal_mutex_init__posix(pContext, pMutex);
#endif #endif
} }
void mal_mutex_delete(mal_context* pContext, mal_mutex* pMutex) void mal_mutex_uninit(mal_mutex* pMutex)
{ {
if (pMutex == NULL) return; if (pMutex == NULL || pMutex->pContext == NULL) return;
#ifdef MAL_WIN32 #ifdef MAL_WIN32
mal_mutex_delete__win32(pContext, pMutex); mal_mutex_uninit__win32(pMutex);
#endif #endif
#ifdef MAL_POSIX #ifdef MAL_POSIX
mal_mutex_delete__posix(pContext, pMutex); mal_mutex_uninit__posix(pMutex);
#endif #endif
} }
void mal_mutex_lock(mal_context* pContext, mal_mutex* pMutex) void mal_mutex_lock(mal_mutex* pMutex)
{ {
if (pMutex == NULL) return; if (pMutex == NULL || pMutex->pContext == NULL) return;
#ifdef MAL_WIN32 #ifdef MAL_WIN32
mal_mutex_lock__win32(pContext, pMutex); mal_mutex_lock__win32(pMutex);
#endif #endif
#ifdef MAL_POSIX #ifdef MAL_POSIX
mal_mutex_lock__posix(pContext, pMutex); mal_mutex_lock__posix(pMutex);
#endif #endif
} }
void mal_mutex_unlock(mal_context* pContext, mal_mutex* pMutex) void mal_mutex_unlock(mal_mutex* pMutex)
{ {
if (pMutex == NULL) return; if (pMutex == NULL || pMutex->pContext == NULL) return;
#ifdef MAL_WIN32 #ifdef MAL_WIN32
mal_mutex_unlock__win32(pContext, pMutex); mal_mutex_unlock__win32(pMutex);
#endif #endif
#ifdef MAL_POSIX #ifdef MAL_POSIX
mal_mutex_unlock__posix(pContext, pMutex); mal_mutex_unlock__posix(pMutex);
#endif #endif
} }
...@@ -8692,7 +8723,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi ...@@ -8692,7 +8723,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
pDevice->internalSampleRate = pDevice->sampleRate; pDevice->internalSampleRate = pDevice->sampleRate;
mal_copy_memory(pDevice->internalChannelMap, pDevice->channelMap, sizeof(pDevice->channelMap)); mal_copy_memory(pDevice->internalChannelMap, pDevice->channelMap, sizeof(pDevice->channelMap));
if (!mal_mutex_create(pContext, &pDevice->lock)) { if (mal_mutex_init(pContext, &pDevice->lock) != MAL_SUCCESS) {
return mal_post_error(pDevice, "Failed to create mutex.", MAL_FAILED_TO_CREATE_MUTEX); return mal_post_error(pDevice, "Failed to create mutex.", MAL_FAILED_TO_CREATE_MUTEX);
} }
...@@ -8702,18 +8733,18 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi ...@@ -8702,18 +8733,18 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
// Each of these semaphores is released internally by the worker thread when the work is completed. The start // Each of these semaphores is released internally by the worker thread when the work is completed. The start
// semaphore is also used to wake up the worker thread. // semaphore is also used to wake up the worker thread.
if (!mal_event_create(pContext, &pDevice->wakeupEvent)) { if (!mal_event_create(pContext, &pDevice->wakeupEvent)) {
mal_mutex_delete(pContext, &pDevice->lock); mal_mutex_uninit(&pDevice->lock);
return mal_post_error(pDevice, "Failed to create worker thread wakeup event.", MAL_FAILED_TO_CREATE_EVENT); return mal_post_error(pDevice, "Failed to create worker thread wakeup event.", MAL_FAILED_TO_CREATE_EVENT);
} }
if (!mal_event_create(pContext, &pDevice->startEvent)) { if (!mal_event_create(pContext, &pDevice->startEvent)) {
mal_event_delete(pContext, &pDevice->wakeupEvent); mal_event_delete(pContext, &pDevice->wakeupEvent);
mal_mutex_delete(pContext, &pDevice->lock); mal_mutex_uninit(&pDevice->lock);
return mal_post_error(pDevice, "Failed to create worker thread start event.", MAL_FAILED_TO_CREATE_EVENT); return mal_post_error(pDevice, "Failed to create worker thread start event.", MAL_FAILED_TO_CREATE_EVENT);
} }
if (!mal_event_create(pContext, &pDevice->stopEvent)) { if (!mal_event_create(pContext, &pDevice->stopEvent)) {
mal_event_delete(pContext, &pDevice->startEvent); mal_event_delete(pContext, &pDevice->startEvent);
mal_event_delete(pContext, &pDevice->wakeupEvent); mal_event_delete(pContext, &pDevice->wakeupEvent);
mal_mutex_delete(pContext, &pDevice->lock); mal_mutex_uninit(&pDevice->lock);
return mal_post_error(pDevice, "Failed to create worker thread stop event.", MAL_FAILED_TO_CREATE_EVENT); return mal_post_error(pDevice, "Failed to create worker thread stop event.", MAL_FAILED_TO_CREATE_EVENT);
} }
...@@ -8849,7 +8880,7 @@ void mal_device_uninit(mal_device* pDevice) ...@@ -8849,7 +8880,7 @@ void mal_device_uninit(mal_device* pDevice)
mal_event_delete(pDevice->pContext, &pDevice->stopEvent); mal_event_delete(pDevice->pContext, &pDevice->stopEvent);
mal_event_delete(pDevice->pContext, &pDevice->startEvent); mal_event_delete(pDevice->pContext, &pDevice->startEvent);
mal_event_delete(pDevice->pContext, &pDevice->wakeupEvent); mal_event_delete(pDevice->pContext, &pDevice->wakeupEvent);
mal_mutex_delete(pDevice->pContext, &pDevice->lock); mal_mutex_uninit(&pDevice->lock);
#ifdef MAL_ENABLE_WASAPI #ifdef MAL_ENABLE_WASAPI
if (pDevice->pContext->backend == mal_backend_wasapi) { if (pDevice->pContext->backend == mal_backend_wasapi) {
...@@ -8919,22 +8950,22 @@ mal_result mal_device_start(mal_device* pDevice) ...@@ -8919,22 +8950,22 @@ mal_result mal_device_start(mal_device* pDevice)
if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED);
mal_result result = MAL_ERROR; mal_result result = MAL_ERROR;
mal_mutex_lock(pDevice->pContext, &pDevice->lock); mal_mutex_lock(&pDevice->lock);
{ {
// Be a bit more descriptive if the device is already started or is already in the process of starting. This is likely // Be a bit more descriptive if the device is already started or is already in the process of starting. This is likely
// a bug with the application. // a bug with the application.
if (mal_device__get_state(pDevice) == MAL_STATE_STARTING) { if (mal_device__get_state(pDevice) == MAL_STATE_STARTING) {
mal_mutex_unlock(pDevice->pContext, &pDevice->lock); mal_mutex_unlock(&pDevice->lock);
return mal_post_error(pDevice, "mal_device_start() called while another thread is already starting it.", MAL_DEVICE_ALREADY_STARTING); return mal_post_error(pDevice, "mal_device_start() called while another thread is already starting it.", MAL_DEVICE_ALREADY_STARTING);
} }
if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) { if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) {
mal_mutex_unlock(pDevice->pContext, &pDevice->lock); mal_mutex_unlock(&pDevice->lock);
return mal_post_error(pDevice, "mal_device_start() called for a device that's already started.", MAL_DEVICE_ALREADY_STARTED); return mal_post_error(pDevice, "mal_device_start() called for a device that's already started.", MAL_DEVICE_ALREADY_STARTED);
} }
// The device needs to be in a stopped state. If it's not, we just let the caller know the device is busy. // The device needs to be in a stopped state. If it's not, we just let the caller know the device is busy.
if (mal_device__get_state(pDevice) != MAL_STATE_STOPPED) { if (mal_device__get_state(pDevice) != MAL_STATE_STOPPED) {
mal_mutex_unlock(pDevice->pContext, &pDevice->lock); mal_mutex_unlock(&pDevice->lock);
return mal_post_error(pDevice, "mal_device_start() called while another thread is in the process of stopping it.", MAL_DEVICE_BUSY); return mal_post_error(pDevice, "mal_device_start() called while another thread is in the process of stopping it.", MAL_DEVICE_BUSY);
} }
...@@ -8957,7 +8988,7 @@ mal_result mal_device_start(mal_device* pDevice) ...@@ -8957,7 +8988,7 @@ mal_result mal_device_start(mal_device* pDevice)
result = pDevice->workResult; result = pDevice->workResult;
} }
} }
mal_mutex_unlock(pDevice->pContext, &pDevice->lock); mal_mutex_unlock(&pDevice->lock);
return result; return result;
} }
...@@ -8968,22 +8999,22 @@ mal_result mal_device_stop(mal_device* pDevice) ...@@ -8968,22 +8999,22 @@ mal_result mal_device_stop(mal_device* pDevice)
if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED);
mal_result result = MAL_ERROR; mal_result result = MAL_ERROR;
mal_mutex_lock(pDevice->pContext, &pDevice->lock); mal_mutex_lock(&pDevice->lock);
{ {
// Be a bit more descriptive if the device is already stopped or is already in the process of stopping. This is likely // Be a bit more descriptive if the device is already stopped or is already in the process of stopping. This is likely
// a bug with the application. // a bug with the application.
if (mal_device__get_state(pDevice) == MAL_STATE_STOPPING) { if (mal_device__get_state(pDevice) == MAL_STATE_STOPPING) {
mal_mutex_unlock(pDevice->pContext, &pDevice->lock); mal_mutex_unlock(&pDevice->lock);
return mal_post_error(pDevice, "mal_device_stop() called while another thread is already stopping it.", MAL_DEVICE_ALREADY_STOPPING); return mal_post_error(pDevice, "mal_device_stop() called while another thread is already stopping it.", MAL_DEVICE_ALREADY_STOPPING);
} }
if (mal_device__get_state(pDevice) == MAL_STATE_STOPPED) { if (mal_device__get_state(pDevice) == MAL_STATE_STOPPED) {
mal_mutex_unlock(pDevice->pContext, &pDevice->lock); mal_mutex_unlock(&pDevice->lock);
return mal_post_error(pDevice, "mal_device_stop() called for a device that's already stopped.", MAL_DEVICE_ALREADY_STOPPED); return mal_post_error(pDevice, "mal_device_stop() called for a device that's already stopped.", MAL_DEVICE_ALREADY_STOPPED);
} }
// The device needs to be in a started state. If it's not, we just let the caller know the device is busy. // The device needs to be in a started state. If it's not, we just let the caller know the device is busy.
if (mal_device__get_state(pDevice) != MAL_STATE_STARTED) { if (mal_device__get_state(pDevice) != MAL_STATE_STARTED) {
mal_mutex_unlock(pDevice->pContext, &pDevice->lock); mal_mutex_unlock(&pDevice->lock);
return mal_post_error(pDevice, "mal_device_stop() called while another thread is in the process of starting it.", MAL_DEVICE_BUSY); return mal_post_error(pDevice, "mal_device_stop() called while another thread is in the process of starting it.", MAL_DEVICE_BUSY);
} }
...@@ -9009,7 +9040,7 @@ mal_result mal_device_stop(mal_device* pDevice) ...@@ -9009,7 +9040,7 @@ mal_result mal_device_stop(mal_device* pDevice)
result = MAL_SUCCESS; result = MAL_SUCCESS;
} }
} }
mal_mutex_unlock(pDevice->pContext, &pDevice->lock); mal_mutex_unlock(&pDevice->lock);
return result; return result;
} }
...@@ -10334,9 +10365,10 @@ void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count) ...@@ -10334,9 +10365,10 @@ void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count)
// ================ // ================
// //
// v0.x - 2017-xx-xx // v0.x - 2017-xx-xx
// - API CHANGE: Expose and improve mutex APIs. If you were using the mutex APIs before this version you'll
// need to update.
// - Add mal_convert_frames(). This is a high-level helper API for performing a one-time, bulk conversion of // - Add mal_convert_frames(). This is a high-level helper API for performing a one-time, bulk conversion of
// audio data to a different format. // audio data to a different format.
// - Expose the mutex APIs.
// //
// v0.5 - 2017-11-11 // v0.5 - 2017-11-11
// - API CHANGE: The mal_context_init() function now takes a pointer to a mal_context_config object for // - API CHANGE: The mal_context_init() function now takes a pointer to a mal_context_config object for
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment