Asterisk - The Open Source Telephony Project  18.5.0
spinlock.h
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2014, Fairview 5 Engineering, LLC
5  *
6  * George Joseph <[email protected]>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  * \brief Spin Locks.
21  *
22  * In some atomic operation circumstances the __atomic calls are not quite
23  * flexible enough but a full fledged mutex or rwlock is too expensive.
24  *
25  * Spin locks should be used only for protecting short blocks of critical
26  * code such as simple compares and assignments. Operations that may block,
27  * hold a lock, or cause the thread to give up it's timeslice should NEVER
28  * be attempted in a spin lock.
29  *
30  * Because spinlocks must be as lightweight as possible, there are no
31  * recursion or deadlock checks.
32  *
33  */
34 
35 #ifndef _ASTERISK_SPINLOCK_H
36 #define _ASTERISK_SPINLOCK_H
37 
38 #include <pthread.h>
39 #include "asterisk/compiler.h"
40 
41 /*!
42  * \brief Spinlock Implementation Types
43  *
44  * Not all implementations will be available on all platforms.
45  *
46  */
55 };
56 
57 /*!
58  * \brief Implementation using GCC Atomics
59  *
60  * Specifically, __sync_lock_test_and_set is used to atomically
61  * set/unset the lock variable.
62  *
63  * Most recent gcc implementations support this method and it's performance
64  * is equal to or better than the assembly implementations hence it is the
65  * most preferred implementation on all platforms.
66  *
67  */
68 #ifdef HAVE_GCC_ATOMICS
69 #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_GCC_ATOMICS
70 #define AST_SPINLOCK_TYPE_LABEL "gcc_atomics"
71 typedef volatile unsigned int ast_spinlock_t;
72 
74 {
75  *lock = 0;
76  return 0;
77 }
78 
80 {
81  while (__sync_lock_test_and_set(lock, 1)) {
82  while(*lock) {
83  }
84  }
85  return 0;
86 }
87 
89 {
90  return __sync_lock_test_and_set(lock, 1);
91 }
92 
94 {
95  __sync_lock_release(lock);
96  return 0;
97 }
98 
100 {
101  return 0;
102 }
103 #endif
104 
105 /*!
106  * \brief Implementation using x86 Assembly
107  *
108  * For x86 implementations that don't support gcc atomics,
109  * this is the next best method.
110  *
111  */
112 #if (defined(__x86_64__) || defined(__i386__)) && !defined(AST_SPINLOCK_TYPE)
113 #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_GAS_X86
114 #define AST_SPINLOCK_TYPE_LABEL "gas_x86"
115 typedef volatile unsigned int ast_spinlock_t;
116 
118 {
119  *lock = 0;
120  return 0;
121 }
122 
123 static force_inline int x86chgl(ast_spinlock_t *p, unsigned int v)
124 {
125  __asm __volatile (
126  " xchg %0, %1 ;"
127  : "+r" (v), "=m" (*p)
128  : "m" (*p)
129  );
130 
131  return (v);
132 }
133 
135 {
136  while (x86chgl(lock, 1)) {
137  while(*lock) {
138  }
139  }
140  return 0;
141 }
142 
144 {
145  return x86chgl(lock, 1);
146 }
147 
149 {
150  x86chgl(lock, 0);
151  return 0;
152 }
153 
155 {
156  return 0;
157 }
158 #endif
159 
160 /*!
161  * \brief Implementation using ARM Assembly
162  *
163  * For ARM implementations that don't support gcc atomics,
164  * this is the next best method.
165  *
166  */
167 #if defined(__arm__) && !defined(AST_SPINLOCK_TYPE)
168 #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_GAS_ARM
169 #define AST_SPINLOCK_TYPE_LABEL "gas_arm"
170 typedef volatile unsigned int ast_spinlock_t;
171 
173 {
174  *lock = 0;
175  return 0;
176 }
177 
179 {
180  unsigned int tmp;
181 
182  __asm __volatile (
183  "1: ldrex %[tmp], %[lock];"
184  " teq %[tmp], #0;"
185 #if defined __ARM_ARCH && __ARM_ARCH >= 7
186  " wfene;"
187 #endif
188  " strexeq %[tmp], %[c1], %[lock];"
189  " teqeq %[tmp], #0;"
190  " bne 1b;"
191  : [tmp] "=&r" (tmp)
192  : [lock] "m" (*lock) [c1] "r" (1)
193  : "cc"
194  );
195 
196  return tmp;
197 }
198 
200 {
201  unsigned int tmp;
202 
203  __asm __volatile (
204  " ldrex %[tmp], %[lock];"
205  " teq %[tmp], #0;"
206 #if defined __ARM_ARCH && __ARM_ARCH >= 7
207  " wfene;"
208 #endif
209  " strexeq %[tmp], %[c1], %[lock];"
210  : [tmp] "=&r" (tmp)
211  : [lock] "m" (*lock) [c1] "r" (1)
212  : "cc"
213  );
214 
215  return tmp;
216 }
217 
219 {
220  __asm __volatile (
221  " dmb;"
222  " str %[c0], %[lock];"
223 #if defined __ARM_ARCH && __ARM_ARCH >= 7
224  " dsb;"
225  " sev;"
226 #endif
227  :
228  : [lock] "m" (*lock) [c0] "r" (0)
229  : "cc"
230  );
231 
232  return 0;
233 }
234 
236 {
237  return 0;
238 }
239 #endif
240 
241 /*!
242  * \brief Implementation using Sparc Assembly
243  *
244  * For Sparc implementations that don't support gcc atomics,
245  * this is the next best method.
246  *
247  */
248 #if defined(__sparc__) && !defined(AST_SPINLOCK_TYPE)
249 #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_GAS_SPARC
250 #define AST_SPINLOCK_TYPE_LABEL "gas_sparc"
251 typedef volatile unsigned char ast_spinlock_t;
252 
254 {
255  *lock = 0;
256  return 0;
257 }
258 
260 {
261  unsigned char tmp;
262 
263  __asm__ __volatile__(
264  "1: ldstub %[lock], %[tmp]\n"
265  " brnz,pn %[tmp], 2f\n"
266  " nop\n"
267  " .subsection 2\n"
268  "2: ldub %[lock], %[tmp]\n"
269  " brnz,pt %[tmp], 2b\n"
270  " nop\n"
271  " ba,a,pt %%xcc, 1b\n"
272  " .previous"
273  : [tmp] "=&r" (tmp)
274  : [lock] "m" (*lock)
275  : "memory"
276  );
277 
278  return 0;
279 }
280 
282 {
283  unsigned long result = 1;
284 
285  __asm__ __volatile__(
286  " ldstub %[lock], %[result]\n"
287  : [result] "=&r" (result)
288  : [lock] "m" (*lock)
289  : "memory", "cc"
290  );
291 
292  return (result != 0);
293 }
294 
296 {
297  __asm__ __volatile__(
298  " stb %%g0, %[lock]"
299  :
300  : [lock] "m" (*lock)
301  : "memory", "cc"
302  );
303 
304  return 0;
305 }
306 
308 {
309  return 0;
310 }
311 #endif
312 
313 /*!
314  * \brief Implementation using pthread_spinlock
315  *
316  * pthread_spinlocks are not supported on all platforms
317  * but if for some reason none of the previous implementations are
318  * available, it can be used with reasonable performance.
319  *
320  */
321 #if defined (HAVE_PTHREAD_SPINLOCK) && !defined(AST_SPINLOCK_TYPE)
322 #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_PTHREAD_SPINLOCK
323 #define AST_SPINLOCK_TYPE_LABEL "pthread_spinlock"
324 typedef pthread_spinlock_t ast_spinlock_t;
325 
327 {
328  return pthread_spin_init(lock, PTHREAD_PROCESS_PRIVATE);
329 }
330 
332 {
333  return pthread_spin_lock(lock);
334 }
335 
337 {
338  return pthread_spin_trylock(lock);
339 }
340 
342 {
343  return pthread_spin_unlock(lock);
344 }
345 
347 {
348  return pthread_spin_destroy(lock);
349 }
350 #endif
351 
352 /*!
353  * \brief Implementation using OSX Atomics
354  *
355  * The Darwin/Mac OSX platform has its own atomics
356  * implementation but it uses more kernel time than
357  * GCC atomics and x86 assembly. It is included
358  * as an unlikely fallback.
359  *
360  */
361 #if defined(HAVE_OSX_ATOMICS) && !defined(AST_SPINLOCK_TYPE)
362 #include <libkern/OSAtomic.h>
363 #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_OSX_ATOMICS
364 #define AST_SPINLOCK_TYPE_LABEL "osx_atomics"
365 typedef OSSpinLock ast_spinlock_t;
366 
368 {
369  *lock = OS_SPINLOCK_INIT;
370  return 0;
371 }
372 
374 {
375  OSSpinLockLock(lock);
376  return 0;
377 }
378 
380 {
381  return !OSSpinLockTry(lock);
382 }
383 
385 {
386  OSSpinLockUnlock(lock);
387  return 0;
388 }
389 
391 {
392  return 0;
393 }
394 #endif
395 
396 /*!
397  * \brief Implementation using pthread_mutex
398  *
399  * pthread_mutex is supported on all platforms but
400  * it is also the worst performing. It is included
401  * as an unlikely fallback.
402  *
403  */
404 #if !defined(AST_SPINLOCK_TYPE)
405 #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_PTHREAD_MUTEX
406 #define AST_SPINLOCK_TYPE_LABEL "pthread_mutex"
408 
410 {
411  pthread_mutex_init(lock, NULL);
412  return 0;
413 }
414 
416 {
417  return pthread_mutex_lock(lock);
418 }
419 
421 {
422  return pthread_mutex_trylock(lock);
423 }
424 
426 {
427  return pthread_mutex_unlock(lock);
428 }
429 
431 {
432  return pthread_mutex_destroy(lock);
433 }
434 #endif
435 
436 #if !defined(AST_SPINLOCK_TYPE)
437 #error "No spinlock implementation could be found."
438 #endif
439 
440 /* Prototypes are declared here to insure that each implementation provides
441  * the same API and to act as placeholders for the documentation.
442  */
443 
444 /*!
445  * \brief Initialize a spin lock
446  * \param lock Address of the lock
447  * \retval 0 Success
448  * \retval other Failure
449  */
451 
452 /*!
453  * \brief Lock a spin lock
454  * \param lock Address of the lock
455  * \retval 0 Success
456  * \retval other Failure
457  */
459 
460 /*!
461  * \brief Try to lock a spin lock
462  *
463  * Attempt to gain a lock. Return immediately
464  * regardless of result.
465  *
466  * \param lock Address of the lock
467  * \retval 0 Success
468  * \retval other Lock was not obtained
469  */
471 
472 /*!
473  * \brief Unlock a spin lock
474  * \param lock Address of the lock
475  * \retval 0 Success
476  * \retval other Failure
477  */
479 
480 /*!
481  * \brief Destroy a spin lock
482  * \param lock Address of the lock
483  * \retval 0 Success
484  * \retval other Failure
485  */
487 
488 #endif /* _ASTERISK_SPINLOCK_H */
#define pthread_mutex_init
Definition: lock.h:626
#define force_inline
Definition: compiler.h:29
static int tmp()
Definition: bt_open.c:389
#define NULL
Definition: resample.c:96
#define pthread_mutex_t
Definition: lock.h:620
Compiler-specific macros and other items.
#define pthread_mutex_destroy
Definition: lock.h:627
#define pthread_mutex_lock
Definition: lock.h:623
ast_mutex_t lock
Definition: app_meetme.c:1091
static force_inline int ast_spinlock_trylock(ast_spinlock_t *lock)
Try to lock a spin lock.
Definition: spinlock.h:420
static force_inline int ast_spinlock_init(ast_spinlock_t *lock)
Initialize a spin lock.
Definition: spinlock.h:409
ast_spinlock_type
Spinlock Implementation Types.
Definition: spinlock.h:47
pthread_mutex_t ast_spinlock_t
Definition: spinlock.h:407
static force_inline int ast_spinlock_unlock(ast_spinlock_t *lock)
Unlock a spin lock.
Definition: spinlock.h:425
static force_inline int ast_spinlock_lock(ast_spinlock_t *lock)
Lock a spin lock.
Definition: spinlock.h:415
static PGresult * result
Definition: cel_pgsql.c:88
static force_inline int ast_spinlock_destroy(ast_spinlock_t *lock)
Destroy a spin lock.
Definition: spinlock.h:430
#define pthread_mutex_unlock
Definition: lock.h:624
#define pthread_mutex_trylock
Definition: lock.h:625