Bump to m4 1.4.19
[platform/upstream/m4.git] / lib / asyncsafe-spin.c
1 /* Spin locks for communication between threads and signal handlers.
2    Copyright (C) 2020-2021 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, see <https://www.gnu.org/licenses/>.  */
16
17 /* Written by Bruno Haible <bruno@clisp.org>, 2020.  */
18
19 #include <config.h>
20
21 /* Specification.  */
22 #include "asyncsafe-spin.h"
23
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #if defined _AIX
27 # include <sys/atomic_op.h>
28 #endif
29
30 #if defined _WIN32 && ! defined __CYGWIN__
31 /* Use Windows threads.  */
32
33 void
34 asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
35 {
36   glwthread_spin_init (lock);
37 }
38
39 static inline void
40 do_lock (asyncsafe_spinlock_t *lock)
41 {
42   glwthread_spin_lock (lock);
43 }
44
45 static inline void
46 do_unlock (asyncsafe_spinlock_t *lock)
47 {
48   if (glwthread_spin_unlock (lock))
49     abort ();
50 }
51
52 void
53 asyncsafe_spin_destroy (asyncsafe_spinlock_t *lock)
54 {
55   glwthread_spin_destroy (lock);
56 }
57
58 #else
59
60 # if HAVE_PTHREAD_H
61 /* Use POSIX threads.  */
62
63 /* We don't use semaphores (although sem_post() is allowed in signal handlers),
64    because it would require to link with -lrt on HP-UX 11, OSF/1, Solaris 10,
65    and also because on macOS only named semaphores work.
66
67    We don't use the C11 <stdatomic.h> (available in GCC >= 4.9) because it would
68    require to link with -latomic.  */
69
70 #  if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) \
71        || __clang_major > 3 || (__clang_major__ == 3 && __clang_minor__ >= 1)) \
72       && !defined __ibmxl__
73 /* Use GCC built-ins (available in GCC >= 4.7 and clang >= 3.1) that operate on
74    the first byte of the lock.
75    Documentation:
76    <https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/_005f_005fatomic-Builtins.html>
77  */
78
79 #   if 1
80 /* An implementation that verifies the unlocks.  */
81
82 void
83 asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
84 {
85   __atomic_store_n (lock, 0, __ATOMIC_SEQ_CST);
86 }
87
88 static inline void
89 do_lock (asyncsafe_spinlock_t *lock)
90 {
91   /* Wait until *lock becomes 0, then replace it with 1.  */
92   asyncsafe_spinlock_t zero;
93   while (!(zero = 0,
94            __atomic_compare_exchange_n (lock, &zero, 1, false,
95                                         __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)))
96     ;
97 }
98
99 static inline void
100 do_unlock (asyncsafe_spinlock_t *lock)
101 {
102   /* If *lock is 1, then replace it with 0.  */
103   asyncsafe_spinlock_t one = 1;
104   if (!__atomic_compare_exchange_n (lock, &one, 0, false,
105                                     __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
106     abort ();
107 }
108
109 #   else
110 /* An implementation that is a little bit more optimized, but does not verify
111    the unlocks.  */
112
113 void
114 asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
115 {
116   __atomic_clear (lock, __ATOMIC_SEQ_CST);
117 }
118
119 static inline void
120 do_lock (asyncsafe_spinlock_t *lock)
121 {
122   while (__atomic_test_and_set (lock, __ATOMIC_SEQ_CST))
123     ;
124 }
125
126 static inline void
127 do_unlock (asyncsafe_spinlock_t *lock)
128 {
129   __atomic_clear (lock, __ATOMIC_SEQ_CST);
130 }
131
132 #   endif
133
134 #  elif (((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) \
135           && !defined __sparc__) \
136          || __clang_major__ >= 3) \
137         && !defined __ibmxl__
138 /* Use GCC built-ins (available in GCC >= 4.1, except on SPARC, and
139    clang >= 3.0).
140    Documentation:
141    <https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html>  */
142
143 void
144 asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
145 {
146   volatile unsigned int *vp = lock;
147   *vp = 0;
148   __sync_synchronize ();
149 }
150
151 static inline void
152 do_lock (asyncsafe_spinlock_t *lock)
153 {
154   /* Wait until *lock becomes 0, then replace it with 1.  */
155   while (__sync_val_compare_and_swap (lock, 0, 1) != 0)
156     ;
157 }
158
159 static inline void
160 do_unlock (asyncsafe_spinlock_t *lock)
161 {
162   /* If *lock is 1, then replace it with 0.  */
163   if (__sync_val_compare_and_swap (lock, 1, 0) != 1)
164     abort ();
165 }
166
167 #  elif defined _AIX
168 /* AIX */
169
170 void
171 asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
172 {
173   atomic_p vp = (int *) lock;
174   _clear_lock (vp, 0);
175 }
176
177 static inline void
178 do_lock (asyncsafe_spinlock_t *lock)
179 {
180   atomic_p vp = (int *) lock;
181   while (_check_lock (vp, 0, 1))
182     ;
183 }
184
185 static inline void
186 do_unlock (asyncsafe_spinlock_t *lock)
187 {
188   atomic_p vp = (int *) lock;
189   if (_check_lock (vp, 1, 0))
190     abort ();
191 }
192
193 #  elif ((defined __GNUC__ || defined __clang__ || defined __SUNPRO_C) && (defined __sparc || defined __i386 || defined __x86_64__)) || (defined __TINYC__ && (defined __i386 || defined __x86_64__))
194 /* For older versions of GCC or clang, use inline assembly.
195    GCC, clang, and the Oracle Studio C 12 compiler understand GCC's extended
196    asm syntax, but the plain Oracle Studio C 11 compiler understands only
197    simple asm.  */
198 /* An implementation that verifies the unlocks.  */
199
200 static void
201 memory_barrier (void)
202 {
203 #   if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590 || defined __TINYC__
204 #    if defined __i386 || defined __x86_64__
205 #     if defined __TINYC__ && defined __i386
206   /* Cannot use the SSE instruction "mfence" with this compiler.  */
207   asm volatile ("lock orl $0,(%esp)");
208 #     else
209   asm volatile ("mfence");
210 #     endif
211 #    endif
212 #    if defined __sparc
213   asm volatile ("membar 2");
214 #    endif
215 #   else
216 #    if defined __i386 || defined __x86_64__
217   asm ("mfence");
218 #    endif
219 #    if defined __sparc
220   asm ("membar 2");
221 #    endif
222 #   endif
223 }
224
225 /* Store NEWVAL in *VP if the old value *VP is == CMP.
226    Return the old value.  */
227 static unsigned int
228 atomic_compare_and_swap (volatile unsigned int *vp, unsigned int cmp,
229                          unsigned int newval)
230 {
231 #   if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590 || defined __TINYC__
232   unsigned int oldval;
233 #    if defined __i386 || defined __x86_64__
234   asm volatile (" lock\n cmpxchgl %3,(%1)"
235                 : "=a" (oldval) : "r" (vp), "a" (cmp), "r" (newval) : "memory");
236 #    endif
237 #    if defined __sparc
238   asm volatile (" cas [%1],%2,%3\n"
239                 " mov %3,%0"
240                 : "=r" (oldval) : "r" (vp), "r" (cmp), "r" (newval) : "memory");
241 #    endif
242   return oldval;
243 #   else /* __SUNPRO_C */
244 #    if defined __x86_64__
245   asm (" movl %esi,%eax\n"
246        " lock\n cmpxchgl %edx,(%rdi)");
247 #    elif defined __i386
248   asm (" movl 16(%ebp),%ecx\n"
249        " movl 12(%ebp),%eax\n"
250        " movl 8(%ebp),%edx\n"
251        " lock\n cmpxchgl %ecx,(%edx)");
252 #    endif
253 #    if defined __sparc
254   asm (" cas [%i0],%i1,%i2\n"
255        " mov %i2,%i0");
256 #    endif
257 #   endif
258 }
259
260 void
261 asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
262 {
263   volatile unsigned int *vp = lock;
264   *vp = 0;
265   memory_barrier ();
266 }
267
268 static inline void
269 do_lock (asyncsafe_spinlock_t *lock)
270 {
271   volatile unsigned int *vp = lock;
272   while (atomic_compare_and_swap (vp, 0, 1) != 0)
273     ;
274 }
275
276 static inline void
277 do_unlock (asyncsafe_spinlock_t *lock)
278 {
279   volatile unsigned int *vp = lock;
280   if (atomic_compare_and_swap (vp, 1, 0) != 1)
281     abort ();
282 }
283
284 #  else
285 /* Fallback code.  It has some race conditions.  */
286
287 void
288 asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
289 {
290   volatile unsigned int *vp = lock;
291   *vp = 0;
292 }
293
294 static inline void
295 do_lock (asyncsafe_spinlock_t *lock)
296 {
297   volatile unsigned int *vp = lock;
298   while (*vp)
299     ;
300   *vp = 1;
301 }
302
303 static inline void
304 do_unlock (asyncsafe_spinlock_t *lock)
305 {
306   volatile unsigned int *vp = lock;
307   *vp = 0;
308 }
309
310 #  endif
311
312 # else
313 /* Provide a dummy implementation for single-threaded applications.  */
314
315 void
316 asyncsafe_spin_init (asyncsafe_spinlock_t *lock)
317 {
318 }
319
320 static inline void
321 do_lock (asyncsafe_spinlock_t *lock)
322 {
323 }
324
325 static inline void
326 do_unlock (asyncsafe_spinlock_t *lock)
327 {
328 }
329
330 # endif
331
332 void
333 asyncsafe_spin_destroy (asyncsafe_spinlock_t *lock)
334 {
335 }
336
337 #endif
338
339 void
340 asyncsafe_spin_lock (asyncsafe_spinlock_t *lock,
341                      const sigset_t *mask, sigset_t *saved_mask)
342 {
343   sigprocmask (SIG_BLOCK, mask, saved_mask); /* equivalent to pthread_sigmask */
344   do_lock (lock);
345 }
346
347 void
348 asyncsafe_spin_unlock (asyncsafe_spinlock_t *lock, const sigset_t *saved_mask)
349 {
350   do_unlock (lock);
351   sigprocmask (SIG_SETMASK, saved_mask, NULL); /* equivalent to pthread_sigmask */
352 }