ObjFW
OFPlainMutex.h
1 /*
2  * Copyright (c) 2008-2023 Jonathan Schleifer <js@nil.im>
3  *
4  * All rights reserved.
5  *
6  * This file is part of ObjFW. It may be distributed under the terms of the
7  * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
8  * the packaging of this file.
9  *
10  * Alternatively, it may be distributed under the terms of the GNU General
11  * Public License, either version 2 or 3, which can be found in the file
12  * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
13  * file.
14  */
15 
16 #include "objfw-defs.h"
17 
18 #include <errno.h>
19 
20 #include "platform.h"
21 
22 #if !defined(OF_HAVE_THREADS) || \
23  (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS))
24 # error No mutexes available!
25 #endif
26 
27 #import "macros.h"
28 
29 #if defined(OF_HAVE_PTHREADS)
30 # include <pthread.h>
31 typedef pthread_mutex_t OFPlainMutex;
32 #elif defined(OF_WINDOWS)
33 # include <windows.h>
34 typedef CRITICAL_SECTION OFPlainMutex;
35 #elif defined(OF_AMIGAOS)
36 # include <exec/semaphores.h>
37 typedef struct SignalSemaphore OFPlainMutex;
38 #endif
39 
40 #if defined(OF_HAVE_ATOMIC_OPS)
41 # import "OFAtomic.h"
42 typedef volatile int OFSpinlock;
43 #elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
44 typedef pthread_spinlock_t OFSpinlock;
45 #else
46 typedef OFPlainMutex OFSpinlock;
47 #endif
48 
49 #ifdef OF_HAVE_SCHED_YIELD
50 # include <sched.h>
51 #endif
52 
53 #if defined(OF_HAVE_RECURSIVE_PTHREAD_MUTEXES) || defined(OF_WINDOWS) || \
54  defined(OF_AMIGAOS)
55 # define OFPlainRecursiveMutex OFPlainMutex
56 #else
57 # import "OFTLSKey.h"
58 typedef struct {
59  OFPlainMutex mutex;
60  OFTLSKey count;
61 } OFPlainRecursiveMutex;
62 #endif
63 
64 #ifdef __cplusplus
65 extern "C" {
66 #endif
67 extern int OFPlainMutexNew(OFPlainMutex *mutex);
68 extern int OFPlainMutexLock(OFPlainMutex *mutex);
69 extern int OFPlainMutexTryLock(OFPlainMutex *mutex);
70 extern int OFPlainMutexUnlock(OFPlainMutex *mutex);
71 extern int OFPlainMutexFree(OFPlainMutex *mutex);
72 extern int OFPlainRecursiveMutexNew(OFPlainRecursiveMutex *rmutex);
73 extern int OFPlainRecursiveMutexLock(OFPlainRecursiveMutex *rmutex);
74 extern int OFPlainRecursiveMutexTryLock(OFPlainRecursiveMutex *rmutex);
75 extern int OFPlainRecursiveMutexUnlock(OFPlainRecursiveMutex *rmutex);
76 extern int OFPlainRecursiveMutexFree(OFPlainRecursiveMutex *rmutex);
77 #ifdef __cplusplus
78 }
79 #endif
80 
81 /* Spinlocks are inlined for performance. */
82 
83 static OF_INLINE void
84 OFYieldThread(void)
85 {
86 #if defined(OF_HAVE_SCHED_YIELD)
87  sched_yield();
88 #elif defined(OF_WINDOWS)
89  Sleep(0);
90 #endif
91 }
92 
93 static OF_INLINE int
94 OFSpinlockNew(OFSpinlock *spinlock)
95 {
96 #if defined(OF_HAVE_ATOMIC_OPS)
97  *spinlock = 0;
98  return 0;
99 #elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
100  return pthread_spin_init(spinlock, 0);
101 #else
102  return OFPlainMutexNew(spinlock);
103 #endif
104 }
105 
106 static OF_INLINE int
107 OFSpinlockTryLock(OFSpinlock *spinlock)
108 {
109 #if defined(OF_HAVE_ATOMIC_OPS)
110  if (OFAtomicIntCompareAndSwap(spinlock, 0, 1)) {
111  OFAcquireMemoryBarrier();
112  return 0;
113  }
114 
115  return EBUSY;
116 #elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
117  return pthread_spin_trylock(spinlock);
118 #else
119  return OFPlainMutexTryLock(spinlock);
120 #endif
121 }
122 
123 static OF_INLINE int
124 OFSpinlockLock(OFSpinlock *spinlock)
125 {
126 #if defined(OF_HAVE_ATOMIC_OPS)
127  size_t i;
128 
129  for (i = 0; i < 10; i++)
130  if (OFSpinlockTryLock(spinlock) == 0)
131  return 0;
132 
133  while (OFSpinlockTryLock(spinlock) == EBUSY)
134  OFYieldThread();
135 
136  return 0;
137 #elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
138  return pthread_spin_lock(spinlock);
139 #else
140  return OFPlainMutexLock(spinlock);
141 #endif
142 }
143 
144 static OF_INLINE int
145 OFSpinlockUnlock(OFSpinlock *spinlock)
146 {
147 #if defined(OF_HAVE_ATOMIC_OPS)
148  bool ret = OFAtomicIntCompareAndSwap(spinlock, 1, 0);
149 
150  OFReleaseMemoryBarrier();
151 
152  return (ret ? 0 : EINVAL);
153 #elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
154  return pthread_spin_unlock(spinlock);
155 #else
156  return OFPlainMutexUnlock(spinlock);
157 #endif
158 }
159 
160 static OF_INLINE int
161 OFSpinlockFree(OFSpinlock *spinlock)
162 {
163 #if defined(OF_HAVE_ATOMIC_OPS)
164  return 0;
165 #elif defined(OF_HAVE_PTHREAD_SPINLOCKS)
166  return pthread_spin_destroy(spinlock);
167 #else
168  return OFPlainMutexFree(spinlock);
169 #endif
170 }