diff options
author | Ian Lance Taylor <iant@golang.org> | 2020-07-27 22:27:54 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2020-08-01 11:21:40 -0700 |
commit | f75af8c1464e948b5e166cf5ab09ebf0d82fc253 (patch) | |
tree | 3ba3299859b504bdeb477727471216bd094a0191 /libgo/go/sync | |
parent | 75a23e59031fe673fc3b2e60fd1fe5f4c70ecb85 (diff) | |
download | gcc-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.go | 68 | ||||
-rw-r--r-- | libgo/go/sync/atomic/doc.go | 5 | ||||
-rw-r--r-- | libgo/go/sync/atomic/value_test.go | 8 | ||||
-rw-r--r-- | libgo/go/sync/map.go | 25 | ||||
-rw-r--r-- | libgo/go/sync/map_bench_test.go | 74 | ||||
-rw-r--r-- | libgo/go/sync/map_reference_test.go | 23 | ||||
-rw-r--r-- | libgo/go/sync/map_test.go | 13 | ||||
-rw-r--r-- | libgo/go/sync/runtime.go | 10 | ||||
-rw-r--r-- | libgo/go/sync/runtime2.go | 15 | ||||
-rw-r--r-- | libgo/go/sync/runtime2_lockrank.go | 18 | ||||
-rw-r--r-- | libgo/go/sync/rwmutex_test.go | 2 |
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++ { |