// Copyright 2011 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. package sync // Cond implements a condition variable, a rendezvous point // for goroutines waiting for or announcing the occurrence // of an event. // // 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. 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 newWaiters int // number of waiters in new generation... newSema *uint32 // ... waiting on this semaphore } // NewCond returns a new Cond with Locker l. func NewCond(l Locker) *Cond { return &Cond{L: l} } // Wait atomically unlocks c.L and suspends execution // of the calling goroutine. After later resuming execution, // Wait locks c.L before returning. Unlike in other systems, // Wait cannot return unless awoken by Broadcast or Signal. // // Because c.L is not locked when Wait first resumes, the caller // typically cannot assume that the condition is true when // Wait returns. Instead, the caller should Wait in a loop: // // c.L.Lock() // for !condition() { // c.Wait() // } // ... make use of condition ... // c.L.Unlock() // func (c *Cond) Wait() { 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() if raceenabled { raceEnable() } c.L.Unlock() runtime_Semacquire(s) c.L.Lock() } // Signal wakes one goroutine waiting on c, if there is any. // // 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() } } // Broadcast wakes all goroutines waiting on c. // // It is allowed but not required for the caller to hold c.L // during the call. func (c *Cond) Broadcast() { 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) } c.oldWaiters = 0 } if c.newWaiters > 0 { for i := 0; i < c.newWaiters; i++ { runtime_Semrelease(c.newSema) } c.newWaiters = 0 c.newSema = nil } c.m.Unlock() if raceenabled { raceEnable() } }