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
23 "runtime/internal/atomic"
27 // Asynchronous semaphore for sync.Mutex.
29 type semaRoot struct {
33 nwait uint32 // Number of waiters. Read w/o the lock.
36 // Prime to not correlate with any user patterns.
37 const semTabSize = 251
39 var semtable [semTabSize]struct {
41 pad [_CacheLineSize - unsafe.Sizeof(semaRoot{})]byte
44 //go:linkname sync_runtime_Semacquire sync.runtime_Semacquire
45 func sync_runtime_Semacquire(addr *uint32) {
46 semacquire(addr, true)
49 //go:linkname net_runtime_Semacquire net.runtime_Semacquire
50 func net_runtime_Semacquire(addr *uint32) {
51 semacquire(addr, true)
54 //go:linkname sync_runtime_Semrelease sync.runtime_Semrelease
55 func sync_runtime_Semrelease(addr *uint32) {
59 //go:linkname net_runtime_Semrelease net.runtime_Semrelease
60 func net_runtime_Semrelease(addr *uint32) {
64 // Called from runtime.
65 func semacquire(addr *uint32, profile bool) {
68 throw("semacquire not on the G stack")
72 if cansemacquire(addr) {
77 // increment waiter count
78 // try cansemacquire one more time, return if succeeded
79 // enqueue itself as a waiter
81 // (waiter descriptor is dequeued by signaler)
86 if profile && blockprofilerate > 0 {
92 // Add ourselves to nwait to disable "easy case" in semrelease.
93 atomic.Xadd(&root.nwait, 1)
94 // Check cansemacquire to avoid missed wakeup.
95 if cansemacquire(addr) {
96 atomic.Xadd(&root.nwait, -1)
100 // Any semrelease after the cansemacquire knows we're waiting
101 // (we set nwait above), so go to sleep.
103 goparkunlock(&root.lock, "semacquire", traceEvGoBlockSync, 4)
104 if cansemacquire(addr) {
108 if s.releasetime > 0 {
109 blockevent(int64(s.releasetime)-t0, 3)
114 func semrelease(addr *uint32) {
115 root := semroot(addr)
118 // Easy case: no waiters?
119 // This check must happen after the xadd, to avoid a missed wakeup
120 // (see loop in semacquire).
121 if atomic.Load(&root.nwait) == 0 {
125 // Harder case: search for a waiter and wake it.
127 if atomic.Load(&root.nwait) == 0 {
128 // The count is already consumed by another goroutine,
129 // so no need to wake up another goroutine.
134 for ; s != nil; s = s.next {
135 if s.elem == unsafe.Pointer(addr) {
136 atomic.Xadd(&root.nwait, -1)
143 if s.releasetime != 0 {
144 s.releasetime = cputicks()
150 func semroot(addr *uint32) *semaRoot {
151 return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root
154 func cansemacquire(addr *uint32) bool {
156 v := atomic.Load(addr)
160 if atomic.Cas(addr, v, v-1) {
166 func (root *semaRoot) queue(addr *uint32, s *sudog) {
168 s.elem = unsafe.Pointer(addr)
171 if root.tail != nil {
179 func (root *semaRoot) dequeue(s *sudog) {
195 // Synchronous semaphore for sync.Cond.
196 type syncSema struct {
202 // syncsemacquire waits for a pairing syncsemrelease on the same semaphore s.
203 //go:linkname syncsemacquire sync.runtime_Syncsemacquire
204 func syncsemacquire(s *syncSema) {
206 if s.head != nil && s.head.nrelease > 0 {
207 // Have pending release, consume it.
210 if s.head.nrelease == 0 {
230 if blockprofilerate > 0 {
240 goparkunlock(&s.lock, "semacquire", traceEvGoBlockCond, 3)
242 blockevent(int64(w.releasetime)-t0, 2)
248 // syncsemrelease waits for n pairing syncsemacquire on the same semaphore s.
249 //go:linkname syncsemrelease sync.runtime_Syncsemrelease
250 func syncsemrelease(s *syncSema, n uint32) {
252 for n > 0 && s.head != nil && s.head.nrelease < 0 {
253 // Have pending acquire, satisfy it.
259 if wake.releasetime != 0 {
260 wake.releasetime = cputicks()
270 w.nrelease = int32(n)
279 goparkunlock(&s.lock, "semarelease", traceEvGoBlockCond, 3)
286 //go:linkname syncsemcheck sync.runtime_Syncsemcheck
287 func syncsemcheck(sz uintptr) {
288 if sz != unsafe.Sizeof(syncSema{}) {
289 print("runtime: bad syncSema size - sync=", sz, " runtime=", unsafe.Sizeof(syncSema{}), "\n")
290 throw("bad syncSema size")