| // 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" |
| |
| // 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 int |
| waiters int |
| 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) { |
| wg.m.Lock() |
| if delta < -wg.counter { |
| wg.m.Unlock() |
| panic("sync: negative WaitGroup count") |
| } |
| wg.counter += delta |
| if wg.counter == 0 && wg.waiters > 0 { |
| for i := 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() { |
| wg.m.Lock() |
| if wg.counter == 0 { |
| wg.m.Unlock() |
| return |
| } |
| wg.waiters++ |
| if wg.sema == nil { |
| wg.sema = new(uint32) |
| } |
| s := wg.sema |
| wg.m.Unlock() |
| runtime.Semacquire(s) |
| } |