123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- // 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
- import "unsafe"
- // Asynchronous semaphore for sync.Mutex.
- type semaRoot struct {
- lock mutex
- head *sudog
- tail *sudog
- nwait uint32 // Number of waiters. Read w/o the lock.
- }
- // Prime to not correlate with any user patterns.
- const semTabSize = 251
- var semtable [semTabSize]struct {
- root semaRoot
- pad [_CacheLineSize - unsafe.Sizeof(semaRoot{})]byte
- }
- // Called from sync/net packages.
- func asyncsemacquire(addr *uint32) {
- semacquire(addr, true)
- }
- func asyncsemrelease(addr *uint32) {
- semrelease(addr)
- }
- // Called from runtime.
- func semacquire(addr *uint32, profile bool) {
- gp := getg()
- if gp != gp.m.curg {
- gothrow("semacquire not on the G stack")
- }
- // 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)
- s := acquireSudog()
- root := semroot(addr)
- t0 := int64(0)
- s.releasetime = 0
- if profile && blockprofilerate > 0 {
- t0 = cputicks()
- s.releasetime = -1
- }
- for {
- lock(&root.lock)
- // Add ourselves to nwait to disable "easy case" in semrelease.
- xadd(&root.nwait, 1)
- // Check cansemacquire to avoid missed wakeup.
- if cansemacquire(addr) {
- xadd(&root.nwait, -1)
- unlock(&root.lock)
- break
- }
- // Any semrelease after the cansemacquire knows we're waiting
- // (we set nwait above), so go to sleep.
- root.queue(addr, s)
- goparkunlock(&root.lock, "semacquire")
- if cansemacquire(addr) {
- break
- }
- }
- if s.releasetime > 0 {
- blockevent(int64(s.releasetime)-t0, 3)
- }
- releaseSudog(s)
- }
- func semrelease(addr *uint32) {
- root := semroot(addr)
- xadd(addr, 1)
- // Easy case: no waiters?
- // This check must happen after the xadd, to avoid a missed wakeup
- // (see loop in semacquire).
- if atomicload(&root.nwait) == 0 {
- return
- }
- // Harder case: search for a waiter and wake it.
- lock(&root.lock)
- if atomicload(&root.nwait) == 0 {
- // The count is already consumed by another goroutine,
- // so no need to wake up another goroutine.
- unlock(&root.lock)
- return
- }
- s := root.head
- for ; s != nil; s = s.next {
- if s.elem == unsafe.Pointer(addr) {
- xadd(&root.nwait, -1)
- root.dequeue(s)
- break
- }
- }
- unlock(&root.lock)
- if s != nil {
- if s.releasetime != 0 {
- s.releasetime = cputicks()
- }
- goready(s.g)
- }
- }
- func semroot(addr *uint32) *semaRoot {
- return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root
- }
- func cansemacquire(addr *uint32) bool {
- for {
- v := atomicload(addr)
- if v == 0 {
- return false
- }
- if cas(addr, v, v-1) {
- return true
- }
- }
- }
- func (root *semaRoot) queue(addr *uint32, s *sudog) {
- s.g = getg()
- s.elem = unsafe.Pointer(addr)
- s.next = nil
- s.prev = root.tail
- if root.tail != nil {
- root.tail.next = s
- } else {
- root.head = s
- }
- root.tail = s
- }
- func (root *semaRoot) dequeue(s *sudog) {
- if s.next != nil {
- s.next.prev = s.prev
- } else {
- root.tail = s.prev
- }
- if s.prev != nil {
- s.prev.next = s.next
- } else {
- root.head = s.next
- }
- s.elem = nil
- s.next = nil
- s.prev = nil
- }
- // Synchronous semaphore for sync.Cond.
- type syncSema struct {
- lock mutex
- head *sudog
- tail *sudog
- }
- // Syncsemacquire waits for a pairing syncsemrelease on the same semaphore s.
- func syncsemacquire(s *syncSema) {
- lock(&s.lock)
- if s.head != nil && s.head.nrelease > 0 {
- // Have pending release, consume it.
- var wake *sudog
- s.head.nrelease--
- if s.head.nrelease == 0 {
- wake = s.head
- s.head = wake.next
- if s.head == nil {
- s.tail = nil
- }
- }
- unlock(&s.lock)
- if wake != nil {
- wake.next = nil
- goready(wake.g)
- }
- } else {
- // Enqueue itself.
- w := acquireSudog()
- w.g = getg()
- w.nrelease = -1
- w.next = nil
- w.releasetime = 0
- t0 := int64(0)
- if blockprofilerate > 0 {
- t0 = cputicks()
- w.releasetime = -1
- }
- if s.tail == nil {
- s.head = w
- } else {
- s.tail.next = w
- }
- s.tail = w
- goparkunlock(&s.lock, "semacquire")
- if t0 != 0 {
- blockevent(int64(w.releasetime)-t0, 2)
- }
- releaseSudog(w)
- }
- }
- // Syncsemrelease waits for n pairing syncsemacquire on the same semaphore s.
- func syncsemrelease(s *syncSema, n uint32) {
- lock(&s.lock)
- for n > 0 && s.head != nil && s.head.nrelease < 0 {
- // Have pending acquire, satisfy it.
- wake := s.head
- s.head = wake.next
- if s.head == nil {
- s.tail = nil
- }
- if wake.releasetime != 0 {
- wake.releasetime = cputicks()
- }
- wake.next = nil
- goready(wake.g)
- n--
- }
- if n > 0 {
- // enqueue itself
- w := acquireSudog()
- w.g = getg()
- w.nrelease = int32(n)
- w.next = nil
- w.releasetime = 0
- if s.tail == nil {
- s.head = w
- } else {
- s.tail.next = w
- }
- s.tail = w
- goparkunlock(&s.lock, "semarelease")
- releaseSudog(w)
- } else {
- unlock(&s.lock)
- }
- }
- func syncsemcheck(sz uintptr) {
- if sz != unsafe.Sizeof(syncSema{}) {
- print("runtime: bad syncSema size - sync=", sz, " runtime=", unsafe.Sizeof(syncSema{}), "\n")
- gothrow("bad syncSema size")
- }
- }
|