lock_futex.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build dragonfly freebsd linux
  5. #include "runtime.h"
  6. // This implementation depends on OS-specific implementations of
  7. //
  8. // runtime_futexsleep(uint32 *addr, uint32 val, int64 ns)
  9. // Atomically,
  10. // if(*addr == val) sleep
  11. // Might be woken up spuriously; that's allowed.
  12. // Don't sleep longer than ns; ns < 0 means forever.
  13. //
  14. // runtime_futexwakeup(uint32 *addr, uint32 cnt)
  15. // If any procs are sleeping on addr, wake up at most cnt.
  16. enum
  17. {
  18. MUTEX_UNLOCKED = 0,
  19. MUTEX_LOCKED = 1,
  20. MUTEX_SLEEPING = 2,
  21. ACTIVE_SPIN = 4,
  22. ACTIVE_SPIN_CNT = 30,
  23. PASSIVE_SPIN = 1,
  24. };
  25. // Possible lock states are MUTEX_UNLOCKED, MUTEX_LOCKED and MUTEX_SLEEPING.
  26. // MUTEX_SLEEPING means that there is presumably at least one sleeping thread.
  27. // Note that there can be spinning threads during all states - they do not
  28. // affect mutex's state.
  29. void
  30. runtime_lock(Lock *l)
  31. {
  32. uint32 i, v, wait, spin;
  33. if(runtime_m()->locks++ < 0)
  34. runtime_throw("runtime_lock: lock count");
  35. // Speculative grab for lock.
  36. v = runtime_xchg((uint32*)&l->key, MUTEX_LOCKED);
  37. if(v == MUTEX_UNLOCKED)
  38. return;
  39. // wait is either MUTEX_LOCKED or MUTEX_SLEEPING
  40. // depending on whether there is a thread sleeping
  41. // on this mutex. If we ever change l->key from
  42. // MUTEX_SLEEPING to some other value, we must be
  43. // careful to change it back to MUTEX_SLEEPING before
  44. // returning, to ensure that the sleeping thread gets
  45. // its wakeup call.
  46. wait = v;
  47. // On uniprocessor's, no point spinning.
  48. // On multiprocessors, spin for ACTIVE_SPIN attempts.
  49. spin = 0;
  50. if(runtime_ncpu > 1)
  51. spin = ACTIVE_SPIN;
  52. for(;;) {
  53. // Try for lock, spinning.
  54. for(i = 0; i < spin; i++) {
  55. while(l->key == MUTEX_UNLOCKED)
  56. if(runtime_cas((uint32*)&l->key, MUTEX_UNLOCKED, wait))
  57. return;
  58. runtime_procyield(ACTIVE_SPIN_CNT);
  59. }
  60. // Try for lock, rescheduling.
  61. for(i=0; i < PASSIVE_SPIN; i++) {
  62. while(l->key == MUTEX_UNLOCKED)
  63. if(runtime_cas((uint32*)&l->key, MUTEX_UNLOCKED, wait))
  64. return;
  65. runtime_osyield();
  66. }
  67. // Sleep.
  68. v = runtime_xchg((uint32*)&l->key, MUTEX_SLEEPING);
  69. if(v == MUTEX_UNLOCKED)
  70. return;
  71. wait = MUTEX_SLEEPING;
  72. runtime_futexsleep((uint32*)&l->key, MUTEX_SLEEPING, -1);
  73. }
  74. }
  75. void
  76. runtime_unlock(Lock *l)
  77. {
  78. uint32 v;
  79. v = runtime_xchg((uint32*)&l->key, MUTEX_UNLOCKED);
  80. if(v == MUTEX_UNLOCKED)
  81. runtime_throw("unlock of unlocked lock");
  82. if(v == MUTEX_SLEEPING)
  83. runtime_futexwakeup((uint32*)&l->key, 1);
  84. if(--runtime_m()->locks < 0)
  85. runtime_throw("runtime_unlock: lock count");
  86. }
  87. // One-time notifications.
  88. void
  89. runtime_noteclear(Note *n)
  90. {
  91. n->key = 0;
  92. }
  93. void
  94. runtime_notewakeup(Note *n)
  95. {
  96. uint32 old;
  97. old = runtime_xchg((uint32*)&n->key, 1);
  98. if(old != 0) {
  99. runtime_printf("notewakeup - double wakeup (%d)\n", old);
  100. runtime_throw("notewakeup - double wakeup");
  101. }
  102. runtime_futexwakeup((uint32*)&n->key, 1);
  103. }
  104. void
  105. runtime_notesleep(Note *n)
  106. {
  107. M *m = runtime_m();
  108. /* For gccgo it's OK to sleep in non-g0, and it happens in
  109. stoptheworld because we have not implemented preemption.
  110. if(runtime_g() != runtime_m()->g0)
  111. runtime_throw("notesleep not on g0");
  112. */
  113. while(runtime_atomicload((uint32*)&n->key) == 0) {
  114. m->blocked = true;
  115. runtime_futexsleep((uint32*)&n->key, 0, -1);
  116. m->blocked = false;
  117. }
  118. }
  119. static bool
  120. notetsleep(Note *n, int64 ns, int64 deadline, int64 now)
  121. {
  122. M *m = runtime_m();
  123. // Conceptually, deadline and now are local variables.
  124. // They are passed as arguments so that the space for them
  125. // does not count against our nosplit stack sequence.
  126. if(ns < 0) {
  127. while(runtime_atomicload((uint32*)&n->key) == 0) {
  128. m->blocked = true;
  129. runtime_futexsleep((uint32*)&n->key, 0, -1);
  130. m->blocked = false;
  131. }
  132. return true;
  133. }
  134. if(runtime_atomicload((uint32*)&n->key) != 0)
  135. return true;
  136. deadline = runtime_nanotime() + ns;
  137. for(;;) {
  138. m->blocked = true;
  139. runtime_futexsleep((uint32*)&n->key, 0, ns);
  140. m->blocked = false;
  141. if(runtime_atomicload((uint32*)&n->key) != 0)
  142. break;
  143. now = runtime_nanotime();
  144. if(now >= deadline)
  145. break;
  146. ns = deadline - now;
  147. }
  148. return runtime_atomicload((uint32*)&n->key) != 0;
  149. }
  150. bool
  151. runtime_notetsleep(Note *n, int64 ns)
  152. {
  153. bool res;
  154. if(runtime_g() != runtime_m()->g0 && !runtime_m()->gcing)
  155. runtime_throw("notetsleep not on g0");
  156. res = notetsleep(n, ns, 0, 0);
  157. return res;
  158. }
  159. // same as runtime_notetsleep, but called on user g (not g0)
  160. // calls only nosplit functions between entersyscallblock/exitsyscall
  161. bool
  162. runtime_notetsleepg(Note *n, int64 ns)
  163. {
  164. bool res;
  165. if(runtime_g() == runtime_m()->g0)
  166. runtime_throw("notetsleepg on g0");
  167. runtime_entersyscallblock();
  168. res = notetsleep(n, ns, 0, 0);
  169. runtime_exitsyscall();
  170. return res;
  171. }