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