lock_sema.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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 darwin nacl netbsd openbsd plan9 solaris windows
  5. #include "runtime.h"
  6. // This implementation depends on OS-specific implementations of
  7. //
  8. // uintptr runtime_semacreate(void)
  9. // Create a semaphore, which will be assigned to m->waitsema.
  10. // The zero value is treated as absence of any semaphore,
  11. // so be sure to return a non-zero value.
  12. //
  13. // int32 runtime_semasleep(int64 ns)
  14. // If ns < 0, acquire m->waitsema and return 0.
  15. // If ns >= 0, try to acquire m->waitsema for at most ns nanoseconds.
  16. // Return 0 if the semaphore was acquired, -1 if interrupted or timed out.
  17. //
  18. // int32 runtime_semawakeup(M *mp)
  19. // Wake up mp, which is or will soon be sleeping on mp->waitsema.
  20. //
  21. enum
  22. {
  23. LOCKED = 1,
  24. ACTIVE_SPIN = 4,
  25. ACTIVE_SPIN_CNT = 30,
  26. PASSIVE_SPIN = 1,
  27. };
  28. void
  29. runtime_lock(Lock *l)
  30. {
  31. M *m;
  32. uintptr v;
  33. uint32 i, spin;
  34. m = runtime_m();
  35. if(m->locks++ < 0)
  36. runtime_throw("runtime_lock: lock count");
  37. // Speculative grab for lock.
  38. if(runtime_casp((void**)&l->key, nil, (void*)LOCKED))
  39. return;
  40. if(m->waitsema == 0)
  41. m->waitsema = runtime_semacreate();
  42. // On uniprocessor's, no point spinning.
  43. // On multiprocessors, spin for ACTIVE_SPIN attempts.
  44. spin = 0;
  45. if(runtime_ncpu > 1)
  46. spin = ACTIVE_SPIN;
  47. for(i=0;; i++) {
  48. v = (uintptr)runtime_atomicloadp((void**)&l->key);
  49. if((v&LOCKED) == 0) {
  50. unlocked:
  51. if(runtime_casp((void**)&l->key, (void*)v, (void*)(v|LOCKED)))
  52. return;
  53. i = 0;
  54. }
  55. if(i<spin)
  56. runtime_procyield(ACTIVE_SPIN_CNT);
  57. else if(i<spin+PASSIVE_SPIN)
  58. runtime_osyield();
  59. else {
  60. // Someone else has it.
  61. // l->waitm points to a linked list of M's waiting
  62. // for this lock, chained through m->nextwaitm.
  63. // Queue this M.
  64. for(;;) {
  65. m->nextwaitm = (void*)(v&~LOCKED);
  66. if(runtime_casp((void**)&l->key, (void*)v, (void*)((uintptr)m|LOCKED)))
  67. break;
  68. v = (uintptr)runtime_atomicloadp((void**)&l->key);
  69. if((v&LOCKED) == 0)
  70. goto unlocked;
  71. }
  72. if(v&LOCKED) {
  73. // Queued. Wait.
  74. runtime_semasleep(-1);
  75. i = 0;
  76. }
  77. }
  78. }
  79. }
  80. void
  81. runtime_unlock(Lock *l)
  82. {
  83. uintptr v;
  84. M *mp;
  85. for(;;) {
  86. v = (uintptr)runtime_atomicloadp((void**)&l->key);
  87. if(v == LOCKED) {
  88. if(runtime_casp((void**)&l->key, (void*)LOCKED, nil))
  89. break;
  90. } else {
  91. // Other M's are waiting for the lock.
  92. // Dequeue an M.
  93. mp = (void*)(v&~LOCKED);
  94. if(runtime_casp((void**)&l->key, (void*)v, mp->nextwaitm)) {
  95. // Dequeued an M. Wake it.
  96. runtime_semawakeup(mp);
  97. break;
  98. }
  99. }
  100. }
  101. if(--runtime_m()->locks < 0)
  102. runtime_throw("runtime_unlock: lock count");
  103. }
  104. // One-time notifications.
  105. void
  106. runtime_noteclear(Note *n)
  107. {
  108. n->key = 0;
  109. }
  110. void
  111. runtime_notewakeup(Note *n)
  112. {
  113. M *mp;
  114. do
  115. mp = runtime_atomicloadp((void**)&n->key);
  116. while(!runtime_casp((void**)&n->key, mp, (void*)LOCKED));
  117. // Successfully set waitm to LOCKED.
  118. // What was it before?
  119. if(mp == nil) {
  120. // Nothing was waiting. Done.
  121. } else if(mp == (M*)LOCKED) {
  122. // Two notewakeups! Not allowed.
  123. runtime_throw("notewakeup - double wakeup");
  124. } else {
  125. // Must be the waiting m. Wake it up.
  126. runtime_semawakeup(mp);
  127. }
  128. }
  129. void
  130. runtime_notesleep(Note *n)
  131. {
  132. M *m;
  133. m = runtime_m();
  134. /* For gccgo it's OK to sleep in non-g0, and it happens in
  135. stoptheworld because we have not implemented preemption.
  136. if(runtime_g() != m->g0)
  137. runtime_throw("notesleep not on g0");
  138. */
  139. if(m->waitsema == 0)
  140. m->waitsema = runtime_semacreate();
  141. if(!runtime_casp((void**)&n->key, nil, m)) { // must be LOCKED (got wakeup)
  142. if(n->key != LOCKED)
  143. runtime_throw("notesleep - waitm out of sync");
  144. return;
  145. }
  146. // Queued. Sleep.
  147. m->blocked = true;
  148. runtime_semasleep(-1);
  149. m->blocked = false;
  150. }
  151. static bool
  152. notetsleep(Note *n, int64 ns, int64 deadline, M *mp)
  153. {
  154. M *m;
  155. m = runtime_m();
  156. // Conceptually, deadline and mp are local variables.
  157. // They are passed as arguments so that the space for them
  158. // does not count against our nosplit stack sequence.
  159. // Register for wakeup on n->waitm.
  160. if(!runtime_casp((void**)&n->key, nil, m)) { // must be LOCKED (got wakeup already)
  161. if(n->key != LOCKED)
  162. runtime_throw("notetsleep - waitm out of sync");
  163. return true;
  164. }
  165. if(ns < 0) {
  166. // Queued. Sleep.
  167. m->blocked = true;
  168. runtime_semasleep(-1);
  169. m->blocked = false;
  170. return true;
  171. }
  172. deadline = runtime_nanotime() + ns;
  173. for(;;) {
  174. // Registered. Sleep.
  175. m->blocked = true;
  176. if(runtime_semasleep(ns) >= 0) {
  177. m->blocked = false;
  178. // Acquired semaphore, semawakeup unregistered us.
  179. // Done.
  180. return true;
  181. }
  182. m->blocked = false;
  183. // Interrupted or timed out. Still registered. Semaphore not acquired.
  184. ns = deadline - runtime_nanotime();
  185. if(ns <= 0)
  186. break;
  187. // Deadline hasn't arrived. Keep sleeping.
  188. }
  189. // Deadline arrived. Still registered. Semaphore not acquired.
  190. // Want to give up and return, but have to unregister first,
  191. // so that any notewakeup racing with the return does not
  192. // try to grant us the semaphore when we don't expect it.
  193. for(;;) {
  194. mp = runtime_atomicloadp((void**)&n->key);
  195. if(mp == m) {
  196. // No wakeup yet; unregister if possible.
  197. if(runtime_casp((void**)&n->key, mp, nil))
  198. return false;
  199. } else if(mp == (M*)LOCKED) {
  200. // Wakeup happened so semaphore is available.
  201. // Grab it to avoid getting out of sync.
  202. m->blocked = true;
  203. if(runtime_semasleep(-1) < 0)
  204. runtime_throw("runtime: unable to acquire - semaphore out of sync");
  205. m->blocked = false;
  206. return true;
  207. } else
  208. runtime_throw("runtime: unexpected waitm - semaphore out of sync");
  209. }
  210. }
  211. bool
  212. runtime_notetsleep(Note *n, int64 ns)
  213. {
  214. M *m;
  215. bool res;
  216. m = runtime_m();
  217. if(runtime_g() != m->g0 && !m->gcing)
  218. runtime_throw("notetsleep not on g0");
  219. if(m->waitsema == 0)
  220. m->waitsema = runtime_semacreate();
  221. res = notetsleep(n, ns, 0, nil);
  222. return res;
  223. }
  224. // same as runtime_notetsleep, but called on user g (not g0)
  225. // calls only nosplit functions between entersyscallblock/exitsyscall
  226. bool
  227. runtime_notetsleepg(Note *n, int64 ns)
  228. {
  229. M *m;
  230. bool res;
  231. m = runtime_m();
  232. if(runtime_g() == m->g0)
  233. runtime_throw("notetsleepg on g0");
  234. if(m->waitsema == 0)
  235. m->waitsema = runtime_semacreate();
  236. runtime_entersyscallblock();
  237. res = notetsleep(n, ns, 0, nil);
  238. runtime_exitsyscall();
  239. return res;
  240. }