summaryrefslogtreecommitdiff
path: root/libgo/go/sync/waitgroup.go
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2011-03-16 23:05:44 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2011-03-16 23:05:44 +0000
commit31c6ec422702226aabab7d082da16663e6c3e72c (patch)
tree44176975832a3faf1626836e70c97d5edd674122 /libgo/go/sync/waitgroup.go
parentb3145af52cfb7c84d62a8e4ceeb165a7369718da (diff)
downloadgcc-31c6ec422702226aabab7d082da16663e6c3e72c.tar.gz
Update to current version of Go library (revision 94d654be2064).
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@171076 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/sync/waitgroup.go')
-rw-r--r--libgo/go/sync/waitgroup.go86
1 files changed, 86 insertions, 0 deletions
diff --git a/libgo/go/sync/waitgroup.go b/libgo/go/sync/waitgroup.go
new file mode 100644
index 00000000000..68e1d509f48
--- /dev/null
+++ b/libgo/go/sync/waitgroup.go
@@ -0,0 +1,86 @@
+// 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)
+}