Asterisk - The Open Source Telephony Project GIT-master-f36a736
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 <george.joseph@fairview5.com>
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"
71typedef 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"
115typedef volatile unsigned int ast_spinlock_t;
116
118{
119 *lock = 0;
120 return 0;
121}
122
123static 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"
170typedef 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"
251typedef 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"
324typedef 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"
365typedef 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{
412 return 0;
413}
414
416{
417 return pthread_mutex_lock(lock);
418}
419
421{
423}
424
426{
428}
429
431{
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 */
ast_mutex_t lock
Definition: app_sla.c:331
static int tmp()
Definition: bt_open.c:389
static PGresult * result
Definition: cel_pgsql.c:84
Compiler-specific macros and other items.
#define force_inline
Definition: compiler.h:29
#define pthread_mutex_trylock
Definition: lock.h:627
#define pthread_mutex_lock
Definition: lock.h:625
#define pthread_mutex_t
Definition: lock.h:622
#define pthread_mutex_unlock
Definition: lock.h:626
#define pthread_mutex_destroy
Definition: lock.h:629
#define pthread_mutex_init
Definition: lock.h:628
#define NULL
Definition: resample.c:96
static force_inline int ast_spinlock_lock(ast_spinlock_t *lock)
Lock a spin lock.
Definition: spinlock.h:415
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_unlock(ast_spinlock_t *lock)
Unlock a spin lock.
Definition: spinlock.h:425
pthread_mutex_t ast_spinlock_t
Definition: spinlock.h:407
static force_inline int ast_spinlock_destroy(ast_spinlock_t *lock)
Destroy a spin lock.
Definition: spinlock.h:430
ast_spinlock_type
Spinlock Implementation Types.
Definition: spinlock.h:47
@ AST_SPINLOCK_TYPE_PTHREAD_MUTEX
Definition: spinlock.h:54
@ AST_SPINLOCK_TYPE_OSX_ATOMICS
Definition: spinlock.h:52
@ AST_SPINLOCK_TYPE_GAS_SPARC
Definition: spinlock.h:51
@ AST_SPINLOCK_TYPE_GAS_X86
Definition: spinlock.h:49
@ AST_SPINLOCK_TYPE_GCC_ATOMICS
Definition: spinlock.h:48
@ AST_SPINLOCK_TYPE_PTHREAD_SPINLOCK
Definition: spinlock.h:53
@ AST_SPINLOCK_TYPE_GAS_ARM
Definition: spinlock.h:50
static force_inline int ast_spinlock_init(ast_spinlock_t *lock)
Initialize a spin lock.
Definition: spinlock.h:409