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.
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.
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.
17 // See Mullender and Cox, ``Semaphores in Plan 9,''
18 // http://swtch.com/semaphore.pdf
24 typedef struct Sema Sema;
27 uint32 volatile *addr;
33 typedef struct SemaRoot SemaRoot;
39 // Number of waiters. Read w/o the lock.
40 uint32 volatile nwait;
43 // Prime to not correlate with any user patterns.
44 #define SEMTABLESZ 251
49 uint8 pad[CacheLineSize];
50 } semtable[SEMTABLESZ];
53 semroot(uint32 volatile *addr)
55 return &semtable[((uintptr)addr >> 3) % SEMTABLESZ];
59 semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s)
73 semdequeue(SemaRoot *root, Sema *s)
76 s->next->prev = s->prev;
80 s->prev->next = s->next;
88 cansemacquire(uint32 volatile *addr)
92 while((v = runtime_atomicload(addr)) > 0)
93 if(runtime_cas(addr, v, v-1))
99 runtime_semacquire(uint32 volatile *addr)
106 if(cansemacquire(addr))
110 // increment waiter count
111 // try cansemacquire one more time, return if succeeded
112 // enqueue itself as a waiter
114 // (waiter descriptor is dequeued by signaler)
116 root = semroot(addr);
120 // Add ourselves to nwait to disable "easy case" in semrelease.
121 runtime_xadd(&root->nwait, 1);
122 // Check cansemacquire to avoid missed wakeup.
123 if(cansemacquire(addr)) {
124 runtime_xadd(&root->nwait, -1);
125 runtime_unlock(root);
128 // Any semrelease after the cansemacquire knows we're waiting
129 // (we set nwait above), so go to sleep.
130 semqueue(root, addr, &s);
131 g->status = Gwaiting;
132 g->waitreason = "semacquire";
133 runtime_unlock(root);
135 if(cansemacquire(addr))
141 runtime_semrelease(uint32 volatile *addr)
146 root = semroot(addr);
147 runtime_xadd(addr, 1);
149 // Easy case: no waiters?
150 // This check must happen after the xadd, to avoid a missed wakeup
151 // (see loop in semacquire).
152 if(runtime_atomicload(&root->nwait) == 0)
155 // Harder case: search for a waiter and wake it.
157 if(runtime_atomicload(&root->nwait) == 0) {
158 // The count is already consumed by another goroutine,
159 // so no need to wake up another goroutine.
160 runtime_unlock(root);
163 for(s = root->head; s; s = s->next) {
164 if(s->addr == addr) {
165 runtime_xadd(&root->nwait, -1);
170 runtime_unlock(root);
175 func Semacquire(addr *uint32) {
176 runtime_semacquire(addr);
179 func Semrelease(addr *uint32) {
180 runtime_semrelease(addr);