mirror of
https://github.com/golang/go
synced 2024-10-04 22:21:22 -06:00
ee6e1a3ff7
benchmark old ns/op new ns/op delta BenchmarkWaitGroupUncontended 93.50 33.60 -64.06% BenchmarkWaitGroupUncontended-2 44.30 16.90 -61.85% BenchmarkWaitGroupUncontended-4 21.80 8.47 -61.15% BenchmarkWaitGroupUncontended-8 12.10 4.86 -59.83% BenchmarkWaitGroupUncontended-16 7.38 3.35 -54.61% BenchmarkWaitGroupAddDone 58.40 33.70 -42.29% BenchmarkWaitGroupAddDone-2 293.00 85.80 -70.72% BenchmarkWaitGroupAddDone-4 243.00 51.10 -78.97% BenchmarkWaitGroupAddDone-8 236.00 52.20 -77.88% BenchmarkWaitGroupAddDone-16 215.00 43.30 -79.86% BenchmarkWaitGroupAddDoneWork 826.00 794.00 -3.87% BenchmarkWaitGroupAddDoneWork-2 450.00 424.00 -5.78% BenchmarkWaitGroupAddDoneWork-4 277.00 220.00 -20.58% BenchmarkWaitGroupAddDoneWork-8 440.00 116.00 -73.64% BenchmarkWaitGroupAddDoneWork-16 569.00 66.50 -88.31% BenchmarkWaitGroupWait 29.00 8.04 -72.28% BenchmarkWaitGroupWait-2 74.10 4.15 -94.40% BenchmarkWaitGroupWait-4 117.00 2.30 -98.03% BenchmarkWaitGroupWait-8 111.00 1.31 -98.82% BenchmarkWaitGroupWait-16 104.00 1.27 -98.78% BenchmarkWaitGroupWaitWork 802.00 792.00 -1.25% BenchmarkWaitGroupWaitWork-2 411.00 401.00 -2.43% BenchmarkWaitGroupWaitWork-4 210.00 199.00 -5.24% BenchmarkWaitGroupWaitWork-8 206.00 105.00 -49.03% BenchmarkWaitGroupWaitWork-16 334.00 54.40 -83.71% R=rsc CC=golang-dev https://golang.org/cl/4672050
98 lines
2.5 KiB
Go
98 lines
2.5 KiB
Go
// 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
|
|
|
|
import (
|
|
"runtime"
|
|
"sync/atomic"
|
|
)
|
|
|
|
// A WaitGroup waits for a collection of goroutines to finish.
|
|
// The main goroutine calls Add to set the number of
|
|
// goroutines to wait for. Then each of the goroutines
|
|
// runs and calls Done when finished. At the same time,
|
|
// Wait can be used to block until all goroutines have finished.
|
|
//
|
|
// For example:
|
|
//
|
|
// for i := 0; i < n; i++ {
|
|
// if !condition(i) {
|
|
// continue
|
|
// }
|
|
// wg.Add(1)
|
|
// go func() {
|
|
// // Do something.
|
|
// wg.Done()
|
|
// }()
|
|
// }
|
|
// wg.Wait()
|
|
//
|
|
type WaitGroup struct {
|
|
m Mutex
|
|
counter int32
|
|
waiters int32
|
|
sema *uint32
|
|
}
|
|
|
|
// WaitGroup creates a new semaphore each time the old semaphore
|
|
// is released. This is to avoid the following race:
|
|
//
|
|
// G1: Add(1)
|
|
// G1: go G2()
|
|
// G1: Wait() // Context switch after Unlock() and before Semacquire().
|
|
// G2: Done() // Release semaphore: sema == 1, waiters == 0. G1 doesn't run yet.
|
|
// G3: Wait() // Finds counter == 0, waiters == 0, doesn't block.
|
|
// G3: Add(1) // Makes counter == 1, waiters == 0.
|
|
// G3: go G4()
|
|
// G3: Wait() // G1 still hasn't run, G3 finds sema == 1, unblocked! Bug.
|
|
|
|
// Add adds delta, which may be negative, to the WaitGroup counter.
|
|
// If the counter becomes zero, all goroutines blocked on Wait() are released.
|
|
func (wg *WaitGroup) Add(delta int) {
|
|
v := atomic.AddInt32(&wg.counter, int32(delta))
|
|
if v < 0 {
|
|
panic("sync: negative WaitGroup count")
|
|
}
|
|
if v > 0 || atomic.LoadInt32(&wg.waiters) == 0 {
|
|
return
|
|
}
|
|
wg.m.Lock()
|
|
for i := int32(0); i < wg.waiters; i++ {
|
|
runtime.Semrelease(wg.sema)
|
|
}
|
|
wg.waiters = 0
|
|
wg.sema = nil
|
|
wg.m.Unlock()
|
|
}
|
|
|
|
// Done decrements the WaitGroup counter.
|
|
func (wg *WaitGroup) Done() {
|
|
wg.Add(-1)
|
|
}
|
|
|
|
// Wait blocks until the WaitGroup counter is zero.
|
|
func (wg *WaitGroup) Wait() {
|
|
if atomic.LoadInt32(&wg.counter) == 0 {
|
|
return
|
|
}
|
|
wg.m.Lock()
|
|
atomic.AddInt32(&wg.waiters, 1)
|
|
// This code is racing with the unlocked path in Add above.
|
|
// The code above modifies counter and then reads waiters.
|
|
// We must modify waiters and then read counter (the opposite order)
|
|
// to avoid missing an Add.
|
|
if atomic.LoadInt32(&wg.counter) == 0 {
|
|
atomic.AddInt32(&wg.waiters, -1)
|
|
wg.m.Unlock()
|
|
return
|
|
}
|
|
if wg.sema == nil {
|
|
wg.sema = new(uint32)
|
|
}
|
|
s := wg.sema
|
|
wg.m.Unlock()
|
|
runtime.Semacquire(s)
|
|
}
|