| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Semaphore implementation exposed to Go. | |
| // Intended use is provide a sleep and wakeup | |
| // primitive that can be used in the contended case | |
| // of other synchronization primitives. | |
| // Thus it targets the same goal as Linux's futex, | |
| // but it has much simpler semantics. | |
| // | |
| // That is, don't think of these as semaphores. | |
| // Think of them as a way to implement sleep and wakeup | |
| // such that every sleep is paired with a single wakeup, | |
| // even if, due to races, the wakeup happens before the sleep. | |
| // | |
| // See Mullender and Cox, ``Semaphores in Plan 9,'' | |
| // http://swtch.com/semaphore.pdf | |
| package runtime | |
| #include "runtime.h" | |
| #include "arch.h" | |
| typedef struct Sema Sema; | |
| struct Sema | |
| { | |
| uint32 volatile *addr; | |
| G *g; | |
| Sema *prev; | |
| Sema *next; | |
| }; | |
| typedef struct SemaRoot SemaRoot; | |
| struct SemaRoot | |
| { | |
| Lock; | |
| Sema *head; | |
| Sema *tail; | |
| // Number of waiters. Read w/o the lock. | |
| uint32 volatile nwait; | |
| }; | |
| // Prime to not correlate with any user patterns. | |
| #define SEMTABLESZ 251 | |
| static union | |
| { | |
| SemaRoot; | |
| uint8 pad[CacheLineSize]; | |
| } semtable[SEMTABLESZ]; | |
| static SemaRoot* | |
| semroot(uint32 *addr) | |
| { | |
| return &semtable[((uintptr)addr >> 3) % SEMTABLESZ]; | |
| } | |
| static void | |
| semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s) | |
| { | |
| s->g = g; | |
| s->addr = addr; | |
| s->next = nil; | |
| s->prev = root->tail; | |
| if(root->tail) | |
| root->tail->next = s; | |
| else | |
| root->head = s; | |
| root->tail = s; | |
| } | |
| static void | |
| semdequeue(SemaRoot *root, Sema *s) | |
| { | |
| if(s->next) | |
| s->next->prev = s->prev; | |
| else | |
| root->tail = s->prev; | |
| if(s->prev) | |
| s->prev->next = s->next; | |
| else | |
| root->head = s->next; | |
| s->prev = nil; | |
| s->next = nil; | |
| } | |
| static int32 | |
| cansemacquire(uint32 *addr) | |
| { | |
| uint32 v; | |
| while((v = runtime·atomicload(addr)) > 0) | |
| if(runtime·cas(addr, v, v-1)) | |
| return 1; | |
| return 0; | |
| } | |
| void | |
| runtime·semacquire(uint32 volatile *addr) | |
| { | |
| Sema s; | |
| SemaRoot *root; | |
| // Easy case. | |
| if(cansemacquire(addr)) | |
| return; | |
| // Harder case: | |
| // increment waiter count | |
| // try cansemacquire one more time, return if succeeded | |
| // enqueue itself as a waiter | |
| // sleep | |
| // (waiter descriptor is dequeued by signaler) | |
| root = semroot(addr); | |
| for(;;) { | |
| runtime·lock(root); | |
| // Add ourselves to nwait to disable "easy case" in semrelease. | |
| runtime·xadd(&root->nwait, 1); | |
| // Check cansemacquire to avoid missed wakeup. | |
| if(cansemacquire(addr)) { | |
| runtime·xadd(&root->nwait, -1); | |
| runtime·unlock(root); | |
| return; | |
| } | |
| // Any semrelease after the cansemacquire knows we're waiting | |
| // (we set nwait above), so go to sleep. | |
| semqueue(root, addr, &s); | |
| g->status = Gwaiting; | |
| g->waitreason = "semacquire"; | |
| runtime·unlock(root); | |
| runtime·gosched(); | |
| if(cansemacquire(addr)) | |
| return; | |
| } | |
| } | |
| void | |
| runtime·semrelease(uint32 volatile *addr) | |
| { | |
| Sema *s; | |
| SemaRoot *root; | |
| root = semroot(addr); | |
| runtime·xadd(addr, 1); | |
| // Easy case: no waiters? | |
| // This check must happen after the xadd, to avoid a missed wakeup | |
| // (see loop in semacquire). | |
| if(runtime·atomicload(&root->nwait) == 0) | |
| return; | |
| // Harder case: search for a waiter and wake it. | |
| runtime·lock(root); | |
| if(runtime·atomicload(&root->nwait) == 0) { | |
| // The count is already consumed by another goroutine, | |
| // so no need to wake up another goroutine. | |
| runtime·unlock(root); | |
| return; | |
| } | |
| for(s = root->head; s; s = s->next) { | |
| if(s->addr == addr) { | |
| runtime·xadd(&root->nwait, -1); | |
| semdequeue(root, s); | |
| break; | |
| } | |
| } | |
| runtime·unlock(root); | |
| if(s) | |
| runtime·ready(s->g); | |
| } | |
| func Semacquire(addr *uint32) { | |
| runtime·semacquire(addr); | |
| } | |
| func Semrelease(addr *uint32) { | |
| runtime·semrelease(addr); | |
| } |