spinlock.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  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. /*! \file
  19. * \brief Spin Locks.
  20. *
  21. * In some atomic operation circumstances the __atomic calls are not quite
  22. * flexible enough but a full fledged mutex or rwlock is too expensive.
  23. *
  24. * Spin locks should be used only for protecting short blocks of critical
  25. * code such as simple compares and assignments. Operations that may block,
  26. * hold a lock, or cause the thread to give up it's timeslice should NEVER
  27. * be attempted in a spin lock.
  28. *
  29. * Because spinlocks must be as lightweight as possible, there are no
  30. * recursion or deadlock checks.
  31. *
  32. */
  33. #ifndef _ASTERISK_SPINLOCK_H
  34. #define _ASTERISK_SPINLOCK_H
  35. #include <pthread.h>
  36. #include "asterisk/compiler.h"
  37. /*!
  38. * \brief Spinlock Implementation Types
  39. *
  40. * Not all implementations will be available on all platforms.
  41. *
  42. */
  43. enum ast_spinlock_type {
  44. AST_SPINLOCK_TYPE_GCC_ATOMICS,
  45. AST_SPINLOCK_TYPE_GAS_X86,
  46. AST_SPINLOCK_TYPE_GAS_ARM,
  47. AST_SPINLOCK_TYPE_GAS_SPARC,
  48. AST_SPINLOCK_TYPE_OSX_ATOMICS,
  49. AST_SPINLOCK_TYPE_PTHREAD_SPINLOCK,
  50. AST_SPINLOCK_TYPE_PTHREAD_MUTEX,
  51. };
  52. /*!
  53. * \brief Implementation using GCC Atomics
  54. *
  55. * Specifically, __sync_lock_test_and_set is used to atomically
  56. * set/unset the lock variable.
  57. *
  58. * Most recent gcc implementations support this method and it's performance
  59. * is equal to or better than the assembly implementations hence it is the
  60. * most preferred implementation on all platforms.
  61. *
  62. */
  63. #ifdef HAVE_GCC_ATOMICS
  64. #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_GCC_ATOMICS
  65. #define AST_SPINLOCK_TYPE_LABEL "gcc_atomics"
  66. typedef volatile unsigned int ast_spinlock_t;
  67. static force_inline int ast_spinlock_init(ast_spinlock_t *lock)
  68. {
  69. *lock = 0;
  70. return 0;
  71. }
  72. static force_inline int ast_spinlock_lock(ast_spinlock_t *lock)
  73. {
  74. while (__sync_lock_test_and_set(lock, 1)) {
  75. while(*lock) {
  76. }
  77. }
  78. return 0;
  79. }
  80. static force_inline int ast_spinlock_trylock(ast_spinlock_t *lock)
  81. {
  82. return __sync_lock_test_and_set(lock, 1);
  83. }
  84. static force_inline int ast_spinlock_unlock(ast_spinlock_t *lock)
  85. {
  86. __sync_lock_release(lock);
  87. return 0;
  88. }
  89. static force_inline int ast_spinlock_destroy(ast_spinlock_t *lock)
  90. {
  91. return 0;
  92. }
  93. #endif
  94. /*!
  95. * \brief Implementation using x86 Assembly
  96. *
  97. * For x86 implementations that don't support gcc atomics,
  98. * this is the next best method.
  99. *
  100. */
  101. #if (defined(__x86_64__) || defined(__i386__)) && !defined(AST_SPINLOCK_TYPE)
  102. #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_GAS_X86
  103. #define AST_SPINLOCK_TYPE_LABEL "gas_x86"
  104. typedef volatile unsigned int ast_spinlock_t;
  105. static force_inline int ast_spinlock_init(ast_spinlock_t *lock)
  106. {
  107. *lock = 0;
  108. return 0;
  109. }
  110. static force_inline int x86chgl(ast_spinlock_t *p, unsigned int v)
  111. {
  112. __asm __volatile (
  113. " xchg %0, %1 ;"
  114. : "+r" (v), "=m" (*p)
  115. : "m" (*p)
  116. );
  117. return (v);
  118. }
  119. static force_inline int ast_spinlock_lock(ast_spinlock_t *lock)
  120. {
  121. while (x86chgl(lock, 1)) {
  122. while(*lock) {
  123. }
  124. }
  125. return 0;
  126. }
  127. static force_inline int ast_spinlock_trylock(ast_spinlock_t *lock)
  128. {
  129. return x86chgl(lock, 1);
  130. }
  131. static force_inline int ast_spinlock_unlock(ast_spinlock_t *lock)
  132. {
  133. x86chgl(lock, 0);
  134. return 0;
  135. }
  136. static force_inline int ast_spinlock_destroy(ast_spinlock_t *lock)
  137. {
  138. return 0;
  139. }
  140. #endif
  141. /*!
  142. * \brief Implementation using ARM Assembly
  143. *
  144. * For ARM implementations that don't support gcc atomics,
  145. * this is the next best method.
  146. *
  147. */
  148. #if defined(__arm__) && !defined(AST_SPINLOCK_TYPE)
  149. #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_GAS_ARM
  150. #define AST_SPINLOCK_TYPE_LABEL "gas_arm"
  151. typedef volatile unsigned int ast_spinlock_t;
  152. static force_inline int ast_spinlock_init(ast_spinlock_t *lock)
  153. {
  154. *lock = 0;
  155. return 0;
  156. }
  157. static force_inline int ast_spinlock_lock(ast_spinlock_t *lock)
  158. {
  159. unsigned int tmp;
  160. __asm __volatile (
  161. "1: ldrex %[tmp], %[lock];"
  162. " teq %[tmp], #0;"
  163. #if defined __ARM_ARCH && __ARM_ARCH >= 7
  164. " wfene;"
  165. #endif
  166. " strexeq %[tmp], %[c1], %[lock];"
  167. " teqeq %[tmp], #0;"
  168. " bne 1b;"
  169. : [tmp] "=&r" (tmp)
  170. : [lock] "m" (*lock) [c1] "r" (1)
  171. : "cc"
  172. );
  173. return tmp;
  174. }
  175. static force_inline int ast_spinlock_trylock(ast_spinlock_t *lock)
  176. {
  177. unsigned int tmp;
  178. __asm __volatile (
  179. " ldrex %[tmp], %[lock];"
  180. " teq %[tmp], #0;"
  181. #if defined __ARM_ARCH && __ARM_ARCH >= 7
  182. " wfene;"
  183. #endif
  184. " strexeq %[tmp], %[c1], %[lock];"
  185. : [tmp] "=&r" (tmp)
  186. : [lock] "m" (*lock) [c1] "r" (1)
  187. : "cc"
  188. );
  189. return tmp;
  190. }
  191. static force_inline int ast_spinlock_unlock(ast_spinlock_t *lock)
  192. {
  193. __asm __volatile (
  194. " dmb;"
  195. " str %[c0], %[lock];"
  196. #if defined __ARM_ARCH && __ARM_ARCH >= 7
  197. " dsb;"
  198. " sev;"
  199. #endif
  200. :
  201. : [lock] "m" (*lock) [c0] "r" (0)
  202. : "cc"
  203. );
  204. return 0;
  205. }
  206. static force_inline int ast_spinlock_destroy(ast_spinlock_t *lock)
  207. {
  208. return 0;
  209. }
  210. #endif
  211. /*!
  212. * \brief Implementation using Sparc Assembly
  213. *
  214. * For Sparc implementations that don't support gcc atomics,
  215. * this is the next best method.
  216. *
  217. */
  218. #if defined(__sparc__) && !defined(AST_SPINLOCK_TYPE)
  219. #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_GAS_SPARC
  220. #define AST_SPINLOCK_TYPE_LABEL "gas_sparc"
  221. typedef volatile unsigned char ast_spinlock_t;
  222. static force_inline int ast_spinlock_init(ast_spinlock_t *lock)
  223. {
  224. *lock = 0;
  225. return 0;
  226. }
  227. static force_inline int ast_spinlock_lock(ast_spinlock_t *lock)
  228. {
  229. unsigned char tmp;
  230. __asm__ __volatile__(
  231. "1: ldstub %[lock], %[tmp]\n"
  232. " brnz,pn %[tmp], 2f\n"
  233. " nop\n"
  234. " .subsection 2\n"
  235. "2: ldub %[lock], %[tmp]\n"
  236. " brnz,pt %[tmp], 2b\n"
  237. " nop\n"
  238. " ba,a,pt %%xcc, 1b\n"
  239. " .previous"
  240. : [tmp] "=&r" (tmp)
  241. : [lock] "m" (*lock)
  242. : "memory"
  243. );
  244. return 0;
  245. }
  246. static force_inline int ast_spinlock_trylock(ast_spinlock_t *lock)
  247. {
  248. unsigned long result = 1;
  249. __asm__ __volatile__(
  250. " ldstub %[lock], %[result]\n"
  251. : [result] "=&r" (result)
  252. : [lock] "m" (*lock)
  253. : "memory", "cc"
  254. );
  255. return (result != 0);
  256. }
  257. static force_inline int ast_spinlock_unlock(ast_spinlock_t *lock)
  258. {
  259. __asm__ __volatile__(
  260. " stb %%g0, %[lock]"
  261. :
  262. : [lock] "m" (*lock)
  263. : "memory", "cc"
  264. );
  265. return 0;
  266. }
  267. static force_inline int ast_spinlock_destroy(ast_spinlock_t *lock)
  268. {
  269. return 0;
  270. }
  271. #endif
  272. /*!
  273. * \brief Implementation using pthread_spinlock
  274. *
  275. * pthread_spinlocks are not supported on all platforms
  276. * but if for some reason none of the previous implementations are
  277. * available, it can be used with reasonable performance.
  278. *
  279. */
  280. #if defined (HAVE_PTHREAD_SPINLOCK) && !defined(AST_SPINLOCK_TYPE)
  281. #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_PTHREAD_SPINLOCK
  282. #define AST_SPINLOCK_TYPE_LABEL "pthread_spinlock"
  283. typedef pthread_spinlock_t ast_spinlock_t;
  284. static force_inline int ast_spinlock_init(ast_spinlock_t *lock)
  285. {
  286. return pthread_spin_init(lock, PTHREAD_PROCESS_PRIVATE);
  287. }
  288. static force_inline int ast_spinlock_lock(ast_spinlock_t *lock)
  289. {
  290. return pthread_spin_lock(lock);
  291. }
  292. static force_inline int ast_spinlock_trylock(ast_spinlock_t *lock)
  293. {
  294. return pthread_spin_trylock(lock);
  295. }
  296. static force_inline int ast_spinlock_unlock(ast_spinlock_t *lock)
  297. {
  298. return pthread_spin_unlock(lock);
  299. }
  300. static force_inline int ast_spinlock_destroy(ast_spinlock_t *lock)
  301. {
  302. return pthread_spin_destroy(lock);
  303. }
  304. #endif
  305. /*!
  306. * \brief Implementation using OSX Atomics
  307. *
  308. * The Darwin/Mac OSX platform has its own atomics
  309. * implementation but it uses more kernel time than
  310. * GCC atomics and x86 assembly. It is included
  311. * as an unlikely fallback.
  312. *
  313. */
  314. #if defined(HAVE_OSX_ATOMICS) && !defined(AST_SPINLOCK_TYPE)
  315. #include <libkern/OSAtomic.h>
  316. #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_OSX_ATOMICS
  317. #define AST_SPINLOCK_TYPE_LABEL "osx_atomics"
  318. typedef OSSpinLock ast_spinlock_t;
  319. static force_inline int ast_spinlock_init(ast_spinlock_t *lock)
  320. {
  321. *lock = OS_SPINLOCK_INIT;
  322. return 0;
  323. }
  324. static force_inline int ast_spinlock_lock(ast_spinlock_t *lock)
  325. {
  326. OSSpinLockLock(lock);
  327. return 0;
  328. }
  329. static force_inline int ast_spinlock_trylock(ast_spinlock_t *lock)
  330. {
  331. return !OSSpinLockTry(lock);
  332. }
  333. static force_inline int ast_spinlock_unlock(ast_spinlock_t *lock)
  334. {
  335. OSSpinLockUnlock(lock);
  336. return 0;
  337. }
  338. static force_inline int ast_spinlock_destroy(ast_spinlock_t *lock)
  339. {
  340. return 0;
  341. }
  342. #endif
  343. /*!
  344. * \brief Implementation using pthread_mutex
  345. *
  346. * pthread_mutex is supported on all platforms but
  347. * it is also the worst performing. It is included
  348. * as an unlikely fallback.
  349. *
  350. */
  351. #if !defined(AST_SPINLOCK_TYPE)
  352. #define AST_SPINLOCK_TYPE AST_SPINLOCK_TYPE_PTHREAD_MUTEX
  353. #define AST_SPINLOCK_TYPE_LABEL "pthread_mutex"
  354. typedef pthread_mutex_t ast_spinlock_t;
  355. static force_inline int ast_spinlock_init(ast_spinlock_t *lock)
  356. {
  357. pthread_mutex_init(lock, NULL);
  358. return 0;
  359. }
  360. static force_inline int ast_spinlock_lock(ast_spinlock_t *lock)
  361. {
  362. return pthread_mutex_lock(lock);
  363. }
  364. static force_inline int ast_spinlock_trylock(ast_spinlock_t *lock)
  365. {
  366. return pthread_mutex_trylock(lock);
  367. }
  368. static force_inline int ast_spinlock_unlock(ast_spinlock_t *lock)
  369. {
  370. return pthread_mutex_unlock(lock);
  371. }
  372. static force_inline int ast_spinlock_destroy(ast_spinlock_t *lock)
  373. {
  374. return pthread_mutex_destroy(lock);
  375. }
  376. #endif
  377. #if !defined(AST_SPINLOCK_TYPE)
  378. #error "No spinlock implementation could be found."
  379. #endif
  380. /* Prototypes are declared here to insure that each implementation provides
  381. * the same API and to act as placeholders for the documentation.
  382. */
  383. /*!
  384. * \brief Initialize a spin lock
  385. * \param lock Address of the lock
  386. * \retval 0 Success
  387. * \retval other Failure
  388. */
  389. static force_inline int ast_spinlock_init(ast_spinlock_t *lock);
  390. /*!
  391. * \brief Lock a spin lock
  392. * \param lock Address of the lock
  393. * \retval 0 Success
  394. * \retval other Failure
  395. */
  396. static force_inline int ast_spinlock_lock(ast_spinlock_t *lock);
  397. /*!
  398. * \brief Try to lock a spin lock
  399. *
  400. * Attempt to gain a lock. Return immediately
  401. * regardless of result.
  402. *
  403. * \param lock Address of the lock
  404. * \retval 0 Success
  405. * \retval other Lock was not obtained
  406. */
  407. static force_inline int ast_spinlock_trylock(ast_spinlock_t *lock);
  408. /*!
  409. * \brief Unlock a spin lock
  410. * \param lock Address of the lock
  411. * \retval 0 Success
  412. * \retval other Failure
  413. */
  414. static force_inline int ast_spinlock_unlock(ast_spinlock_t *lock);
  415. /*!
  416. * \brief Destroy a spin lock
  417. * \param lock Address of the lock
  418. * \retval 0 Success
  419. * \retval other Failure
  420. */
  421. static force_inline int ast_spinlock_destroy(ast_spinlock_t *lock);
  422. #endif /* _ASTERISK_SPINLOCK_H */