github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/src/runtime/sema.go (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 runtime 21 22 import "unsafe" 23 24 // Asynchronous semaphore for sync.Mutex. 25 26 type semaRoot struct { 27 lock mutex 28 head *sudog 29 tail *sudog 30 nwait uint32 // Number of waiters. Read w/o the lock. 31 } 32 33 // Prime to not correlate with any user patterns. 34 const semTabSize = 251 35 36 var semtable [semTabSize]struct { 37 root semaRoot 38 pad [_CacheLineSize - unsafe.Sizeof(semaRoot{})]byte 39 } 40 41 // Called from sync/net packages. 42 func asyncsemacquire(addr *uint32) { 43 semacquire(addr, true) 44 } 45 46 func asyncsemrelease(addr *uint32) { 47 semrelease(addr) 48 } 49 50 // Called from runtime. 51 func semacquire(addr *uint32, profile bool) { 52 gp := getg() 53 if gp != gp.m.curg { 54 gothrow("semacquire not on the G stack") 55 } 56 57 // Easy case. 58 if cansemacquire(addr) { 59 return 60 } 61 62 // Harder case: 63 // increment waiter count 64 // try cansemacquire one more time, return if succeeded 65 // enqueue itself as a waiter 66 // sleep 67 // (waiter descriptor is dequeued by signaler) 68 s := acquireSudog() 69 root := semroot(addr) 70 t0 := int64(0) 71 s.releasetime = 0 72 if profile && blockprofilerate > 0 { 73 t0 = cputicks() 74 s.releasetime = -1 75 } 76 for { 77 lock(&root.lock) 78 // Add ourselves to nwait to disable "easy case" in semrelease. 79 xadd(&root.nwait, 1) 80 // Check cansemacquire to avoid missed wakeup. 81 if cansemacquire(addr) { 82 xadd(&root.nwait, -1) 83 unlock(&root.lock) 84 break 85 } 86 // Any semrelease after the cansemacquire knows we're waiting 87 // (we set nwait above), so go to sleep. 88 root.queue(addr, s) 89 goparkunlock(&root.lock, "semacquire") 90 if cansemacquire(addr) { 91 break 92 } 93 } 94 if s.releasetime > 0 { 95 blockevent(int64(s.releasetime)-t0, 3) 96 } 97 releaseSudog(s) 98 } 99 100 func semrelease(addr *uint32) { 101 root := semroot(addr) 102 xadd(addr, 1) 103 104 // Easy case: no waiters? 105 // This check must happen after the xadd, to avoid a missed wakeup 106 // (see loop in semacquire). 107 if atomicload(&root.nwait) == 0 { 108 return 109 } 110 111 // Harder case: search for a waiter and wake it. 112 lock(&root.lock) 113 if atomicload(&root.nwait) == 0 { 114 // The count is already consumed by another goroutine, 115 // so no need to wake up another goroutine. 116 unlock(&root.lock) 117 return 118 } 119 s := root.head 120 for ; s != nil; s = s.next { 121 if s.elem == unsafe.Pointer(addr) { 122 xadd(&root.nwait, -1) 123 root.dequeue(s) 124 break 125 } 126 } 127 unlock(&root.lock) 128 if s != nil { 129 if s.releasetime != 0 { 130 s.releasetime = cputicks() 131 } 132 goready(s.g) 133 } 134 } 135 136 func semroot(addr *uint32) *semaRoot { 137 return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root 138 } 139 140 func cansemacquire(addr *uint32) bool { 141 for { 142 v := atomicload(addr) 143 if v == 0 { 144 return false 145 } 146 if cas(addr, v, v-1) { 147 return true 148 } 149 } 150 } 151 152 func (root *semaRoot) queue(addr *uint32, s *sudog) { 153 s.g = getg() 154 s.elem = unsafe.Pointer(addr) 155 s.next = nil 156 s.prev = root.tail 157 if root.tail != nil { 158 root.tail.next = s 159 } else { 160 root.head = s 161 } 162 root.tail = s 163 } 164 165 func (root *semaRoot) dequeue(s *sudog) { 166 if s.next != nil { 167 s.next.prev = s.prev 168 } else { 169 root.tail = s.prev 170 } 171 if s.prev != nil { 172 s.prev.next = s.next 173 } else { 174 root.head = s.next 175 } 176 s.elem = nil 177 s.next = nil 178 s.prev = nil 179 } 180 181 // Synchronous semaphore for sync.Cond. 182 type syncSema struct { 183 lock mutex 184 head *sudog 185 tail *sudog 186 } 187 188 // Syncsemacquire waits for a pairing syncsemrelease on the same semaphore s. 189 func syncsemacquire(s *syncSema) { 190 lock(&s.lock) 191 if s.head != nil && s.head.nrelease > 0 { 192 // Have pending release, consume it. 193 var wake *sudog 194 s.head.nrelease-- 195 if s.head.nrelease == 0 { 196 wake = s.head 197 s.head = wake.next 198 if s.head == nil { 199 s.tail = nil 200 } 201 } 202 unlock(&s.lock) 203 if wake != nil { 204 wake.next = nil 205 goready(wake.g) 206 } 207 } else { 208 // Enqueue itself. 209 w := acquireSudog() 210 w.g = getg() 211 w.nrelease = -1 212 w.next = nil 213 w.releasetime = 0 214 t0 := int64(0) 215 if blockprofilerate > 0 { 216 t0 = cputicks() 217 w.releasetime = -1 218 } 219 if s.tail == nil { 220 s.head = w 221 } else { 222 s.tail.next = w 223 } 224 s.tail = w 225 goparkunlock(&s.lock, "semacquire") 226 if t0 != 0 { 227 blockevent(int64(w.releasetime)-t0, 2) 228 } 229 releaseSudog(w) 230 } 231 } 232 233 // Syncsemrelease waits for n pairing syncsemacquire on the same semaphore s. 234 func syncsemrelease(s *syncSema, n uint32) { 235 lock(&s.lock) 236 for n > 0 && s.head != nil && s.head.nrelease < 0 { 237 // Have pending acquire, satisfy it. 238 wake := s.head 239 s.head = wake.next 240 if s.head == nil { 241 s.tail = nil 242 } 243 if wake.releasetime != 0 { 244 wake.releasetime = cputicks() 245 } 246 wake.next = nil 247 goready(wake.g) 248 n-- 249 } 250 if n > 0 { 251 // enqueue itself 252 w := acquireSudog() 253 w.g = getg() 254 w.nrelease = int32(n) 255 w.next = nil 256 w.releasetime = 0 257 if s.tail == nil { 258 s.head = w 259 } else { 260 s.tail.next = w 261 } 262 s.tail = w 263 goparkunlock(&s.lock, "semarelease") 264 releaseSudog(w) 265 } else { 266 unlock(&s.lock) 267 } 268 } 269 270 func syncsemcheck(sz uintptr) { 271 if sz != unsafe.Sizeof(syncSema{}) { 272 print("runtime: bad syncSema size - sync=", sz, " runtime=", unsafe.Sizeof(syncSema{}), "\n") 273 gothrow("bad syncSema size") 274 } 275 }