1
0
mirror of https://github.com/golang/go synced 2024-11-13 19:00:25 -07:00

race: sync changes

This is a part of a bigger change that adds data race detection feature:
https://golang.org/cl/6456044

R=rsc, minux.ma
CC=gobot, golang-dev
https://golang.org/cl/6529053
This commit is contained in:
Dmitriy Vyukov 2012-10-07 22:07:03 +04:00
parent 2f6cbc74f1
commit 53390c8fc7
7 changed files with 159 additions and 4 deletions

View File

@ -30,7 +30,7 @@ var pkgDeps = map[string][]string{
"errors": {}, "errors": {},
"io": {"errors", "sync"}, "io": {"errors", "sync"},
"runtime": {"unsafe"}, "runtime": {"unsafe"},
"sync": {"sync/atomic"}, "sync": {"sync/atomic", "unsafe"},
"sync/atomic": {"unsafe"}, "sync/atomic": {"unsafe"},
"unsafe": {}, "unsafe": {},

View File

@ -56,6 +56,9 @@ func NewCond(l Locker) *Cond {
// c.L.Unlock() // c.L.Unlock()
// //
func (c *Cond) Wait() { func (c *Cond) Wait() {
if raceenabled {
raceDisable()
}
c.m.Lock() c.m.Lock()
if c.newSema == nil { if c.newSema == nil {
c.newSema = new(uint32) c.newSema = new(uint32)
@ -63,6 +66,9 @@ func (c *Cond) Wait() {
s := c.newSema s := c.newSema
c.newWaiters++ c.newWaiters++
c.m.Unlock() c.m.Unlock()
if raceenabled {
raceEnable()
}
c.L.Unlock() c.L.Unlock()
runtime_Semacquire(s) runtime_Semacquire(s)
c.L.Lock() c.L.Lock()
@ -73,6 +79,9 @@ func (c *Cond) Wait() {
// It is allowed but not required for the caller to hold c.L // It is allowed but not required for the caller to hold c.L
// during the call. // during the call.
func (c *Cond) Signal() { func (c *Cond) Signal() {
if raceenabled {
raceDisable()
}
c.m.Lock() c.m.Lock()
if c.oldWaiters == 0 && c.newWaiters > 0 { if c.oldWaiters == 0 && c.newWaiters > 0 {
// Retire old generation; rename new to old. // Retire old generation; rename new to old.
@ -86,6 +95,9 @@ func (c *Cond) Signal() {
runtime_Semrelease(c.oldSema) runtime_Semrelease(c.oldSema)
} }
c.m.Unlock() c.m.Unlock()
if raceenabled {
raceEnable()
}
} }
// Broadcast wakes all goroutines waiting on c. // Broadcast wakes all goroutines waiting on c.
@ -93,6 +105,9 @@ func (c *Cond) Signal() {
// It is allowed but not required for the caller to hold c.L // It is allowed but not required for the caller to hold c.L
// during the call. // during the call.
func (c *Cond) Broadcast() { func (c *Cond) Broadcast() {
if raceenabled {
raceDisable()
}
c.m.Lock() c.m.Lock()
// Wake both generations. // Wake both generations.
if c.oldWaiters > 0 { if c.oldWaiters > 0 {
@ -109,4 +124,7 @@ func (c *Cond) Broadcast() {
c.newSema = nil c.newSema = nil
} }
c.m.Unlock() c.m.Unlock()
if raceenabled {
raceEnable()
}
} }

View File

@ -10,7 +10,10 @@
// Values containing the types defined in this package should not be copied. // Values containing the types defined in this package should not be copied.
package sync package sync
import "sync/atomic" import (
"sync/atomic"
"unsafe"
)
// A Mutex is a mutual exclusion lock. // A Mutex is a mutual exclusion lock.
// Mutexes can be created as part of other structures; // Mutexes can be created as part of other structures;
@ -38,6 +41,9 @@ const (
func (m *Mutex) Lock() { func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex. // Fast path: grab unlocked mutex.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if raceenabled {
raceAcquire(unsafe.Pointer(m))
}
return return
} }
@ -61,6 +67,10 @@ func (m *Mutex) Lock() {
awoke = true awoke = true
} }
} }
if raceenabled {
raceAcquire(unsafe.Pointer(m))
}
} }
// Unlock unlocks m. // Unlock unlocks m.
@ -70,6 +80,10 @@ func (m *Mutex) Lock() {
// It is allowed for one goroutine to lock a Mutex and then // It is allowed for one goroutine to lock a Mutex and then
// arrange for another goroutine to unlock it. // arrange for another goroutine to unlock it.
func (m *Mutex) Unlock() { func (m *Mutex) Unlock() {
if raceenabled {
raceRelease(unsafe.Pointer(m))
}
// Fast path: drop lock bit. // Fast path: drop lock bit.
new := atomic.AddInt32(&m.state, -mutexLocked) new := atomic.AddInt32(&m.state, -mutexLocked)
if (new+mutexLocked)&mutexLocked == 0 { if (new+mutexLocked)&mutexLocked == 0 {

34
src/pkg/sync/race.go Normal file
View File

@ -0,0 +1,34 @@
// Copyright 2012 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.
// +build race
package sync
import (
"runtime"
"unsafe"
)
const raceenabled = true
func raceAcquire(addr unsafe.Pointer) {
runtime.RaceAcquire(addr)
}
func raceRelease(addr unsafe.Pointer) {
runtime.RaceRelease(addr)
}
func raceReleaseMerge(addr unsafe.Pointer) {
runtime.RaceReleaseMerge(addr)
}
func raceDisable() {
runtime.RaceDisable()
}
func raceEnable() {
runtime.RaceEnable()
}

28
src/pkg/sync/race0.go Normal file
View File

@ -0,0 +1,28 @@
// Copyright 2012 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.
// +build !race
package sync
import (
"unsafe"
)
const raceenabled = false
func raceAcquire(addr unsafe.Pointer) {
}
func raceRelease(addr unsafe.Pointer) {
}
func raceReleaseMerge(addr unsafe.Pointer) {
}
func raceDisable() {
}
func raceEnable() {
}

View File

@ -4,7 +4,10 @@
package sync package sync
import "sync/atomic" import (
"sync/atomic"
"unsafe"
)
// An RWMutex is a reader/writer mutual exclusion lock. // An RWMutex is a reader/writer mutual exclusion lock.
// The lock can be held by an arbitrary number of readers // The lock can be held by an arbitrary number of readers
@ -24,10 +27,17 @@ const rwmutexMaxReaders = 1 << 30
// RLock locks rw for reading. // RLock locks rw for reading.
func (rw *RWMutex) RLock() { func (rw *RWMutex) RLock() {
if raceenabled {
raceDisable()
}
if atomic.AddInt32(&rw.readerCount, 1) < 0 { if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// A writer is pending, wait for it. // A writer is pending, wait for it.
runtime_Semacquire(&rw.readerSem) runtime_Semacquire(&rw.readerSem)
} }
if raceenabled {
raceEnable()
raceAcquire(unsafe.Pointer(&rw.readerSem))
}
} }
// RUnlock undoes a single RLock call; // RUnlock undoes a single RLock call;
@ -35,6 +45,10 @@ func (rw *RWMutex) RLock() {
// It is a run-time error if rw is not locked for reading // It is a run-time error if rw is not locked for reading
// on entry to RUnlock. // on entry to RUnlock.
func (rw *RWMutex) RUnlock() { func (rw *RWMutex) RUnlock() {
if raceenabled {
raceReleaseMerge(unsafe.Pointer(&rw.writerSem))
raceDisable()
}
if atomic.AddInt32(&rw.readerCount, -1) < 0 { if atomic.AddInt32(&rw.readerCount, -1) < 0 {
// A writer is pending. // A writer is pending.
if atomic.AddInt32(&rw.readerWait, -1) == 0 { if atomic.AddInt32(&rw.readerWait, -1) == 0 {
@ -42,6 +56,9 @@ func (rw *RWMutex) RUnlock() {
runtime_Semrelease(&rw.writerSem) runtime_Semrelease(&rw.writerSem)
} }
} }
if raceenabled {
raceEnable()
}
} }
// Lock locks rw for writing. // Lock locks rw for writing.
@ -51,6 +68,9 @@ func (rw *RWMutex) RUnlock() {
// a blocked Lock call excludes new readers from acquiring // a blocked Lock call excludes new readers from acquiring
// the lock. // the lock.
func (rw *RWMutex) Lock() { func (rw *RWMutex) Lock() {
if raceenabled {
raceDisable()
}
// First, resolve competition with other writers. // First, resolve competition with other writers.
rw.w.Lock() rw.w.Lock()
// Announce to readers there is a pending writer. // Announce to readers there is a pending writer.
@ -59,6 +79,11 @@ func (rw *RWMutex) Lock() {
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_Semacquire(&rw.writerSem) runtime_Semacquire(&rw.writerSem)
} }
if raceenabled {
raceEnable()
raceAcquire(unsafe.Pointer(&rw.readerSem))
raceAcquire(unsafe.Pointer(&rw.writerSem))
}
} }
// Unlock unlocks rw for writing. It is a run-time error if rw is // Unlock unlocks rw for writing. It is a run-time error if rw is
@ -68,6 +93,12 @@ func (rw *RWMutex) Lock() {
// goroutine. One goroutine may RLock (Lock) an RWMutex and then // goroutine. One goroutine may RLock (Lock) an RWMutex and then
// arrange for another goroutine to RUnlock (Unlock) it. // arrange for another goroutine to RUnlock (Unlock) it.
func (rw *RWMutex) Unlock() { func (rw *RWMutex) Unlock() {
if raceenabled {
raceRelease(unsafe.Pointer(&rw.readerSem))
raceRelease(unsafe.Pointer(&rw.writerSem))
raceDisable()
}
// Announce to readers there is no active writer. // Announce to readers there is no active writer.
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
// Unblock blocked readers, if any. // Unblock blocked readers, if any.
@ -76,6 +107,9 @@ func (rw *RWMutex) Unlock() {
} }
// Allow other writers to proceed. // Allow other writers to proceed.
rw.w.Unlock() rw.w.Unlock()
if raceenabled {
raceEnable()
}
} }
// RLocker returns a Locker interface that implements // RLocker returns a Locker interface that implements

View File

@ -4,7 +4,10 @@
package sync package sync
import "sync/atomic" import (
"sync/atomic"
"unsafe"
)
// A WaitGroup waits for a collection of goroutines to finish. // A WaitGroup waits for a collection of goroutines to finish.
// The main goroutine calls Add to set the number of // The main goroutine calls Add to set the number of
@ -34,6 +37,11 @@ type WaitGroup struct {
// If the counter becomes zero, all goroutines blocked on Wait() are released. // If the counter becomes zero, all goroutines blocked on Wait() are released.
// If the counter goes negative, Add panics. // If the counter goes negative, Add panics.
func (wg *WaitGroup) Add(delta int) { func (wg *WaitGroup) Add(delta int) {
if raceenabled {
raceReleaseMerge(unsafe.Pointer(wg))
raceDisable()
defer raceEnable()
}
v := atomic.AddInt32(&wg.counter, int32(delta)) v := atomic.AddInt32(&wg.counter, int32(delta))
if v < 0 { if v < 0 {
panic("sync: negative WaitGroup counter") panic("sync: negative WaitGroup counter")
@ -57,7 +65,14 @@ func (wg *WaitGroup) Done() {
// Wait blocks until the WaitGroup counter is zero. // Wait blocks until the WaitGroup counter is zero.
func (wg *WaitGroup) Wait() { func (wg *WaitGroup) Wait() {
if raceenabled {
raceDisable()
}
if atomic.LoadInt32(&wg.counter) == 0 { if atomic.LoadInt32(&wg.counter) == 0 {
if raceenabled {
raceEnable()
raceAcquire(unsafe.Pointer(wg))
}
return return
} }
wg.m.Lock() wg.m.Lock()
@ -68,7 +83,15 @@ func (wg *WaitGroup) Wait() {
// to avoid missing an Add. // to avoid missing an Add.
if atomic.LoadInt32(&wg.counter) == 0 { if atomic.LoadInt32(&wg.counter) == 0 {
atomic.AddInt32(&wg.waiters, -1) atomic.AddInt32(&wg.waiters, -1)
if raceenabled {
raceEnable()
raceAcquire(unsafe.Pointer(wg))
raceDisable()
}
wg.m.Unlock() wg.m.Unlock()
if raceenabled {
raceEnable()
}
return return
} }
if wg.sema == nil { if wg.sema == nil {
@ -77,4 +100,8 @@ func (wg *WaitGroup) Wait() {
s := wg.sema s := wg.sema
wg.m.Unlock() wg.m.Unlock()
runtime_Semacquire(s) runtime_Semacquire(s)
if raceenabled {
raceEnable()
raceAcquire(unsafe.Pointer(wg))
}
} }