diff --git a/src/cor/flags.h b/src/cor/flags.h index 31a8074..da41a0b 100644 --- a/src/cor/flags.h +++ b/src/cor/flags.h @@ -26,15 +26,18 @@ static struct int RANGE; // Index out of range int DUPLICATE; // Duplicate key or value int ERROR; // Generic error, usually caused by algorithm error -} cmc_flags = { 0, 1, 2, 3, 4, 5, 6, 7 }; + int THREAD; // Generic error regarding threads + int MUTEX; // Generic error regarding mutexes +} cmc_flags = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; /** * cmc_flags_to_str * * Maps the error codes to their character representation. */ -const char *cmc_flags_to_str[8] = { "OK", "ALLOC", "EMPTY", - "NOT_FOUND", "INVALID", "RANGE", - "DUPLICATE", "ERROR" }; +const char *cmc_flags_to_str[10] = { "OK", "ALLOC", "EMPTY", + "NOT_FOUND", "INVALID", "RANGE", + "DUPLICATE", "ERROR", "THREAD", + "MUTEX" }; #endif /* CMC_FLAGS_H */ diff --git a/src/utl/mutex.h b/src/utl/mutex.h new file mode 100644 index 0000000..7b51e3e --- /dev/null +++ b/src/utl/mutex.h @@ -0,0 +1,212 @@ +/** + * 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 + */ + +#include + +#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 +#elif defined(CMC_MUTEX_UNIX) +#include +#include +#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_flags.MUTEX; + else + mtx->flag = cmc_flags.OK; + + return mtx->mutex != NULL; + +#elif defined(CMC_MUTEX_UNIX) + int err = pthread_mutex_init(&(mtx->mutex), NULL); + + if (err != 0) + mtx->flag = cmc_flags.MUTEX; + else + mtx->flag = cmc_flags.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_flags.MUTEX; + return false; + } + + mtx->flag = cmc_flags.OK; + return true; + +#elif defined(CMC_MUTEX_UNIX) + if (pthread_mutex_destroy(&(mtx->mutex)) != 0) + { + mtx->flag = cmc_flags.MUTEX; + return false; + } + + mtx->flag = cmc_flags.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_flags.MUTEX; + return false; + } + + mtx->flag = cmc_flags.OK; + return true; + +#elif defined(CMC_MUTEX_UNIX) + if (pthread_mutex_lock(&mtx->mutex) != 0) + { + mtx->flag = cmc_flags.MUTEX; + return false; + } + + mtx->flag = cmc_flags.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_flags.MUTEX; + else + mtx->flag = cmc_flags.OK; + + return mtx->flag == cmc_flags.OK; + +#elif defined(CMC_MUTEX_UNIX) + if (pthread_mutex_unlock(&mtx->mutex) != 0) + mtx->flag = cmc_flags.MUTEX; + else + mtx->flag = cmc_flags.OK; + + return mtx->flag == cmc_flags.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_flags.MUTEX; + return false; + } + + mtx->flag = cmc_flags.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_flags.MUTEX; + else + mtx->flag = cmc_flags.OK; + + return err == 0; +#endif +} diff --git a/src/utl/thread.h b/src/utl/thread.h new file mode 100644 index 0000000..635860e --- /dev/null +++ b/src/utl/thread.h @@ -0,0 +1,173 @@ +/** + * thread.h + * + * Creation Date: 14/05/2020 + * + * Authors: + * Leonardo Vencovsky (https://github.com/LeoVen) + * + */ + +/** + * A very simple, header-only utility for spawning and joining threads + * + * Types + * - cmc_thread + * - cmc_thread_proc + * + * Functions + * - cmc_thrd_create + * - cmc_thrd_join + */ + +#include +#include + +#include "../cor/flags.h" + +#if defined(_WIN32) +#define CMC_THREAD_WINDOWS +#elif defined(__unix__) +#define CMC_THREAD_UNIX +#else +#error "Unknown platform for CMC Threads" +#endif + +#if defined(CMC_THREAD_WINDOWS) +#include +#elif defined(CMC_THREAD_UNIX) +#include +#endif + +/* The type of a process run by a thread */ +typedef int (*cmc_thread_proc)(void *); + +/** + * struct cmc_thread + * + * A thread wrapper. + */ +struct cmc_thread +{ +#if defined(CMC_THREAD_WINDOWS) + HANDLE thread; +#elif defined(CMC_THREAD_UNIX) + pthread_t thread; +#endif + int flag; + void *args; + cmc_thread_proc process; +}; + +/* A thread wrapper */ +#if defined(CMC_THREAD_WINDOWS) +static DWORD cmc_thread_wrapper(void *arg) +#elif defined(CMC_THREAD_UNIX) +static void *cmc_thread_wrapper(void *arg) +#endif +{ + struct cmc_thread *thr = (struct cmc_thread *)arg; + + int result = thr->process(thr->args); + +#if defined(CMC_THREAD_WINDOWS) + return result; +#elif defined(CMC_THREAD_UNIX) + return (void *)(intptr_t)result; +#endif +} + +/** + * Creates and spawns a new thread. + * + * \param thr An uninitialized thread wrapper. + * \param proc Pointer to a function that will run in a new thread. + * \param args The arguments passed to the proc function. + * \return True or false if the thread creation was successfull. + */ +static inline bool cmc_thrd_create(struct cmc_thread *thr, cmc_thread_proc proc, + void *args) +{ +#if defined(CMC_THREAD_WINDOWS) + thr->args = args; + thr->process = proc; + thr->thread = CreateThread(NULL, 0, cmc_thread_wrapper, thr, 0, NULL); + + if (!thr->thread) + thr->flag = cmc_flags.THREAD; + else + thr->flag = cmc_flags.OK; + + return thr->flag == cmc_flags.OK; + +#elif defined(CMC_THREAD_UNIX) + thr->args = args; + thr->process = proc; + if (pthread_create(&(thr->thread), NULL, cmc_thread_wrapper, thr) != 0) + thr->flag = cmc_flags.THREAD; + else + thr->flag = cmc_flags.OK; + + return thr->flag == cmc_flags.OK; +#endif +} + +/** + * Blocks the current thread until the target thread terminates. + * + * \param thr The target thread to wait on. + * \param result The value returned by the target thread. Optional. + * \return True or false if the join was successfull. + */ +static inline bool cmc_thrd_join(struct cmc_thread *thr, int *result) +{ +#if defined(CMC_THREAD_WINDOWS) + if (WaitForSingleObject(thr->thread, INFINITE) == WAIT_FAILED) + { + thr->flag = cmc_flags.THREAD; + return false; + } + + DWORD exit_code; + + if (result) + { + if (GetExitCodeThread(thr->thread, &exit_code) == 0) + { + thr->flag = cmc_flags.THREAD; + return false; + } + else + *result = (int)exit_code; + } + + thr->flag = cmc_flags.OK; + + if (CloseHandle(thr->thread) == 0) + { + thr->flag = cmc_flags.THREAD; + return false; + } + + thr->flag = cmc_flags.OK; + return true; + +#elif defined(CMC_THREAD_UNIX) + void *exit_value; + + if (pthread_join(thr->thread, &exit_value) != 0) + { + thr->flag = cmc_flags.THREAD; + return false; + } + + if (result) + { + *result = (int)(intptr_t)exit_value; + } + + thr->flag = cmc_flags.OK; + return true; + +#endif +}