Source file src/pkg/sync/cond.go
1 // Copyright 2011 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 package sync 6 7 // Cond implements a condition variable, a rendezvous point 8 // for goroutines waiting for or announcing the occurrence 9 // of an event. 10 // 11 // Each Cond has an associated Locker L (often a *Mutex or *RWMutex), 12 // which must be held when changing the condition and 13 // when calling the Wait method. 14 type Cond struct { 15 L Locker // held while observing or changing the condition 16 m Mutex // held to avoid internal races 17 18 // We must be careful to make sure that when Signal 19 // releases a semaphore, the corresponding acquire is 20 // executed by a goroutine that was already waiting at 21 // the time of the call to Signal, not one that arrived later. 22 // To ensure this, we segment waiting goroutines into 23 // generations punctuated by calls to Signal. Each call to 24 // Signal begins another generation if there are no goroutines 25 // left in older generations for it to wake. Because of this 26 // optimization (only begin another generation if there 27 // are no older goroutines left), we only need to keep track 28 // of the two most recent generations, which we call old 29 // and new. 30 oldWaiters int // number of waiters in old generation... 31 oldSema *uint32 // ... waiting on this semaphore 32 33 newWaiters int // number of waiters in new generation... 34 newSema *uint32 // ... waiting on this semaphore 35 } 36 37 // NewCond returns a new Cond with Locker l. 38 func NewCond(l Locker) *Cond { 39 return &Cond{L: l} 40 } 41 42 // Wait atomically unlocks c.L and suspends execution 43 // of the calling goroutine. After later resuming execution, 44 // Wait locks c.L before returning. Unlike in other systems, 45 // Wait cannot return unless awoken by Broadcast or Signal. 46 // 47 // Because c.L is not locked when Wait first resumes, the caller 48 // typically cannot assume that the condition is true when 49 // Wait returns. Instead, the caller should Wait in a loop: 50 // 51 // c.L.Lock() 52 // for !condition() { 53 // c.Wait() 54 // } 55 // ... make use of condition ... 56 // c.L.Unlock() 57 // 58 func (c *Cond) Wait() { 59 c.m.Lock() 60 if c.newSema == nil { 61 c.newSema = new(uint32) 62 } 63 s := c.newSema 64 c.newWaiters++ 65 c.m.Unlock() 66 c.L.Unlock() 67 runtime_Semacquire(s) 68 c.L.Lock() 69 } 70 71 // Signal wakes one goroutine waiting on c, if there is any. 72 // 73 // It is allowed but not required for the caller to hold c.L 74 // during the call. 75 func (c *Cond) Signal() { 76 c.m.Lock() 77 if c.oldWaiters == 0 && c.newWaiters > 0 { 78 // Retire old generation; rename new to old. 79 c.oldWaiters = c.newWaiters 80 c.oldSema = c.newSema 81 c.newWaiters = 0 82 c.newSema = nil 83 } 84 if c.oldWaiters > 0 { 85 c.oldWaiters-- 86 runtime_Semrelease(c.oldSema) 87 } 88 c.m.Unlock() 89 } 90 91 // Broadcast wakes all goroutines waiting on c. 92 // 93 // It is allowed but not required for the caller to hold c.L 94 // during the call. 95 func (c *Cond) Broadcast() { 96 c.m.Lock() 97 // Wake both generations. 98 if c.oldWaiters > 0 { 99 for i := 0; i < c.oldWaiters; i++ { 100 runtime_Semrelease(c.oldSema) 101 } 102 c.oldWaiters = 0 103 } 104 if c.newWaiters > 0 { 105 for i := 0; i < c.newWaiters; i++ { 106 runtime_Semrelease(c.newSema) 107 } 108 c.newWaiters = 0 109 c.newSema = nil 110 } 111 c.m.Unlock() 112 }