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