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;
34 typedef struct SemaRoot SemaRoot;
40 // Number of waiters. Read w/o the lock.
41 uint32 volatile nwait;
44 // Prime to not correlate with any user patterns.
45 #define SEMTABLESZ 251
50 uint8 pad[CacheLineSize];
52 static union semtable semtable[SEMTABLESZ];
55 semroot(uint32 volatile *addr)
57 return &semtable[((uintptr)addr >> 3) % SEMTABLESZ];
61 semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s)
75 semdequeue(SemaRoot *root, Sema *s)
78 s->next->prev = s->prev;
82 s->prev->next = s->next;
90 cansemacquire(uint32 volatile *addr)
94 while((v = runtime_atomicload(addr)) > 0)
95 if(runtime_cas(addr, v, v-1))
101 semacquireimpl(uint32 volatile *addr, int32 profile)
103 Sema s; // Needs to be allocated on stack, otherwise garbage collector could deallocate it
108 if(cansemacquire(addr))
112 // increment waiter count
113 // try cansemacquire one more time, return if succeeded
114 // enqueue itself as a waiter
116 // (waiter descriptor is dequeued by signaler)
117 root = semroot(addr);
120 if(profile && runtime_blockprofilerate > 0) {
121 t0 = runtime_cputicks();
127 // Add ourselves to nwait to disable "easy case" in semrelease.
128 runtime_xadd(&root->nwait, 1);
129 // Check cansemacquire to avoid missed wakeup.
130 if(cansemacquire(addr)) {
131 runtime_xadd(&root->nwait, -1);
132 runtime_unlock(root);
135 // Any semrelease after the cansemacquire knows we're waiting
136 // (we set nwait above), so go to sleep.
137 semqueue(root, addr, &s);
138 runtime_park(runtime_unlock, root, "semacquire");
139 if(cansemacquire(addr)) {
141 runtime_blockevent(s.releasetime - t0, 3);
148 runtime_semacquire(uint32 volatile *addr)
150 semacquireimpl(addr, 0);
154 runtime_semrelease(uint32 volatile *addr)
159 root = semroot(addr);
160 runtime_xadd(addr, 1);
162 // Easy case: no waiters?
163 // This check must happen after the xadd, to avoid a missed wakeup
164 // (see loop in semacquire).
165 if(runtime_atomicload(&root->nwait) == 0)
168 // Harder case: search for a waiter and wake it.
170 if(runtime_atomicload(&root->nwait) == 0) {
171 // The count is already consumed by another goroutine,
172 // so no need to wake up another goroutine.
173 runtime_unlock(root);
176 for(s = root->head; s; s = s->next) {
177 if(s->addr == addr) {
178 runtime_xadd(&root->nwait, -1);
183 runtime_unlock(root);
186 s->releasetime = runtime_cputicks();
191 func runtime_Semacquire(addr *uint32) {
192 semacquireimpl(addr, 1);
195 func runtime_Semrelease(addr *uint32) {
196 runtime_semrelease(addr);