diff options
Diffstat (limited to 'libgo/go/sync/cond.go')
-rw-r--r-- | libgo/go/sync/cond.go | 113 |
1 files changed, 49 insertions, 64 deletions
diff --git a/libgo/go/sync/cond.go b/libgo/go/sync/cond.go index 13547a8a11b..9e6bc170f19 100644 --- a/libgo/go/sync/cond.go +++ b/libgo/go/sync/cond.go @@ -4,6 +4,11 @@ package sync +import ( + "sync/atomic" + "unsafe" +) + // Cond implements a condition variable, a rendezvous point // for goroutines waiting for or announcing the occurrence // of an event. @@ -11,27 +16,16 @@ package sync // Each Cond has an associated Locker L (often a *Mutex or *RWMutex), // which must be held when changing the condition and // when calling the Wait method. +// +// A Cond can be created as part of other structures. +// A Cond must not be copied after first use. type Cond struct { - L Locker // held while observing or changing the condition - m Mutex // held to avoid internal races - - // We must be careful to make sure that when Signal - // releases a semaphore, the corresponding acquire is - // executed by a goroutine that was already waiting at - // the time of the call to Signal, not one that arrived later. - // To ensure this, we segment waiting goroutines into - // generations punctuated by calls to Signal. Each call to - // Signal begins another generation if there are no goroutines - // left in older generations for it to wake. Because of this - // optimization (only begin another generation if there - // are no older goroutines left), we only need to keep track - // of the two most recent generations, which we call old - // and new. - oldWaiters int // number of waiters in old generation... - oldSema *uint32 // ... waiting on this semaphore + // L is held while observing or changing the condition + L Locker - newWaiters int // number of waiters in new generation... - newSema *uint32 // ... waiting on this semaphore + sema syncSema + waiters uint32 // number of waiters + checker copyChecker } // NewCond returns a new Cond with Locker l. @@ -56,22 +50,16 @@ func NewCond(l Locker) *Cond { // c.L.Unlock() // func (c *Cond) Wait() { + c.checker.check() if raceenabled { - _ = c.m.state raceDisable() } - c.m.Lock() - if c.newSema == nil { - c.newSema = new(uint32) - } - s := c.newSema - c.newWaiters++ - c.m.Unlock() + atomic.AddUint32(&c.waiters, 1) if raceenabled { raceEnable() } c.L.Unlock() - runtime_Semacquire(s) + runtime_Syncsemacquire(&c.sema) c.L.Lock() } @@ -80,26 +68,7 @@ func (c *Cond) Wait() { // It is allowed but not required for the caller to hold c.L // during the call. func (c *Cond) Signal() { - if raceenabled { - _ = c.m.state - raceDisable() - } - c.m.Lock() - if c.oldWaiters == 0 && c.newWaiters > 0 { - // Retire old generation; rename new to old. - c.oldWaiters = c.newWaiters - c.oldSema = c.newSema - c.newWaiters = 0 - c.newSema = nil - } - if c.oldWaiters > 0 { - c.oldWaiters-- - runtime_Semrelease(c.oldSema) - } - c.m.Unlock() - if raceenabled { - raceEnable() - } + c.signalImpl(false) } // Broadcast wakes all goroutines waiting on c. @@ -107,27 +76,43 @@ func (c *Cond) Signal() { // It is allowed but not required for the caller to hold c.L // during the call. func (c *Cond) Broadcast() { + c.signalImpl(true) +} + +func (c *Cond) signalImpl(all bool) { + c.checker.check() if raceenabled { - _ = c.m.state raceDisable() } - c.m.Lock() - // Wake both generations. - if c.oldWaiters > 0 { - for i := 0; i < c.oldWaiters; i++ { - runtime_Semrelease(c.oldSema) + for { + old := atomic.LoadUint32(&c.waiters) + if old == 0 { + if raceenabled { + raceEnable() + } + return } - c.oldWaiters = 0 - } - if c.newWaiters > 0 { - for i := 0; i < c.newWaiters; i++ { - runtime_Semrelease(c.newSema) + new := old - 1 + if all { + new = 0 + } + if atomic.CompareAndSwapUint32(&c.waiters, old, new) { + if raceenabled { + raceEnable() + } + runtime_Syncsemrelease(&c.sema, old-new) + return } - c.newWaiters = 0 - c.newSema = nil } - c.m.Unlock() - if raceenabled { - raceEnable() +} + +// copyChecker holds back pointer to itself to detect object copying. +type copyChecker uintptr + +func (c *copyChecker) check() { + if uintptr(*c) != uintptr(unsafe.Pointer(c)) && + !atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) && + uintptr(*c) != uintptr(unsafe.Pointer(c)) { + panic("sync.Cond is copied") } } |