mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-12-01 15:54:17 +08:00
f8d9fa9e80
This upgrades all of libgo other than the runtime package to the Go 1.4 release. In Go 1.4 much of the runtime was rewritten into Go. Merging that code will take more time and will not change the API, so I'm putting it off for now. There are a few runtime changes anyhow, to accomodate other packages that rely on minor modifications to the runtime support. The compiler changes slightly to add a one-bit flag to each type descriptor kind that is stored directly in an interface, which for gccgo is currently only pointer types. Another one-bit flag (gcprog) is reserved because it is used by the gc compiler, but gccgo does not currently use it. There is another error check in the compiler since I ran across it during testing. gotools/: * Makefile.am (go_cmd_go_files): Sort entries. Add generate.go. * Makefile.in: Rebuild. From-SVN: r219627
138 lines
3.9 KiB
Go
138 lines
3.9 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 (
|
|
"sync/atomic"
|
|
"unsafe"
|
|
)
|
|
|
|
// 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.
|
|
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.
|
|
// If the counter goes negative, Add panics.
|
|
//
|
|
// Note that calls with a positive delta that occur when the counter is zero
|
|
// must happen before a Wait. Calls with a negative delta, or calls with a
|
|
// positive delta that start when the counter is greater than zero, may happen
|
|
// at any time.
|
|
// Typically this means the calls to Add should execute before the statement
|
|
// creating the goroutine or other event to be waited for.
|
|
// See the WaitGroup example.
|
|
func (wg *WaitGroup) Add(delta int) {
|
|
if raceenabled {
|
|
_ = wg.m.state // trigger nil deref early
|
|
if delta < 0 {
|
|
// Synchronize decrements with Wait.
|
|
raceReleaseMerge(unsafe.Pointer(wg))
|
|
}
|
|
raceDisable()
|
|
defer raceEnable()
|
|
}
|
|
v := atomic.AddInt32(&wg.counter, int32(delta))
|
|
if raceenabled {
|
|
if delta > 0 && v == int32(delta) {
|
|
// The first increment must be synchronized with Wait.
|
|
// Need to model this as a read, because there can be
|
|
// several concurrent wg.counter transitions from 0.
|
|
raceRead(unsafe.Pointer(&wg.sema))
|
|
}
|
|
}
|
|
if v < 0 {
|
|
panic("sync: negative WaitGroup counter")
|
|
}
|
|
if v > 0 || atomic.LoadInt32(&wg.waiters) == 0 {
|
|
return
|
|
}
|
|
wg.m.Lock()
|
|
if atomic.LoadInt32(&wg.counter) == 0 {
|
|
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 raceenabled {
|
|
_ = wg.m.state // trigger nil deref early
|
|
raceDisable()
|
|
}
|
|
if atomic.LoadInt32(&wg.counter) == 0 {
|
|
if raceenabled {
|
|
raceEnable()
|
|
raceAcquire(unsafe.Pointer(wg))
|
|
}
|
|
return
|
|
}
|
|
wg.m.Lock()
|
|
w := 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)
|
|
if raceenabled {
|
|
raceEnable()
|
|
raceAcquire(unsafe.Pointer(wg))
|
|
raceDisable()
|
|
}
|
|
wg.m.Unlock()
|
|
if raceenabled {
|
|
raceEnable()
|
|
}
|
|
return
|
|
}
|
|
if raceenabled && w == 1 {
|
|
// Wait must be synchronized with the first Add.
|
|
// Need to model this is as a write to race with the read in Add.
|
|
// As a consequence, can do the write only for the first waiter,
|
|
// otherwise concurrent Waits will race with each other.
|
|
raceWrite(unsafe.Pointer(&wg.sema))
|
|
}
|
|
if wg.sema == nil {
|
|
wg.sema = new(uint32)
|
|
}
|
|
s := wg.sema
|
|
wg.m.Unlock()
|
|
runtime_Semacquire(s)
|
|
if raceenabled {
|
|
raceEnable()
|
|
raceAcquire(unsafe.Pointer(wg))
|
|
}
|
|
}
|