/* * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im> * * All rights reserved. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3.0 only, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public License * version 3.0 along with this program. If not, see * <https://www.gnu.org/licenses/>. */ #include "objfw-defs.h" #include <errno.h> #include "platform.h" #if !defined(OF_HAVE_THREADS) || \ (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS)) # error No mutexes available! #endif #import "macros.h" /** @file */ #if defined(OF_HAVE_PTHREADS) # include <pthread.h> typedef pthread_mutex_t OFPlainMutex; #elif defined(OF_WINDOWS) # include <windows.h> typedef CRITICAL_SECTION OFPlainMutex; #elif defined(OF_AMIGAOS) # include <exec/semaphores.h> typedef struct SignalSemaphore OFPlainMutex; #endif #if defined(OF_HAVE_ATOMIC_OPS) # import "OFAtomic.h" typedef volatile int OFSpinlock; #elif defined(OF_HAVE_PTHREAD_SPINLOCKS) typedef pthread_spinlock_t OFSpinlock; #else typedef OFPlainMutex OFSpinlock; #endif #ifdef OF_HAVE_SCHED_YIELD # include <sched.h> #endif #if defined(OF_HAVE_RECURSIVE_PTHREAD_MUTEXES) || defined(OF_WINDOWS) || \ defined(OF_AMIGAOS) # define OFPlainRecursiveMutex OFPlainMutex #else # import "OFTLSKey.h" typedef struct { OFPlainMutex mutex; OFTLSKey count; } OFPlainRecursiveMutex; #endif #ifdef __cplusplus extern "C" { #endif /** * @brief Creates a new plain mutex. * * A plain mutex is similar to an @ref OFMutex, but does not use exceptions and * is just a lightweight wrapper around the system's mutex implementation. * * @param mutex A pointer to the mutex to create * @return 0 on success, or an error number from `<errno.h>` on error */ extern int OFPlainMutexNew(OFPlainMutex *mutex); /** * @brief Locks the specified mutex. * * @param mutex A pointer to the mutex to lock * @return 0 on success, or an error number from `<errno.h>` on error */ extern int OFPlainMutexLock(OFPlainMutex *mutex); /** * @brief Tries to lock the specified mutex without blocking. * * @param mutex A pointer to the mutex to try to lock * @return 0 on success, or an error number from `<errno.h>` on error */ extern int OFPlainMutexTryLock(OFPlainMutex *mutex); /** * @brief Unlocks the specified mutex. * * @param mutex A pointer to the mutex to unlock * @return 0 on success, or an error number from `<errno.h>` on error */ extern int OFPlainMutexUnlock(OFPlainMutex *mutex); /** * @brief Destroys the specified mutex * * @param mutex A pointer to the mutex to destruct * @return 0 on success, or an error number from `<errno.h>` on error */ extern int OFPlainMutexFree(OFPlainMutex *mutex); /** * @brief Creates a new plain recursive mutex. * * A plain recursive mutex is similar to an @ref OFRecursiveMutex, but does not * use exceptions and is just a lightweight wrapper around the system's * recursive mutex implementation (or lacking that, a simple implementation of * recursive mutexes via regular mutexes). * * @param rmutex A pointer to the recursive mutex to create * @return 0 on success, or an error number from `<errno.h>` on error */ extern int OFPlainRecursiveMutexNew(OFPlainRecursiveMutex *rmutex); /** * @brief Locks the specified recursive mutex. * * @param rmutex A pointer to the recursive mutex to lock * @return 0 on success, or an error number from `<errno.h>` on error */ extern int OFPlainRecursiveMutexLock(OFPlainRecursiveMutex *rmutex); /** * @brief Tries to lock the specified recursive mutex without blocking. * * @param rmutex A pointer to the recursive mutex to try to lock * @return 0 on success, or an error number from `<errno.h>` on error */ extern int OFPlainRecursiveMutexTryLock(OFPlainRecursiveMutex *rmutex); /** * @brief Unlocks the specified recursive mutex. * * @param rmutex A pointer to the recursive mutex to unlock * @return 0 on success, or an error number from `<errno.h>` on error */ extern int OFPlainRecursiveMutexUnlock(OFPlainRecursiveMutex *rmutex); /** * @brief Destroys the specified recursive mutex * * @param rmutex A pointer to the recursive mutex to destruct * @return 0 on success, or an error number from `<errno.h>` on error */ extern int OFPlainRecursiveMutexFree(OFPlainRecursiveMutex *rmutex); #ifdef __cplusplus } #endif /* Spinlocks are inlined for performance. */ /** * @brief Yield the current thread, indicating to the OS that another thread * should execute instead. */ static OF_INLINE void OFYieldThread(void) { #if defined(OF_HAVE_SCHED_YIELD) sched_yield(); #elif defined(OF_WINDOWS) Sleep(0); #endif } /** * @brief Creates a new spinlock. * * @param spinlock A pointer to the spinlock to create * @return 0 on success, or an error number from `<errno.h>` on error */ static OF_INLINE int OFSpinlockNew(OFSpinlock *spinlock) { #if defined(OF_HAVE_ATOMIC_OPS) *spinlock = 0; return 0; #elif defined(OF_HAVE_PTHREAD_SPINLOCKS) return pthread_spin_init(spinlock, 0); #else return OFPlainMutexNew(spinlock); #endif } /** * @brief Tries to lock a spinlock. * * @param spinlock A pointer to the spinlock to try to lock * @return 0 on success, or an error number from `<errno.h>` on error */ static OF_INLINE int OFSpinlockTryLock(OFSpinlock *spinlock) { #if defined(OF_HAVE_ATOMIC_OPS) if (OFAtomicIntCompareAndSwap(spinlock, 0, 1)) { OFAcquireMemoryBarrier(); return 0; } return EBUSY; #elif defined(OF_HAVE_PTHREAD_SPINLOCKS) return pthread_spin_trylock(spinlock); #else return OFPlainMutexTryLock(spinlock); #endif } /** * @brief Locks a spinlock. * * @param spinlock A pointer to the spinlock to lock * @return 0 on success, or an error number from `<errno.h>` on error */ static OF_INLINE int OFSpinlockLock(OFSpinlock *spinlock) { #if defined(OF_HAVE_ATOMIC_OPS) size_t i; for (i = 0; i < 10; i++) if (OFSpinlockTryLock(spinlock) == 0) return 0; while (OFSpinlockTryLock(spinlock) == EBUSY) OFYieldThread(); return 0; #elif defined(OF_HAVE_PTHREAD_SPINLOCKS) return pthread_spin_lock(spinlock); #else return OFPlainMutexLock(spinlock); #endif } /** * @brief Unlocks a spinlock. * * @param spinlock A pointer to the spinlock to unlock * @return 0 on success, or an error number from `<errno.h>` on error */ static OF_INLINE int OFSpinlockUnlock(OFSpinlock *spinlock) { #if defined(OF_HAVE_ATOMIC_OPS) bool ret = OFAtomicIntCompareAndSwap(spinlock, 1, 0); OFReleaseMemoryBarrier(); return (ret ? 0 : EINVAL); #elif defined(OF_HAVE_PTHREAD_SPINLOCKS) return pthread_spin_unlock(spinlock); #else return OFPlainMutexUnlock(spinlock); #endif } /** * @brief Destroys a spinlock. * * @param spinlock A pointer to the spinlock to destroy * @return 0 on success, or an error number from `<errno.h>` on error */ static OF_INLINE int OFSpinlockFree(OFSpinlock *spinlock) { #if defined(OF_HAVE_ATOMIC_OPS) (void)spinlock; return 0; #elif defined(OF_HAVE_PTHREAD_SPINLOCKS) return pthread_spin_destroy(spinlock); #else return OFPlainMutexFree(spinlock); #endif }