/*
* Copyright (c) 2008-2021 Jonathan Schleifer <js@nil.im>
*
* All rights reserved.
*
* This file is part of ObjFW. It may be distributed under the terms of the
* Q Public License 1.0, which can be found in the file LICENSE.QPL included in
* the packaging of this file.
*
* Alternatively, it may be distributed under the terms of the GNU General
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#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"
#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
extern int OFPlainMutexNew(OFPlainMutex *mutex);
extern int OFPlainMutexLock(OFPlainMutex *mutex);
extern int OFPlainMutexTryLock(OFPlainMutex *mutex);
extern int OFPlainMutexUnlock(OFPlainMutex *mutex);
extern int OFPlainMutexFree(OFPlainMutex *mutex);
extern int OFPlainRecursiveMutexNew(OFPlainRecursiveMutex *rmutex);
extern int OFPlainRecursiveMutexLock(OFPlainRecursiveMutex *rmutex);
extern int OFPlainRecursiveMutexTryLock(OFPlainRecursiveMutex *rmutex);
extern int OFPlainRecursiveMutexUnlock(OFPlainRecursiveMutex *rmutex);
extern int OFPlainRecursiveMutexFree(OFPlainRecursiveMutex *rmutex);
#ifdef __cplusplus
}
#endif
/* Spinlocks are inlined for performance. */
static OF_INLINE void
OFYieldThread(void)
{
#if defined(OF_HAVE_SCHED_YIELD)
sched_yield();
#elif defined(OF_WINDOWS)
Sleep(0);
#endif
}
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
}
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
}
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
}
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
}
static OF_INLINE int
OFSpinlockFree(OFSpinlock *spinlock)
{
#if defined(OF_HAVE_ATOMIC_OPS)
return 0;
#elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
return pthread_spin_destroy(spinlock);
#else
return OFPlainMutexFree(spinlock);
#endif
}