aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/sync
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2020-07-27 22:27:54 -0700
committerIan Lance Taylor <iant@golang.org>2020-08-01 11:21:40 -0700
commitf75af8c1464e948b5e166cf5ab09ebf0d82fc253 (patch)
tree3ba3299859b504bdeb477727471216bd094a0191 /libgo/go/sync
parent75a23e59031fe673fc3b2e60fd1fe5f4c70ecb85 (diff)
downloadgcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.zip
gcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.tar.gz
gcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.tar.bz2
libgo: update to go1.15rc1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/245157
Diffstat (limited to 'libgo/go/sync')
-rw-r--r--libgo/go/sync/atomic/atomic_test.go68
-rw-r--r--libgo/go/sync/atomic/doc.go5
-rw-r--r--libgo/go/sync/atomic/value_test.go8
-rw-r--r--libgo/go/sync/map.go25
-rw-r--r--libgo/go/sync/map_bench_test.go74
-rw-r--r--libgo/go/sync/map_reference_test.go23
-rw-r--r--libgo/go/sync/map_test.go13
-rw-r--r--libgo/go/sync/runtime.go10
-rw-r--r--libgo/go/sync/runtime2.go15
-rw-r--r--libgo/go/sync/runtime2_lockrank.go18
-rw-r--r--libgo/go/sync/rwmutex_test.go2
11 files changed, 204 insertions, 57 deletions
diff --git a/libgo/go/sync/atomic/atomic_test.go b/libgo/go/sync/atomic/atomic_test.go
index 286eadc..83e7c8d 100644
--- a/libgo/go/sync/atomic/atomic_test.go
+++ b/libgo/go/sync/atomic/atomic_test.go
@@ -153,6 +153,21 @@ func TestSwapUintptr(t *testing.T) {
}
}
+var global [1024]byte
+
+func testPointers() []unsafe.Pointer {
+ var pointers []unsafe.Pointer
+ // globals
+ for i := 0; i < 10; i++ {
+ pointers = append(pointers, unsafe.Pointer(&global[1<<i-1]))
+ }
+ // heap
+ pointers = append(pointers, unsafe.Pointer(new(byte)))
+ // nil
+ pointers = append(pointers, nil)
+ return pointers
+}
+
func TestSwapPointer(t *testing.T) {
var x struct {
before uintptr
@@ -163,13 +178,14 @@ func TestSwapPointer(t *testing.T) {
magicptr := uintptr(m)
x.before = magicptr
x.after = magicptr
- var j uintptr
- for delta := uintptr(1 << 16); delta+delta > delta; delta += delta {
- k := SwapPointer(&x.i, unsafe.Pointer(delta))
- if uintptr(x.i) != delta || uintptr(k) != j {
- t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k)
+ var j unsafe.Pointer
+
+ for _, p := range testPointers() {
+ k := SwapPointer(&x.i, p)
+ if x.i != p || k != j {
+ t.Fatalf("p=%p i=%p j=%p k=%p", p, x.i, j, k)
}
- j = delta
+ j = p
}
if x.before != magicptr || x.after != magicptr {
t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr)
@@ -456,20 +472,20 @@ func TestCompareAndSwapPointer(t *testing.T) {
magicptr := uintptr(m)
x.before = magicptr
x.after = magicptr
- for val := uintptr(1 << 16); val+val > val; val += val {
- x.i = unsafe.Pointer(val)
- if !CompareAndSwapPointer(&x.i, unsafe.Pointer(val), unsafe.Pointer(val+1)) {
- t.Fatalf("should have swapped %#x %#x", val, val+1)
+ q := unsafe.Pointer(new(byte))
+ for _, p := range testPointers() {
+ x.i = p
+ if !CompareAndSwapPointer(&x.i, p, q) {
+ t.Fatalf("should have swapped %p %p", p, q)
}
- if x.i != unsafe.Pointer(val+1) {
- t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+ if x.i != q {
+ t.Fatalf("wrong x.i after swap: x.i=%p want %p", x.i, q)
}
- x.i = unsafe.Pointer(val + 1)
- if CompareAndSwapPointer(&x.i, unsafe.Pointer(val), unsafe.Pointer(val+2)) {
- t.Fatalf("should not have swapped %#x %#x", val, val+2)
+ if CompareAndSwapPointer(&x.i, p, nil) {
+ t.Fatalf("should not have swapped %p nil", p)
}
- if x.i != unsafe.Pointer(val+1) {
- t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+ if x.i != q {
+ t.Fatalf("wrong x.i after swap: x.i=%p want %p", x.i, q)
}
}
if x.before != magicptr || x.after != magicptr {
@@ -595,12 +611,12 @@ func TestLoadPointer(t *testing.T) {
magicptr := uintptr(m)
x.before = magicptr
x.after = magicptr
- for delta := uintptr(1 << 16); delta+delta > delta; delta += delta {
+ for _, p := range testPointers() {
+ x.i = p
k := LoadPointer(&x.i)
- if k != x.i {
- t.Fatalf("delta=%d i=%d k=%d", delta, x.i, k)
+ if k != p {
+ t.Fatalf("p=%x k=%x", p, k)
}
- x.i = unsafe.Pointer(uintptr(x.i) + delta)
}
if x.before != magicptr || x.after != magicptr {
t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr)
@@ -730,13 +746,11 @@ func TestStorePointer(t *testing.T) {
magicptr := uintptr(m)
x.before = magicptr
x.after = magicptr
- v := unsafe.Pointer(uintptr(0))
- for delta := uintptr(1 << 16); delta+delta > delta; delta += delta {
- StorePointer(&x.i, unsafe.Pointer(v))
- if x.i != v {
- t.Fatalf("delta=%d i=%d v=%d", delta, x.i, v)
+ for _, p := range testPointers() {
+ StorePointer(&x.i, p)
+ if x.i != p {
+ t.Fatalf("x.i=%p p=%p", x.i, p)
}
- v = unsafe.Pointer(uintptr(v) + delta)
}
if x.before != magicptr || x.after != magicptr {
t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr)
diff --git a/libgo/go/sync/atomic/doc.go b/libgo/go/sync/atomic/doc.go
index 108b76b..ff4ad80 100644
--- a/libgo/go/sync/atomic/doc.go
+++ b/libgo/go/sync/atomic/doc.go
@@ -143,8 +143,3 @@ func StoreUintptr(addr *uintptr, val uintptr)
// StorePointer atomically stores val into *addr.
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)
-
-// Helper for ARM. Linker will discard on other systems
-func panic64() {
- panic("sync/atomic: broken 64-bit atomic operations (buggy QEMU)")
-}
diff --git a/libgo/go/sync/atomic/value_test.go b/libgo/go/sync/atomic/value_test.go
index fd69ba3..f289766 100644
--- a/libgo/go/sync/atomic/value_test.go
+++ b/libgo/go/sync/atomic/value_test.go
@@ -91,10 +91,11 @@ func TestValueConcurrent(t *testing.T) {
}
for _, test := range tests {
var v Value
- done := make(chan bool)
+ done := make(chan bool, p)
for i := 0; i < p; i++ {
go func() {
r := rand.New(rand.NewSource(rand.Int63()))
+ expected := true
loop:
for j := 0; j < N; j++ {
x := test[r.Intn(len(test))]
@@ -106,9 +107,10 @@ func TestValueConcurrent(t *testing.T) {
}
}
t.Logf("loaded unexpected value %+v, want %+v", x, test)
- done <- false
+ expected = false
+ break
}
- done <- true
+ done <- expected
}()
}
for i := 0; i < p; i++ {
diff --git a/libgo/go/sync/map.go b/libgo/go/sync/map.go
index c6aa308..a61e2eb 100644
--- a/libgo/go/sync/map.go
+++ b/libgo/go/sync/map.go
@@ -263,8 +263,9 @@ func (e *entry) tryLoadOrStore(i interface{}) (actual interface{}, loaded, ok bo
}
}
-// Delete deletes the value for a key.
-func (m *Map) Delete(key interface{}) {
+// LoadAndDelete deletes the value for a key, returning the previous value if any.
+// The loaded result reports whether the key was present.
+func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) {
read, _ := m.read.Load().(readOnly)
e, ok := read.m[key]
if !ok && read.amended {
@@ -272,23 +273,33 @@ func (m *Map) Delete(key interface{}) {
read, _ = m.read.Load().(readOnly)
e, ok = read.m[key]
if !ok && read.amended {
- delete(m.dirty, key)
+ e, ok = m.dirty[key]
+ // Regardless of whether the entry was present, record a miss: this key
+ // will take the slow path until the dirty map is promoted to the read
+ // map.
+ m.missLocked()
}
m.mu.Unlock()
}
if ok {
- e.delete()
+ return e.delete()
}
+ return nil, false
}
-func (e *entry) delete() (hadValue bool) {
+// Delete deletes the value for a key.
+func (m *Map) Delete(key interface{}) {
+ m.LoadAndDelete(key)
+}
+
+func (e *entry) delete() (value interface{}, ok bool) {
for {
p := atomic.LoadPointer(&e.p)
if p == nil || p == expunged {
- return false
+ return nil, false
}
if atomic.CompareAndSwapPointer(&e.p, p, nil) {
- return true
+ return *(*interface{})(p), true
}
}
}
diff --git a/libgo/go/sync/map_bench_test.go b/libgo/go/sync/map_bench_test.go
index e6a8bad..cf0a3d7 100644
--- a/libgo/go/sync/map_bench_test.go
+++ b/libgo/go/sync/map_bench_test.go
@@ -144,6 +144,66 @@ func BenchmarkLoadOrStoreCollision(b *testing.B) {
})
}
+func BenchmarkLoadAndDeleteBalanced(b *testing.B) {
+ const hits, misses = 128, 128
+
+ benchMap(b, bench{
+ setup: func(b *testing.B, m mapInterface) {
+ if _, ok := m.(*DeepCopyMap); ok {
+ b.Skip("DeepCopyMap has quadratic running time.")
+ }
+ for i := 0; i < hits; i++ {
+ m.LoadOrStore(i, i)
+ }
+ // Prime the map to get it into a steady state.
+ for i := 0; i < hits*2; i++ {
+ m.Load(i % hits)
+ }
+ },
+
+ perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
+ for ; pb.Next(); i++ {
+ j := i % (hits + misses)
+ if j < hits {
+ m.LoadAndDelete(j)
+ } else {
+ m.LoadAndDelete(i)
+ }
+ }
+ },
+ })
+}
+
+func BenchmarkLoadAndDeleteUnique(b *testing.B) {
+ benchMap(b, bench{
+ setup: func(b *testing.B, m mapInterface) {
+ if _, ok := m.(*DeepCopyMap); ok {
+ b.Skip("DeepCopyMap has quadratic running time.")
+ }
+ },
+
+ perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
+ for ; pb.Next(); i++ {
+ m.LoadAndDelete(i)
+ }
+ },
+ })
+}
+
+func BenchmarkLoadAndDeleteCollision(b *testing.B) {
+ benchMap(b, bench{
+ setup: func(_ *testing.B, m mapInterface) {
+ m.LoadOrStore(0, 0)
+ },
+
+ perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
+ for ; pb.Next(); i++ {
+ m.LoadAndDelete(0)
+ }
+ },
+ })
+}
+
func BenchmarkRange(b *testing.B) {
const mapSize = 1 << 10
@@ -213,3 +273,17 @@ func BenchmarkAdversarialDelete(b *testing.B) {
},
})
}
+
+func BenchmarkDeleteCollision(b *testing.B) {
+ benchMap(b, bench{
+ setup: func(_ *testing.B, m mapInterface) {
+ m.LoadOrStore(0, 0)
+ },
+
+ perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
+ for ; pb.Next(); i++ {
+ m.Delete(0)
+ }
+ },
+ })
+}
diff --git a/libgo/go/sync/map_reference_test.go b/libgo/go/sync/map_reference_test.go
index 9f27b07..d105a24 100644
--- a/libgo/go/sync/map_reference_test.go
+++ b/libgo/go/sync/map_reference_test.go
@@ -16,6 +16,7 @@ type mapInterface interface {
Load(interface{}) (interface{}, bool)
Store(key, value interface{})
LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
+ LoadAndDelete(key interface{}) (value interface{}, loaded bool)
Delete(interface{})
Range(func(key, value interface{}) (shouldContinue bool))
}
@@ -56,6 +57,18 @@ func (m *RWMutexMap) LoadOrStore(key, value interface{}) (actual interface{}, lo
return actual, loaded
}
+func (m *RWMutexMap) LoadAndDelete(key interface{}) (value interface{}, loaded bool) {
+ m.mu.Lock()
+ value, loaded = m.dirty[key]
+ if !loaded {
+ m.mu.Unlock()
+ return nil, false
+ }
+ delete(m.dirty, key)
+ m.mu.Unlock()
+ return value, loaded
+}
+
func (m *RWMutexMap) Delete(key interface{}) {
m.mu.Lock()
delete(m.dirty, key)
@@ -124,6 +137,16 @@ func (m *DeepCopyMap) LoadOrStore(key, value interface{}) (actual interface{}, l
return actual, loaded
}
+func (m *DeepCopyMap) LoadAndDelete(key interface{}) (value interface{}, loaded bool) {
+ m.mu.Lock()
+ dirty := m.dirty()
+ value, loaded = dirty[key]
+ delete(dirty, key)
+ m.clean.Store(dirty)
+ m.mu.Unlock()
+ return
+}
+
func (m *DeepCopyMap) Delete(key interface{}) {
m.mu.Lock()
dirty := m.dirty()
diff --git a/libgo/go/sync/map_test.go b/libgo/go/sync/map_test.go
index b60a1c7..4ae989a 100644
--- a/libgo/go/sync/map_test.go
+++ b/libgo/go/sync/map_test.go
@@ -16,13 +16,14 @@ import (
type mapOp string
const (
- opLoad = mapOp("Load")
- opStore = mapOp("Store")
- opLoadOrStore = mapOp("LoadOrStore")
- opDelete = mapOp("Delete")
+ opLoad = mapOp("Load")
+ opStore = mapOp("Store")
+ opLoadOrStore = mapOp("LoadOrStore")
+ opLoadAndDelete = mapOp("LoadAndDelete")
+ opDelete = mapOp("Delete")
)
-var mapOps = [...]mapOp{opLoad, opStore, opLoadOrStore, opDelete}
+var mapOps = [...]mapOp{opLoad, opStore, opLoadOrStore, opLoadAndDelete, opDelete}
// mapCall is a quick.Generator for calls on mapInterface.
type mapCall struct {
@@ -39,6 +40,8 @@ func (c mapCall) apply(m mapInterface) (interface{}, bool) {
return nil, false
case opLoadOrStore:
return m.LoadOrStore(c.k, c.v)
+ case opLoadAndDelete:
+ return m.LoadAndDelete(c.k)
case opDelete:
m.Delete(c.k)
return nil, false
diff --git a/libgo/go/sync/runtime.go b/libgo/go/sync/runtime.go
index 3ad44e7..de2b0a3 100644
--- a/libgo/go/sync/runtime.go
+++ b/libgo/go/sync/runtime.go
@@ -28,16 +28,6 @@ func runtime_SemacquireMutex(s *uint32, lifo bool, skipframes int)
// runtime_Semrelease's caller.
func runtime_Semrelease(s *uint32, handoff bool, skipframes int)
-// Approximation of notifyList in runtime/sema.go. Size and alignment must
-// agree.
-type notifyList struct {
- wait uint32
- notify uint32
- lock uintptr
- head unsafe.Pointer
- tail unsafe.Pointer
-}
-
// See runtime/sema.go for documentation.
func runtime_notifyListAdd(l *notifyList) uint32
diff --git a/libgo/go/sync/runtime2.go b/libgo/go/sync/runtime2.go
new file mode 100644
index 0000000..931edad
--- /dev/null
+++ b/libgo/go/sync/runtime2.go
@@ -0,0 +1,15 @@
+// +build !goexperiment.staticlockranking
+
+package sync
+
+import "unsafe"
+
+// Approximation of notifyList in runtime/sema.go. Size and alignment must
+// agree.
+type notifyList struct {
+ wait uint32
+ notify uint32
+ lock uintptr // key field of the mutex
+ head unsafe.Pointer
+ tail unsafe.Pointer
+}
diff --git a/libgo/go/sync/runtime2_lockrank.go b/libgo/go/sync/runtime2_lockrank.go
new file mode 100644
index 0000000..5a68e90
--- /dev/null
+++ b/libgo/go/sync/runtime2_lockrank.go
@@ -0,0 +1,18 @@
+// +build goexperiment.staticlockranking
+
+package sync
+
+import "unsafe"
+
+// Approximation of notifyList in runtime/sema.go. Size and alignment must
+// agree.
+type notifyList struct {
+ wait uint32
+ notify uint32
+ rank int // rank field of the mutex
+ pad int // pad field of the mutex
+ lock uintptr // key field of the mutex
+
+ head unsafe.Pointer
+ tail unsafe.Pointer
+}
diff --git a/libgo/go/sync/rwmutex_test.go b/libgo/go/sync/rwmutex_test.go
index 9ee8864..c98e69f 100644
--- a/libgo/go/sync/rwmutex_test.go
+++ b/libgo/go/sync/rwmutex_test.go
@@ -59,6 +59,7 @@ func reader(rwm *RWMutex, num_iterations int, activity *int32, cdone chan bool)
rwm.RLock()
n := atomic.AddInt32(activity, 1)
if n < 1 || n >= 10000 {
+ rwm.RUnlock()
panic(fmt.Sprintf("wlock(%d)\n", n))
}
for i := 0; i < 100; i++ {
@@ -74,6 +75,7 @@ func writer(rwm *RWMutex, num_iterations int, activity *int32, cdone chan bool)
rwm.Lock()
n := atomic.AddInt32(activity, 10000)
if n != 10000 {
+ rwm.Unlock()
panic(fmt.Sprintf("wlock(%d)\n", n))
}
for i := 0; i < 100; i++ {