mirror of
https://github.com/golang/go
synced 2024-11-18 00:34:44 -07:00
sync: enable Pool when using race detector
Disabled by https://golang.org/cl/53020044 due to false positives. Reenable and model properly. Fixes #17306. Change-Id: I28405ddfcd17f58cf1427c300273212729154359 Reviewed-on: https://go-review.googlesource.com/31589 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
This commit is contained in:
parent
154860ff17
commit
ba048f7ce4
47
src/runtime/race/testdata/pool_test.go
vendored
Normal file
47
src/runtime/race/testdata/pool_test.go
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2016 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 race_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRacePool(t *testing.T) {
|
||||
// Pool randomly drops the argument on the floor during Put.
|
||||
// Repeat so that at least one iteration gets reuse.
|
||||
for i := 0; i < 10; i++ {
|
||||
c := make(chan int)
|
||||
p := &sync.Pool{New: func() interface{} { return make([]byte, 10) }}
|
||||
x := p.Get().([]byte)
|
||||
x[0] = 1
|
||||
p.Put(x)
|
||||
go func() {
|
||||
y := p.Get().([]byte)
|
||||
y[0] = 2
|
||||
c <- 1
|
||||
}()
|
||||
x[0] = 3
|
||||
<-c
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoRacePool(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
p := &sync.Pool{New: func() interface{} { return make([]byte, 10) }}
|
||||
x := p.Get().([]byte)
|
||||
x[0] = 1
|
||||
p.Put(x)
|
||||
go func() {
|
||||
y := p.Get().([]byte)
|
||||
y[0] = 2
|
||||
p.Put(y)
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
x = p.Get().([]byte)
|
||||
x[0] = 3
|
||||
}
|
||||
}
|
@ -96,6 +96,9 @@ var hashLoad = loadFactor
|
||||
// in asm_*.s
|
||||
func fastrand() uint32
|
||||
|
||||
//go:linkname sync_fastrand sync.fastrand
|
||||
func sync_fastrand() uint32 { return fastrand() }
|
||||
|
||||
// in asm_*.s
|
||||
//go:noescape
|
||||
func memequal(a, b unsafe.Pointer, size uintptr) bool
|
||||
|
@ -61,29 +61,49 @@ type poolLocal struct {
|
||||
pad [128]byte // Prevents false sharing.
|
||||
}
|
||||
|
||||
// from runtime
|
||||
func fastrand() uint32
|
||||
|
||||
var poolRaceHash [128]uint64
|
||||
|
||||
// poolRaceAddr returns an address to use as the synchronization point
|
||||
// for race detector logic. We don't use the actual pointer stored in x
|
||||
// directly, for fear of conflicting with other synchronization on that address.
|
||||
// Instead, we hash the pointer to get an index into poolRaceHash.
|
||||
// See discussion on golang.org/cl/31589.
|
||||
func poolRaceAddr(x interface{}) unsafe.Pointer {
|
||||
ptr := uintptr((*[2]unsafe.Pointer)(unsafe.Pointer(&x))[1])
|
||||
h := uint32((uint64(uint32(ptr)) * 0x85ebca6b) >> 16)
|
||||
return unsafe.Pointer(&poolRaceHash[h%uint32(len(poolRaceHash))])
|
||||
}
|
||||
|
||||
// Put adds x to the pool.
|
||||
func (p *Pool) Put(x interface{}) {
|
||||
if race.Enabled {
|
||||
// Under race detector the Pool degenerates into no-op.
|
||||
// It's conforming, simple and does not introduce excessive
|
||||
// happens-before edges between unrelated goroutines.
|
||||
return
|
||||
}
|
||||
if x == nil {
|
||||
return
|
||||
}
|
||||
if race.Enabled {
|
||||
if fastrand()%4 == 0 {
|
||||
// Randomly drop x on floor.
|
||||
return
|
||||
}
|
||||
race.ReleaseMerge(poolRaceAddr(x))
|
||||
race.Disable()
|
||||
}
|
||||
l := p.pin()
|
||||
if l.private == nil {
|
||||
l.private = x
|
||||
x = nil
|
||||
}
|
||||
runtime_procUnpin()
|
||||
if x == nil {
|
||||
return
|
||||
}
|
||||
if x != nil {
|
||||
l.Lock()
|
||||
l.shared = append(l.shared, x)
|
||||
l.Unlock()
|
||||
}
|
||||
if race.Enabled {
|
||||
race.Enable()
|
||||
}
|
||||
}
|
||||
|
||||
// Get selects an arbitrary item from the Pool, removes it from the
|
||||
@ -96,18 +116,13 @@ func (p *Pool) Put(x interface{}) {
|
||||
// the result of calling p.New.
|
||||
func (p *Pool) Get() interface{} {
|
||||
if race.Enabled {
|
||||
if p.New != nil {
|
||||
return p.New()
|
||||
}
|
||||
return nil
|
||||
race.Disable()
|
||||
}
|
||||
l := p.pin()
|
||||
x := l.private
|
||||
l.private = nil
|
||||
runtime_procUnpin()
|
||||
if x != nil {
|
||||
return x
|
||||
}
|
||||
if x == nil {
|
||||
l.Lock()
|
||||
last := len(l.shared) - 1
|
||||
if last >= 0 {
|
||||
@ -115,10 +130,20 @@ func (p *Pool) Get() interface{} {
|
||||
l.shared = l.shared[:last]
|
||||
}
|
||||
l.Unlock()
|
||||
if x != nil {
|
||||
return x
|
||||
if x == nil {
|
||||
x = p.getSlow()
|
||||
}
|
||||
return p.getSlow()
|
||||
}
|
||||
if race.Enabled {
|
||||
race.Enable()
|
||||
if x != nil {
|
||||
race.Acquire(poolRaceAddr(x))
|
||||
}
|
||||
}
|
||||
if x == nil && p.New != nil {
|
||||
x = p.New()
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func (p *Pool) getSlow() (x interface{}) {
|
||||
@ -140,10 +165,6 @@ func (p *Pool) getSlow() (x interface{}) {
|
||||
}
|
||||
l.Unlock()
|
||||
}
|
||||
|
||||
if x == nil && p.New != nil {
|
||||
x = p.New()
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user