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 }