Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(12)

Side by Side Diff: src/pkg/sync/cond.go

Issue 11573043: sync: faster Cond (Closed)
Patch Set: diff -r ebf9b6a68289 https://dvyukov%40google.com@code.google.com/p/go/ Created 10 years, 7 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pkg/runtime/sema.goc ('k') | src/pkg/sync/cond_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2011 The Go Authors. All rights reserved. 1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package sync 5 package sync
6 6
7 import (
8 "sync/atomic"
9 "unsafe"
10 )
11
7 // Cond implements a condition variable, a rendezvous point 12 // Cond implements a condition variable, a rendezvous point
8 // for goroutines waiting for or announcing the occurrence 13 // for goroutines waiting for or announcing the occurrence
9 // of an event. 14 // of an event.
10 // 15 //
11 // Each Cond has an associated Locker L (often a *Mutex or *RWMutex), 16 // Each Cond has an associated Locker L (often a *Mutex or *RWMutex),
12 // which must be held when changing the condition and 17 // which must be held when changing the condition and
13 // when calling the Wait method. 18 // when calling the Wait method.
19 //
20 // A Cond can be created as part of other structures.
21 // A Cond must not be copied after first use.
14 type Cond struct { 22 type Cond struct {
15 » L Locker // held while observing or changing the condition 23 » // L is held while observing or changing the condition
16 » m Mutex // held to avoid internal races 24 » L Locker
17 25
18 » // We must be careful to make sure that when Signal 26 » sema syncSema
19 » // releases a semaphore, the corresponding acquire is 27 » waiters uint32 // number of waiters
20 » // executed by a goroutine that was already waiting at 28 » checker copyChecker
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 } 29 }
36 30
37 // NewCond returns a new Cond with Locker l. 31 // NewCond returns a new Cond with Locker l.
38 func NewCond(l Locker) *Cond { 32 func NewCond(l Locker) *Cond {
39 return &Cond{L: l} 33 return &Cond{L: l}
40 } 34 }
41 35
42 // Wait atomically unlocks c.L and suspends execution 36 // Wait atomically unlocks c.L and suspends execution
43 // of the calling goroutine. After later resuming execution, 37 // of the calling goroutine. After later resuming execution,
44 // Wait locks c.L before returning. Unlike in other systems, 38 // Wait locks c.L before returning. Unlike in other systems,
45 // Wait cannot return unless awoken by Broadcast or Signal. 39 // Wait cannot return unless awoken by Broadcast or Signal.
46 // 40 //
47 // Because c.L is not locked when Wait first resumes, the caller 41 // Because c.L is not locked when Wait first resumes, the caller
48 // typically cannot assume that the condition is true when 42 // typically cannot assume that the condition is true when
49 // Wait returns. Instead, the caller should Wait in a loop: 43 // Wait returns. Instead, the caller should Wait in a loop:
50 // 44 //
51 // c.L.Lock() 45 // c.L.Lock()
52 // for !condition() { 46 // for !condition() {
53 // c.Wait() 47 // c.Wait()
54 // } 48 // }
55 // ... make use of condition ... 49 // ... make use of condition ...
56 // c.L.Unlock() 50 // c.L.Unlock()
57 // 51 //
58 func (c *Cond) Wait() { 52 func (c *Cond) Wait() {
53 c.checker.check()
59 if raceenabled { 54 if raceenabled {
60 _ = c.m.state
61 raceDisable() 55 raceDisable()
62 } 56 }
63 » c.m.Lock() 57 » atomic.AddUint32(&c.waiters, 1)
64 » if c.newSema == nil {
65 » » c.newSema = new(uint32)
66 » }
67 » s := c.newSema
68 » c.newWaiters++
69 » c.m.Unlock()
70 if raceenabled { 58 if raceenabled {
71 raceEnable() 59 raceEnable()
72 } 60 }
73 c.L.Unlock() 61 c.L.Unlock()
74 » runtime_Semacquire(s) 62 » runtime_Syncsemacquire(&c.sema)
75 c.L.Lock() 63 c.L.Lock()
76 } 64 }
77 65
78 // Signal wakes one goroutine waiting on c, if there is any. 66 // Signal wakes one goroutine waiting on c, if there is any.
79 // 67 //
80 // It is allowed but not required for the caller to hold c.L 68 // It is allowed but not required for the caller to hold c.L
81 // during the call. 69 // during the call.
82 func (c *Cond) Signal() { 70 func (c *Cond) Signal() {
83 » if raceenabled { 71 » c.signalImpl(false)
84 » » _ = c.m.state
85 » » raceDisable()
86 » }
87 » c.m.Lock()
88 » if c.oldWaiters == 0 && c.newWaiters > 0 {
89 » » // Retire old generation; rename new to old.
90 » » c.oldWaiters = c.newWaiters
91 » » c.oldSema = c.newSema
92 » » c.newWaiters = 0
93 » » c.newSema = nil
94 » }
95 » if c.oldWaiters > 0 {
96 » » c.oldWaiters--
97 » » runtime_Semrelease(c.oldSema)
98 » }
99 » c.m.Unlock()
100 » if raceenabled {
101 » » raceEnable()
102 » }
103 } 72 }
104 73
105 // Broadcast wakes all goroutines waiting on c. 74 // Broadcast wakes all goroutines waiting on c.
106 // 75 //
107 // It is allowed but not required for the caller to hold c.L 76 // It is allowed but not required for the caller to hold c.L
108 // during the call. 77 // during the call.
109 func (c *Cond) Broadcast() { 78 func (c *Cond) Broadcast() {
79 c.signalImpl(true)
80 }
81
82 func (c *Cond) signalImpl(all bool) {
83 c.checker.check()
110 if raceenabled { 84 if raceenabled {
111 _ = c.m.state
112 raceDisable() 85 raceDisable()
113 } 86 }
114 » c.m.Lock() 87 » for {
115 » // Wake both generations. 88 » » old := atomic.LoadUint32(&c.waiters)
116 » if c.oldWaiters > 0 { 89 » » if old == 0 {
117 » » for i := 0; i < c.oldWaiters; i++ { 90 » » » if raceenabled {
118 » » » runtime_Semrelease(c.oldSema) 91 » » » » raceEnable()
92 » » » }
93 » » » return
119 } 94 }
120 » » c.oldWaiters = 0 95 » » new := old - 1
121 » } 96 » » if all {
122 » if c.newWaiters > 0 { 97 » » » new = 0
123 » » for i := 0; i < c.newWaiters; i++ {
124 » » » runtime_Semrelease(c.newSema)
125 } 98 }
126 » » c.newWaiters = 0 99 » » if atomic.CompareAndSwapUint32(&c.waiters, old, new) {
127 » » c.newSema = nil 100 » » » if raceenabled {
128 » } 101 » » » » raceEnable()
129 » c.m.Unlock() 102 » » » }
130 » if raceenabled { 103 » » » runtime_Syncsemrelease(&c.sema, old-new)
131 » » raceEnable() 104 » » » return
105 » » }
132 } 106 }
133 } 107 }
108
109 // copyChecker holds back pointer to itself to detect object copying.
110 type copyChecker uintptr
111
112 func (c *copyChecker) check() {
113 if uintptr(*c) != uintptr(unsafe.Pointer(c)) &&
114 !atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.P ointer(c))) &&
115 uintptr(*c) != uintptr(unsafe.Pointer(c)) {
116 panic("sync.Cond is copied")
117 }
118 }
OLDNEW
« no previous file with comments | « src/pkg/runtime/sema.goc ('k') | src/pkg/sync/cond_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b