Files

242 lines
5.6 KiB
C

/**
* Copyright (c) 2019 Leonardo Vencovsky
*
* This file is part of the C Macro Collections Library.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* utl_mutex.h
*
* Creation Date: 14/05/2020
*
* Authors:
* Leonardo Vencovsky (https://github.com/LeoVen)
*
*/
/**
* A very simple, header-only and minimalistic cross-platform mutex
*
* Types
* - cmc_mutex
*
* Functions
* - cmc_mtx_init
* - cmc_mtx_destroy
* - cmc_mtx_lock
* - cmc_mtx_unlock
* - cmc_mtx_trylock
*/
#ifndef CMC_UTL_MUTEX_H
#define CMC_UTL_MUTEX_H
#include <stdbool.h>
#include "../cor/flags.h"
#if defined(_WIN32)
#define CMC_MUTEX_WINDOWS
#elif defined(__unix__)
#define CMC_MUTEX_UNIX
#else
#error "Unknown platform for CMC Mutex"
#endif
/* Platform specific includes */
#if defined(CMC_MUTEX_WINDOWS)
#include <windows.h>
#elif defined(CMC_MUTEX_UNIX)
#include <errno.h>
#include <pthread.h>
#endif
/**
* struct cmc_mutex
*
* A mutex wrapper.
*/
struct cmc_mutex
{
#if defined(CMC_MUTEX_WINDOWS)
HANDLE mutex;
#elif defined(CMC_MUTEX_UNIX)
pthread_mutex_t mutex;
#endif
int flag;
};
/**
* Acquire resources for a mutex.
*
* \param mtx An uninitialized mutex wrapper.
* \return True or false if the mutex was successfully initialized.
*/
static inline bool cmc_mtx_init(struct cmc_mutex *mtx)
{
#if defined(CMC_MUTEX_WINDOWS)
mtx->mutex = CreateMutex(NULL, FALSE, NULL);
if (mtx->mutex == NULL)
mtx->flag = CMC_FLAG_MUTEX;
else
mtx->flag = CMC_FLAG_OK;
return mtx->mutex != NULL;
#elif defined(CMC_MUTEX_UNIX)
int err = pthread_mutex_init(&(mtx->mutex), NULL);
if (err != 0)
mtx->flag = CMC_FLAG_MUTEX;
else
mtx->flag = CMC_FLAG_OK;
return err == 0;
#endif
}
/**
* Release all resources from a mutex. Calling this function on a locked mutex
* causes undefined behavior.
*
* \param mtx A mutex to be destroyed.
* \return True or false if the mutex was successfully destroyed.
*/
static inline bool cmc_mtx_destroy(struct cmc_mutex *mtx)
{
#if defined(CMC_MUTEX_WINDOWS)
if (!CloseHandle(mtx->mutex))
{
mtx->flag = CMC_FLAG_MUTEX;
return false;
}
mtx->flag = CMC_FLAG_OK;
return true;
#elif defined(CMC_MUTEX_UNIX)
if (pthread_mutex_destroy(&(mtx->mutex)) != 0)
{
mtx->flag = CMC_FLAG_MUTEX;
return false;
}
mtx->flag = CMC_FLAG_OK;
return true;
#endif
}
/**
* Locks a mutex. If the mutex is already locked, the thread is blocked until
* the mutex can be acquired.
*
* \param mtx A mutex to be locked.
* \return True or false if the lock on the mutex was successfully acquired.
*/
static inline bool cmc_mtx_lock(struct cmc_mutex *mtx)
{
#if defined(CMC_MUTEX_WINDOWS)
DWORD result = WaitForSingleObject(mtx->mutex, INFINITE);
if (result == WAIT_FAILED || result == WAIT_ABANDONED)
{
mtx->flag = CMC_FLAG_MUTEX;
return false;
}
mtx->flag = CMC_FLAG_OK;
return true;
#elif defined(CMC_MUTEX_UNIX)
if (pthread_mutex_lock(&mtx->mutex) != 0)
{
mtx->flag = CMC_FLAG_MUTEX;
return false;
}
mtx->flag = CMC_FLAG_OK;
return true;
#endif
}
/**
* Unlocks a mutex. If the mutex is already locked, the thread is blocked until
* the mutex can be acquired.
*
* \param mtx A mutex to be unlocked.
* \return True or false if the mutex was successfully unlocked.
*/
static inline bool cmc_mtx_unlock(struct cmc_mutex *mtx)
{
#if defined(CMC_MUTEX_WINDOWS)
if (!ReleaseMutex(mtx->mutex))
mtx->flag = CMC_FLAG_MUTEX;
else
mtx->flag = CMC_FLAG_OK;
return mtx->flag == CMC_FLAG_OK;
#elif defined(CMC_MUTEX_UNIX)
if (pthread_mutex_unlock(&mtx->mutex) != 0)
mtx->flag = CMC_FLAG_MUTEX;
else
mtx->flag = CMC_FLAG_OK;
return mtx->flag == CMC_FLAG_OK;
#endif
}
/**
* Tries to lock a mutex. If the mutex is already locked, the thread is not
* blocked and the function returns false.
*
* \param mtx A mutex to try to lock.
* \return True or false if the lock on the mutex was successfully acquired.
*/
static inline bool cmc_mtx_trylock(struct cmc_mutex *mtx)
{
#if defined(CMC_MUTEX_WINDOWS)
DWORD result = WaitForSingleObject(mtx->mutex, 0);
if (result == WAIT_FAILED || result == WAIT_ABANDONED)
{
mtx->flag = CMC_FLAG_MUTEX;
return false;
}
mtx->flag = CMC_FLAG_OK;
return result != WAIT_TIMEOUT;
#elif defined(CMC_MUTEX_UNIX)
int err = pthread_mutex_trylock(&mtx->mutex);
if (err == EINVAL || err == EFAULT)
mtx->flag = CMC_FLAG_MUTEX;
else
mtx->flag = CMC_FLAG_OK;
return err == 0;
#endif
}
#endif /* CMC_UTL_MUTEX_H */