sema.goc 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. // Copyright 2009 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. // Semaphore implementation exposed to Go.
  5. // Intended use is provide a sleep and wakeup
  6. // primitive that can be used in the contended case
  7. // of other synchronization primitives.
  8. // Thus it targets the same goal as Linux's futex,
  9. // but it has much simpler semantics.
  10. //
  11. // That is, don't think of these as semaphores.
  12. // Think of them as a way to implement sleep and wakeup
  13. // such that every sleep is paired with a single wakeup,
  14. // even if, due to races, the wakeup happens before the sleep.
  15. //
  16. // See Mullender and Cox, ``Semaphores in Plan 9,''
  17. // http://swtch.com/semaphore.pdf
  18. package sync
  19. #include "runtime.h"
  20. #include "arch.h"
  21. typedef struct SemaWaiter SemaWaiter;
  22. struct SemaWaiter
  23. {
  24. uint32 volatile* addr;
  25. G* g;
  26. int64 releasetime;
  27. int32 nrelease; // -1 for acquire
  28. SemaWaiter* prev;
  29. SemaWaiter* next;
  30. };
  31. typedef struct SemaRoot SemaRoot;
  32. struct SemaRoot
  33. {
  34. Lock;
  35. SemaWaiter* head;
  36. SemaWaiter* tail;
  37. // Number of waiters. Read w/o the lock.
  38. uint32 volatile nwait;
  39. };
  40. // Prime to not correlate with any user patterns.
  41. #define SEMTABLESZ 251
  42. struct semtable
  43. {
  44. SemaRoot;
  45. uint8 pad[CacheLineSize-sizeof(SemaRoot)];
  46. };
  47. static struct semtable semtable[SEMTABLESZ];
  48. static SemaRoot*
  49. semroot(uint32 volatile *addr)
  50. {
  51. return &semtable[((uintptr)addr >> 3) % SEMTABLESZ];
  52. }
  53. static void
  54. semqueue(SemaRoot *root, uint32 volatile *addr, SemaWaiter *s)
  55. {
  56. s->g = runtime_g();
  57. s->addr = addr;
  58. s->next = nil;
  59. s->prev = root->tail;
  60. if(root->tail)
  61. root->tail->next = s;
  62. else
  63. root->head = s;
  64. root->tail = s;
  65. }
  66. static void
  67. semdequeue(SemaRoot *root, SemaWaiter *s)
  68. {
  69. if(s->next)
  70. s->next->prev = s->prev;
  71. else
  72. root->tail = s->prev;
  73. if(s->prev)
  74. s->prev->next = s->next;
  75. else
  76. root->head = s->next;
  77. s->prev = nil;
  78. s->next = nil;
  79. }
  80. static int32
  81. cansemacquire(uint32 volatile *addr)
  82. {
  83. uint32 v;
  84. while((v = runtime_atomicload(addr)) > 0)
  85. if(runtime_cas(addr, v, v-1))
  86. return 1;
  87. return 0;
  88. }
  89. void
  90. runtime_semacquire(uint32 volatile *addr, bool profile)
  91. {
  92. SemaWaiter s; // Needs to be allocated on stack, otherwise garbage collector could deallocate it
  93. SemaRoot *root;
  94. int64 t0;
  95. // Easy case.
  96. if(cansemacquire(addr))
  97. return;
  98. // Harder case:
  99. // increment waiter count
  100. // try cansemacquire one more time, return if succeeded
  101. // enqueue itself as a waiter
  102. // sleep
  103. // (waiter descriptor is dequeued by signaler)
  104. root = semroot(addr);
  105. t0 = 0;
  106. s.releasetime = 0;
  107. if(profile && runtime_blockprofilerate > 0) {
  108. t0 = runtime_cputicks();
  109. s.releasetime = -1;
  110. }
  111. for(;;) {
  112. runtime_lock(root);
  113. // Add ourselves to nwait to disable "easy case" in semrelease.
  114. runtime_xadd(&root->nwait, 1);
  115. // Check cansemacquire to avoid missed wakeup.
  116. if(cansemacquire(addr)) {
  117. runtime_xadd(&root->nwait, -1);
  118. runtime_unlock(root);
  119. return;
  120. }
  121. // Any semrelease after the cansemacquire knows we're waiting
  122. // (we set nwait above), so go to sleep.
  123. semqueue(root, addr, &s);
  124. runtime_parkunlock(root, "semacquire");
  125. if(cansemacquire(addr)) {
  126. if(t0)
  127. runtime_blockevent(s.releasetime - t0, 3);
  128. return;
  129. }
  130. }
  131. }
  132. void
  133. runtime_semrelease(uint32 volatile *addr)
  134. {
  135. SemaWaiter *s;
  136. SemaRoot *root;
  137. root = semroot(addr);
  138. runtime_xadd(addr, 1);
  139. // Easy case: no waiters?
  140. // This check must happen after the xadd, to avoid a missed wakeup
  141. // (see loop in semacquire).
  142. if(runtime_atomicload(&root->nwait) == 0)
  143. return;
  144. // Harder case: search for a waiter and wake it.
  145. runtime_lock(root);
  146. if(runtime_atomicload(&root->nwait) == 0) {
  147. // The count is already consumed by another goroutine,
  148. // so no need to wake up another goroutine.
  149. runtime_unlock(root);
  150. return;
  151. }
  152. for(s = root->head; s; s = s->next) {
  153. if(s->addr == addr) {
  154. runtime_xadd(&root->nwait, -1);
  155. semdequeue(root, s);
  156. break;
  157. }
  158. }
  159. runtime_unlock(root);
  160. if(s) {
  161. if(s->releasetime)
  162. s->releasetime = runtime_cputicks();
  163. runtime_ready(s->g);
  164. }
  165. }
  166. // TODO(dvyukov): move to netpoll.goc once it's used by all OSes.
  167. void net_runtime_Semacquire(uint32 *addr)
  168. __asm__ (GOSYM_PREFIX "net.runtime_Semacquire");
  169. void net_runtime_Semacquire(uint32 *addr)
  170. {
  171. runtime_semacquire(addr, true);
  172. }
  173. void net_runtime_Semrelease(uint32 *addr)
  174. __asm__ (GOSYM_PREFIX "net.runtime_Semrelease");
  175. void net_runtime_Semrelease(uint32 *addr)
  176. {
  177. runtime_semrelease(addr);
  178. }
  179. func runtime_Semacquire(addr *uint32) {
  180. runtime_semacquire(addr, true);
  181. }
  182. func runtime_Semrelease(addr *uint32) {
  183. runtime_semrelease(addr);
  184. }
  185. typedef struct SyncSema SyncSema;
  186. struct SyncSema
  187. {
  188. Lock;
  189. SemaWaiter* head;
  190. SemaWaiter* tail;
  191. };
  192. func runtime_Syncsemcheck(size uintptr) {
  193. if(size != sizeof(SyncSema)) {
  194. runtime_printf("bad SyncSema size: sync:%D runtime:%D\n", (int64)size, (int64)sizeof(SyncSema));
  195. runtime_throw("bad SyncSema size");
  196. }
  197. }
  198. // Syncsemacquire waits for a pairing Syncsemrelease on the same semaphore s.
  199. func runtime_Syncsemacquire(s *SyncSema) {
  200. SemaWaiter w, *wake;
  201. int64 t0;
  202. w.g = runtime_g();
  203. w.nrelease = -1;
  204. w.next = nil;
  205. w.releasetime = 0;
  206. t0 = 0;
  207. if(runtime_blockprofilerate > 0) {
  208. t0 = runtime_cputicks();
  209. w.releasetime = -1;
  210. }
  211. runtime_lock(s);
  212. if(s->head && s->head->nrelease > 0) {
  213. // have pending release, consume it
  214. wake = nil;
  215. s->head->nrelease--;
  216. if(s->head->nrelease == 0) {
  217. wake = s->head;
  218. s->head = wake->next;
  219. if(s->head == nil)
  220. s->tail = nil;
  221. }
  222. runtime_unlock(s);
  223. if(wake)
  224. runtime_ready(wake->g);
  225. } else {
  226. // enqueue itself
  227. if(s->tail == nil)
  228. s->head = &w;
  229. else
  230. s->tail->next = &w;
  231. s->tail = &w;
  232. runtime_parkunlock(s, "semacquire");
  233. if(t0)
  234. runtime_blockevent(w.releasetime - t0, 2);
  235. }
  236. }
  237. // Syncsemrelease waits for n pairing Syncsemacquire on the same semaphore s.
  238. func runtime_Syncsemrelease(s *SyncSema, n uint32) {
  239. SemaWaiter w, *wake;
  240. w.g = runtime_g();
  241. w.nrelease = (int32)n;
  242. w.next = nil;
  243. w.releasetime = 0;
  244. runtime_lock(s);
  245. while(w.nrelease > 0 && s->head && s->head->nrelease < 0) {
  246. // have pending acquire, satisfy it
  247. wake = s->head;
  248. s->head = wake->next;
  249. if(s->head == nil)
  250. s->tail = nil;
  251. if(wake->releasetime)
  252. wake->releasetime = runtime_cputicks();
  253. runtime_ready(wake->g);
  254. w.nrelease--;
  255. }
  256. if(w.nrelease > 0) {
  257. // enqueue itself
  258. if(s->tail == nil)
  259. s->head = &w;
  260. else
  261. s->tail->next = &w;
  262. s->tail = &w;
  263. runtime_parkunlock(s, "semarelease");
  264. } else
  265. runtime_unlock(s);
  266. }