futexlock.h 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. * $Id$
  3. *
  4. * Copyright (C) 2007 iptelorg GmbH
  5. *
  6. * Permission to use, copy, modify, and distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. /*
  19. * futex based lock (mutex) implementation (linux 2.6+ only)
  20. * based on Ulrich Drepper implementation in "Futexes Are Tricky"
  21. * (http://people.redhat.com/drepper/futex.pdf)
  22. *
  23. * Implements:
  24. * void futex_get(futex_lock_t* lock); - mutex lock
  25. * void futex_release(futex_lock_t* lock); - unlock
  26. * int futex_try(futex_lock_t* lock); - tries to get lock, returns 0
  27. * on success and !=0 on failure
  28. * (1 or 2)
  29. *
  30. * Config defines:
  31. */
  32. /*
  33. * History:
  34. * --------
  35. * 2007-05-13 created by andrei
  36. * 2007-06-12 added ADAPTIVE_WAIT busy waiting (andrei)
  37. */
  38. #ifndef _futexlock_h
  39. #define _futexlock_h
  40. #include "atomic/atomic_common.h"
  41. #include "atomic/atomic_native.h"
  42. #ifdef HAVE_ASM_INLINE_ATOMIC_OPS
  43. #define HAVE_FUTEX
  44. #include <sys/types.h> /* hack to workaround some type conflicts
  45. between linux-libc-dev andlibc headers
  46. in recent (6.08.2008) x86_64 debian sid
  47. installations */
  48. /* hack to work with old linux/futex.h versions, that depend on sched.h in
  49. __KERNEL__ mode (futex.h < 2.6.20) */
  50. #include <linux/types.h>
  51. typedef __u32 u32;
  52. struct task_struct;
  53. /* end of the hack */
  54. /* another hack this time for OpenSuse 10.2:
  55. futex.h uses a __user attribute, which is defined in linux/compiler.h
  56. However linux/compiler.h is not part of the kernel headers package in
  57. most distributions. Instead they ship a modified linux/futex.h that does
  58. not include <linux/compile.h> and does not user __user.
  59. */
  60. #ifndef __user
  61. #define __user
  62. #endif /* __user__*/
  63. /* end of hack */
  64. #include <linux/futex.h>
  65. #include <sys/syscall.h>
  66. #include <unistd.h>
  67. #include "compiler_opt.h"
  68. /* either syscall directly or #include <sys/linux/syscall.h> and use
  69. * sys_futex directly */
  70. #define sys_futex(addr, op, val, timeout, addr2, val3) \
  71. syscall(__NR_futex , (addr), (op), (val), (timeout), (addr2), (val3))
  72. typedef atomic_t futex_lock_t;
  73. /* the mutex has 3 states: 0 - free/unlocked and nobody waiting
  74. * 1 - locked and nobody waiting for it
  75. * 2 - locked w/ 0 or more waiting processes/threads
  76. */
  77. inline static futex_lock_t* futex_init(futex_lock_t* lock)
  78. {
  79. atomic_set(lock, 0);
  80. return lock;
  81. }
  82. inline static void futex_get(futex_lock_t* lock)
  83. {
  84. int v;
  85. #ifdef ADAPTIVE_WAIT
  86. register int i=ADAPTIVE_WAIT_LOOPS;
  87. retry:
  88. #endif
  89. v=atomic_cmpxchg(lock, 0, 1); /* lock if 0 */
  90. if (likely(v==0)){ /* optimize for the uncontended case */
  91. /* success */
  92. membar_enter_lock();
  93. return;
  94. }else if (unlikely(v==2)){ /* if contended, optimize for the one waiter
  95. case */
  96. /* waiting processes/threads => add ourselves to the queue */
  97. do{
  98. sys_futex(&(lock)->val, FUTEX_WAIT, 2, 0, 0, 0);
  99. v=atomic_get_and_set(lock, 2);
  100. }while(v);
  101. }else{
  102. /* v==1 */
  103. #ifdef ADAPTIVE_WAIT
  104. if (i>0){
  105. i--;
  106. goto retry;
  107. }
  108. #endif
  109. v=atomic_get_and_set(lock, 2);
  110. while(v){
  111. sys_futex(&(lock)->val, FUTEX_WAIT, 2, 0, 0, 0);
  112. v=atomic_get_and_set(lock, 2);
  113. }
  114. }
  115. membar_enter_lock();
  116. }
  117. inline static void futex_release(futex_lock_t* lock)
  118. {
  119. int v;
  120. membar_leave_lock();
  121. v=atomic_get_and_set(lock, 0);
  122. if (unlikely(v==2)){ /* optimize for the uncontended case */
  123. sys_futex(&(lock)->val, FUTEX_WAKE, 1, 0, 0, 0);
  124. }
  125. }
  126. static inline int futex_try(futex_lock_t* lock)
  127. {
  128. int c;
  129. c=atomic_cmpxchg(lock, 0, 1);
  130. if (likely(c))
  131. membar_enter_lock();
  132. return c;
  133. }
  134. #else /*HAVE_ASM_INLINE_ATOMIC_OPS*/
  135. #undef USE_FUTEX
  136. #endif /*HAVE_ASM_INLINE_ATOMIC_OPS*/
  137. #endif /* _futexlocks_h*/