diff options
| author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-11-06 19:49:01 +0000 |
|---|---|---|
| committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-11-06 19:49:01 +0000 |
| commit | 0ce10ea1348e9afd5d0eec6bca986bfe58bac5ac (patch) | |
| tree | 39530b071991b2326f881b2a30a2d82d6c133fd6 /libgo/go/sync/atomic | |
| parent | 57a8bf1b0c6057ccbacb0cf79eb84d1985c2c1fe (diff) | |
| download | ppe42-gcc-0ce10ea1348e9afd5d0eec6bca986bfe58bac5ac.tar.gz ppe42-gcc-0ce10ea1348e9afd5d0eec6bca986bfe58bac5ac.zip | |
libgo: Update to October 24 version of master library.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@204466 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/sync/atomic')
| -rw-r--r-- | libgo/go/sync/atomic/64bit_arm.go | 10 | ||||
| -rw-r--r-- | libgo/go/sync/atomic/atomic.c | 129 | ||||
| -rw-r--r-- | libgo/go/sync/atomic/atomic_test.go | 397 | ||||
| -rw-r--r-- | libgo/go/sync/atomic/doc.go | 31 | ||||
| -rw-r--r-- | libgo/go/sync/atomic/race.go | 48 |
5 files changed, 544 insertions, 71 deletions
diff --git a/libgo/go/sync/atomic/64bit_arm.go b/libgo/go/sync/atomic/64bit_arm.go index f070e78bd3c..c08f214c7ef 100644 --- a/libgo/go/sync/atomic/64bit_arm.go +++ b/libgo/go/sync/atomic/64bit_arm.go @@ -34,3 +34,13 @@ func addUint64(val *uint64, delta uint64) (new uint64) { } return } + +func swapUint64(addr *uint64, new uint64) (old uint64) { + for { + old = *addr + if CompareAndSwapUint64(addr, old, new) { + break + } + } + return +} diff --git a/libgo/go/sync/atomic/atomic.c b/libgo/go/sync/atomic/atomic.c index 32430df2ba2..f0ba57b3cca 100644 --- a/libgo/go/sync/atomic/atomic.c +++ b/libgo/go/sync/atomic/atomic.c @@ -8,8 +8,69 @@ #include "runtime.h" +int32_t SwapInt32 (int32_t *, int32_t) + __asm__ (GOSYM_PREFIX "sync_atomic.SwapInt32") + __attribute__ ((no_split_stack)); + +int32_t +SwapInt32 (int32_t *addr, int32_t new) +{ + return __atomic_exchange_n (addr, new, __ATOMIC_SEQ_CST); +} + +int64_t SwapInt64 (int64_t *, int64_t) + __asm__ (GOSYM_PREFIX "sync_atomic.SwapInt64") + __attribute__ ((no_split_stack)); + +int64_t +SwapInt64 (int64_t *addr, int64_t new) +{ + return __atomic_exchange_n (addr, new, __ATOMIC_SEQ_CST); +} + +uint32_t SwapUint32 (uint32_t *, uint32_t) + __asm__ (GOSYM_PREFIX "sync_atomic.SwapUint32") + __attribute__ ((no_split_stack)); + +uint32_t +SwapUint32 (uint32_t *addr, uint32_t new) +{ + return __atomic_exchange_n (addr, new, __ATOMIC_SEQ_CST); +} + +uint64_t SwapUint64 (uint64_t *, uint64_t) + __asm__ (GOSYM_PREFIX "sync_atomic.SwapUint64") + __attribute__ ((no_split_stack)); + +uint64_t +SwapUint64 (uint64_t *addr, uint64_t new) +{ + return __atomic_exchange_n (addr, new, __ATOMIC_SEQ_CST); +} + +uintptr_t SwapUintptr (uintptr_t *, uintptr_t) + __asm__ (GOSYM_PREFIX "sync_atomic.SwapUintptr") + __attribute__ ((no_split_stack)); + +uintptr_t +SwapUintptr (uintptr_t *addr, uintptr_t new) +{ + return __atomic_exchange_n (addr, new, __ATOMIC_SEQ_CST); +} + +void *SwapPointer (void **, void *) + __asm__ (GOSYM_PREFIX "sync_atomic.SwapPointer") + __attribute__ ((no_split_stack)); + +void * +SwapPointer (void **addr, void *new) +{ + return __atomic_exchange_n (addr, new, __ATOMIC_SEQ_CST); +} + _Bool CompareAndSwapInt32 (int32_t *, int32_t, int32_t) - __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapInt32"); + __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapInt32") + __attribute__ ((no_split_stack)); _Bool CompareAndSwapInt32 (int32_t *val, int32_t old, int32_t new) @@ -18,7 +79,8 @@ CompareAndSwapInt32 (int32_t *val, int32_t old, int32_t new) } _Bool CompareAndSwapInt64 (int64_t *, int64_t, int64_t) - __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapInt64"); + __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapInt64") + __attribute__ ((no_split_stack)); _Bool CompareAndSwapInt64 (int64_t *val, int64_t old, int64_t new) @@ -27,7 +89,8 @@ CompareAndSwapInt64 (int64_t *val, int64_t old, int64_t new) } _Bool CompareAndSwapUint32 (uint32_t *, uint32_t, uint32_t) - __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUint32"); + __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUint32") + __attribute__ ((no_split_stack)); _Bool CompareAndSwapUint32 (uint32_t *val, uint32_t old, uint32_t new) @@ -36,7 +99,8 @@ CompareAndSwapUint32 (uint32_t *val, uint32_t old, uint32_t new) } _Bool CompareAndSwapUint64 (uint64_t *, uint64_t, uint64_t) - __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUint64"); + __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUint64") + __attribute__ ((no_split_stack)); _Bool CompareAndSwapUint64 (uint64_t *val, uint64_t old, uint64_t new) @@ -45,7 +109,8 @@ CompareAndSwapUint64 (uint64_t *val, uint64_t old, uint64_t new) } _Bool CompareAndSwapUintptr (uintptr_t *, uintptr_t, uintptr_t) - __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUintptr"); + __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUintptr") + __attribute__ ((no_split_stack)); _Bool CompareAndSwapUintptr (uintptr_t *val, uintptr_t old, uintptr_t new) @@ -54,7 +119,8 @@ CompareAndSwapUintptr (uintptr_t *val, uintptr_t old, uintptr_t new) } _Bool CompareAndSwapPointer (void **, void *, void *) - __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapPointer"); + __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapPointer") + __attribute__ ((no_split_stack)); _Bool CompareAndSwapPointer (void **val, void *old, void *new) @@ -63,7 +129,8 @@ CompareAndSwapPointer (void **val, void *old, void *new) } int32_t AddInt32 (int32_t *, int32_t) - __asm__ (GOSYM_PREFIX "sync_atomic.AddInt32"); + __asm__ (GOSYM_PREFIX "sync_atomic.AddInt32") + __attribute__ ((no_split_stack)); int32_t AddInt32 (int32_t *val, int32_t delta) @@ -72,7 +139,8 @@ AddInt32 (int32_t *val, int32_t delta) } uint32_t AddUint32 (uint32_t *, uint32_t) - __asm__ (GOSYM_PREFIX "sync_atomic.AddUint32"); + __asm__ (GOSYM_PREFIX "sync_atomic.AddUint32") + __attribute__ ((no_split_stack)); uint32_t AddUint32 (uint32_t *val, uint32_t delta) @@ -81,7 +149,8 @@ AddUint32 (uint32_t *val, uint32_t delta) } int64_t AddInt64 (int64_t *, int64_t) - __asm__ (GOSYM_PREFIX "sync_atomic.AddInt64"); + __asm__ (GOSYM_PREFIX "sync_atomic.AddInt64") + __attribute__ ((no_split_stack)); int64_t AddInt64 (int64_t *val, int64_t delta) @@ -90,7 +159,8 @@ AddInt64 (int64_t *val, int64_t delta) } uint64_t AddUint64 (uint64_t *, uint64_t) - __asm__ (GOSYM_PREFIX "sync_atomic.AddUint64"); + __asm__ (GOSYM_PREFIX "sync_atomic.AddUint64") + __attribute__ ((no_split_stack)); uint64_t AddUint64 (uint64_t *val, uint64_t delta) @@ -99,7 +169,8 @@ AddUint64 (uint64_t *val, uint64_t delta) } uintptr_t AddUintptr (uintptr_t *, uintptr_t) - __asm__ (GOSYM_PREFIX "sync_atomic.AddUintptr"); + __asm__ (GOSYM_PREFIX "sync_atomic.AddUintptr") + __attribute__ ((no_split_stack)); uintptr_t AddUintptr (uintptr_t *val, uintptr_t delta) @@ -108,7 +179,8 @@ AddUintptr (uintptr_t *val, uintptr_t delta) } int32_t LoadInt32 (int32_t *addr) - __asm__ (GOSYM_PREFIX "sync_atomic.LoadInt32"); + __asm__ (GOSYM_PREFIX "sync_atomic.LoadInt32") + __attribute__ ((no_split_stack)); int32_t LoadInt32 (int32_t *addr) @@ -122,7 +194,8 @@ LoadInt32 (int32_t *addr) } int64_t LoadInt64 (int64_t *addr) - __asm__ (GOSYM_PREFIX "sync_atomic.LoadInt64"); + __asm__ (GOSYM_PREFIX "sync_atomic.LoadInt64") + __attribute__ ((no_split_stack)); int64_t LoadInt64 (int64_t *addr) @@ -136,7 +209,8 @@ LoadInt64 (int64_t *addr) } uint32_t LoadUint32 (uint32_t *addr) - __asm__ (GOSYM_PREFIX "sync_atomic.LoadUint32"); + __asm__ (GOSYM_PREFIX "sync_atomic.LoadUint32") + __attribute__ ((no_split_stack)); uint32_t LoadUint32 (uint32_t *addr) @@ -150,7 +224,8 @@ LoadUint32 (uint32_t *addr) } uint64_t LoadUint64 (uint64_t *addr) - __asm__ (GOSYM_PREFIX "sync_atomic.LoadUint64"); + __asm__ (GOSYM_PREFIX "sync_atomic.LoadUint64") + __attribute__ ((no_split_stack)); uint64_t LoadUint64 (uint64_t *addr) @@ -164,7 +239,8 @@ LoadUint64 (uint64_t *addr) } uintptr_t LoadUintptr (uintptr_t *addr) - __asm__ (GOSYM_PREFIX "sync_atomic.LoadUintptr"); + __asm__ (GOSYM_PREFIX "sync_atomic.LoadUintptr") + __attribute__ ((no_split_stack)); uintptr_t LoadUintptr (uintptr_t *addr) @@ -178,7 +254,8 @@ LoadUintptr (uintptr_t *addr) } void *LoadPointer (void **addr) - __asm__ (GOSYM_PREFIX "sync_atomic.LoadPointer"); + __asm__ (GOSYM_PREFIX "sync_atomic.LoadPointer") + __attribute__ ((no_split_stack)); void * LoadPointer (void **addr) @@ -192,7 +269,8 @@ LoadPointer (void **addr) } void StoreInt32 (int32_t *addr, int32_t val) - __asm__ (GOSYM_PREFIX "sync_atomic.StoreInt32"); + __asm__ (GOSYM_PREFIX "sync_atomic.StoreInt32") + __attribute__ ((no_split_stack)); void StoreInt32 (int32_t *addr, int32_t val) @@ -205,7 +283,8 @@ StoreInt32 (int32_t *addr, int32_t val) } void StoreInt64 (int64_t *addr, int64_t val) - __asm__ (GOSYM_PREFIX "sync_atomic.StoreInt64"); + __asm__ (GOSYM_PREFIX "sync_atomic.StoreInt64") + __attribute__ ((no_split_stack)); void StoreInt64 (int64_t *addr, int64_t val) @@ -218,7 +297,8 @@ StoreInt64 (int64_t *addr, int64_t val) } void StoreUint32 (uint32_t *addr, uint32_t val) - __asm__ (GOSYM_PREFIX "sync_atomic.StoreUint32"); + __asm__ (GOSYM_PREFIX "sync_atomic.StoreUint32") + __attribute__ ((no_split_stack)); void StoreUint32 (uint32_t *addr, uint32_t val) @@ -231,7 +311,8 @@ StoreUint32 (uint32_t *addr, uint32_t val) } void StoreUint64 (uint64_t *addr, uint64_t val) - __asm__ (GOSYM_PREFIX "sync_atomic.StoreUint64"); + __asm__ (GOSYM_PREFIX "sync_atomic.StoreUint64") + __attribute__ ((no_split_stack)); void StoreUint64 (uint64_t *addr, uint64_t val) @@ -244,7 +325,8 @@ StoreUint64 (uint64_t *addr, uint64_t val) } void StoreUintptr (uintptr_t *addr, uintptr_t val) - __asm__ (GOSYM_PREFIX "sync_atomic.StoreUintptr"); + __asm__ (GOSYM_PREFIX "sync_atomic.StoreUintptr") + __attribute__ ((no_split_stack)); void StoreUintptr (uintptr_t *addr, uintptr_t val) @@ -257,7 +339,8 @@ StoreUintptr (uintptr_t *addr, uintptr_t val) } void StorePointer (void **addr, void *val) - __asm__ (GOSYM_PREFIX "sync_atomic.StorePointer"); + __asm__ (GOSYM_PREFIX "sync_atomic.StorePointer") + __attribute__ ((no_split_stack)); void StorePointer (void **addr, void *val) diff --git a/libgo/go/sync/atomic/atomic_test.go b/libgo/go/sync/atomic/atomic_test.go index c6c33dc3c69..06dd5f7ce89 100644 --- a/libgo/go/sync/atomic/atomic_test.go +++ b/libgo/go/sync/atomic/atomic_test.go @@ -5,7 +5,9 @@ package atomic_test import ( + "fmt" "runtime" + "strings" . "sync/atomic" "testing" "unsafe" @@ -38,6 +40,142 @@ var test64err = func() (err interface{}) { return nil }() +func TestSwapInt32(t *testing.T) { + var x struct { + before int32 + i int32 + after int32 + } + x.before = magic32 + x.after = magic32 + var j int32 + for delta := int32(1); delta+delta > delta; delta += delta { + k := SwapInt32(&x.i, delta) + if x.i != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k) + } + j = delta + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) + } +} + +func TestSwapUint32(t *testing.T) { + var x struct { + before uint32 + i uint32 + after uint32 + } + x.before = magic32 + x.after = magic32 + var j uint32 + for delta := uint32(1); delta+delta > delta; delta += delta { + k := SwapUint32(&x.i, delta) + if x.i != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k) + } + j = delta + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) + } +} + +func TestSwapInt64(t *testing.T) { + if test64err != nil { + t.Skipf("Skipping 64-bit tests: %v", test64err) + } + var x struct { + before int64 + i int64 + after int64 + } + x.before = magic64 + x.after = magic64 + var j int64 + for delta := int64(1); delta+delta > delta; delta += delta { + k := SwapInt64(&x.i, delta) + if x.i != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k) + } + j = delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + } +} + +func TestSwapUint64(t *testing.T) { + if test64err != nil { + t.Skipf("Skipping 64-bit tests: %v", test64err) + } + var x struct { + before uint64 + i uint64 + after uint64 + } + x.before = magic64 + x.after = magic64 + var j uint64 + for delta := uint64(1); delta+delta > delta; delta += delta { + k := SwapUint64(&x.i, delta) + if x.i != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k) + } + j = delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + } +} + +func TestSwapUintptr(t *testing.T) { + var x struct { + before uintptr + i uintptr + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + var j uintptr + for delta := uintptr(1); delta+delta > delta; delta += delta { + k := SwapUintptr(&x.i, delta) + if x.i != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k) + } + j = delta + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) + } +} + +func TestSwapPointer(t *testing.T) { + var x struct { + before uintptr + i unsafe.Pointer + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + var j uintptr + for delta := uintptr(1); 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) + } + j = delta + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) + } +} + func TestAddInt32(t *testing.T) { var x struct { before int32 @@ -241,7 +379,7 @@ func TestCompareAndSwapInt64(t *testing.T) { } } -func TestCompareAndSwapUint64(t *testing.T) { +func testCompareAndSwapUint64(t *testing.T, cas func(*uint64, uint64, uint64) bool) { if test64err != nil { t.Skipf("Skipping 64-bit tests: %v", test64err) } @@ -254,14 +392,14 @@ func TestCompareAndSwapUint64(t *testing.T) { x.after = magic64 for val := uint64(1); val+val > val; val += val { x.i = val - if !CompareAndSwapUint64(&x.i, val, val+1) { + if !cas(&x.i, val, val+1) { t.Fatalf("should have swapped %#x %#x", val, val+1) } if x.i != val+1 { t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1) } x.i = val + 1 - if CompareAndSwapUint64(&x.i, val, val+2) { + if cas(&x.i, val, val+2) { t.Fatalf("should not have swapped %#x %#x", val, val+2) } if x.i != val+1 { @@ -273,6 +411,10 @@ func TestCompareAndSwapUint64(t *testing.T) { } } +func TestCompareAndSwapUint64(t *testing.T) { + testCompareAndSwapUint64(t, CompareAndSwapUint64) +} + func TestCompareAndSwapUintptr(t *testing.T) { var x struct { before uintptr @@ -608,27 +750,85 @@ func TestStorePointer(t *testing.T) { // uses the atomic operation to add 1 to a value. After running // multiple hammers in parallel, check that we end with the correct // total. - -var hammer32 = []struct { - name string - f func(*uint32, int) -}{ - {"AddInt32", hammerAddInt32}, - {"AddUint32", hammerAddUint32}, - {"AddUintptr", hammerAddUintptr32}, - {"CompareAndSwapInt32", hammerCompareAndSwapInt32}, - {"CompareAndSwapUint32", hammerCompareAndSwapUint32}, - {"CompareAndSwapUintptr", hammerCompareAndSwapUintptr32}, - {"CompareAndSwapPointer", hammerCompareAndSwapPointer32}, +// Swap can't add 1, so it uses a different scheme. +// The functions repeatedly generate a pseudo-random number such that +// low bits are equal to high bits, swap, check that the old value +// has low and high bits equal. + +var hammer32 = map[string]func(*uint32, int){ + "SwapInt32": hammerSwapInt32, + "SwapUint32": hammerSwapUint32, + "SwapUintptr": hammerSwapUintptr32, + "SwapPointer": hammerSwapPointer32, + "AddInt32": hammerAddInt32, + "AddUint32": hammerAddUint32, + "AddUintptr": hammerAddUintptr32, + "CompareAndSwapInt32": hammerCompareAndSwapInt32, + "CompareAndSwapUint32": hammerCompareAndSwapUint32, + "CompareAndSwapUintptr": hammerCompareAndSwapUintptr32, + "CompareAndSwapPointer": hammerCompareAndSwapPointer32, } func init() { var v uint64 = 1 << 50 if uintptr(v) != 0 { // 64-bit system; clear uintptr tests - hammer32[2].f = nil - hammer32[5].f = nil - hammer32[6].f = nil + delete(hammer32, "SwapUintptr") + delete(hammer32, "SwapPointer") + delete(hammer32, "AddUintptr") + delete(hammer32, "CompareAndSwapUintptr") + delete(hammer32, "CompareAndSwapPointer") + } +} + +func hammerSwapInt32(uaddr *uint32, count int) { + addr := (*int32)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uint32(seed+i)<<16 | uint32(seed+i)<<16>>16 + old := uint32(SwapInt32(addr, int32(new))) + if old>>16 != old<<16>>16 { + panic(fmt.Sprintf("SwapInt32 is not atomic: %v", old)) + } + } +} + +func hammerSwapUint32(addr *uint32, count int) { + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uint32(seed+i)<<16 | uint32(seed+i)<<16>>16 + old := SwapUint32(addr, new) + if old>>16 != old<<16>>16 { + panic(fmt.Sprintf("SwapUint32 is not atomic: %v", old)) + } + } +} + +func hammerSwapUintptr32(uaddr *uint32, count int) { + // only safe when uintptr is 32-bit. + // not called on 64-bit systems. + addr := (*uintptr)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uintptr(seed+i)<<16 | uintptr(seed+i)<<16>>16 + old := SwapUintptr(addr, new) + if old>>16 != old<<16>>16 { + panic(fmt.Sprintf("SwapUintptr is not atomic: %v", old)) + } + } +} + +func hammerSwapPointer32(uaddr *uint32, count int) { + // only safe when uintptr is 32-bit. + // not called on 64-bit systems. + addr := (*unsafe.Pointer)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uintptr(seed+i)<<16 | uintptr(seed+i)<<16>>16 + old := uintptr(SwapPointer(addr, unsafe.Pointer(new))) + if old>>16 != old<<16>>16 { + panic(fmt.Sprintf("SwapPointer is not atomic: %v", old)) + } } } @@ -713,47 +913,103 @@ func TestHammer32(t *testing.T) { } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(p)) - for _, tt := range hammer32 { - if tt.f == nil { - continue - } + for name, testf := range hammer32 { c := make(chan int) var val uint32 for i := 0; i < p; i++ { go func() { - tt.f(&val, n) - c <- 1 + defer func() { + if err := recover(); err != nil { + t.Error(err.(string)) + } + c <- 1 + }() + testf(&val, n) }() } for i := 0; i < p; i++ { <-c } - if val != uint32(n)*p { - t.Fatalf("%s: val=%d want %d", tt.name, val, n*p) + if !strings.HasPrefix(name, "Swap") && val != uint32(n)*p { + t.Fatalf("%s: val=%d want %d", name, val, n*p) } } } -var hammer64 = []struct { - name string - f func(*uint64, int) -}{ - {"AddInt64", hammerAddInt64}, - {"AddUint64", hammerAddUint64}, - {"AddUintptr", hammerAddUintptr64}, - {"CompareAndSwapInt64", hammerCompareAndSwapInt64}, - {"CompareAndSwapUint64", hammerCompareAndSwapUint64}, - {"CompareAndSwapUintptr", hammerCompareAndSwapUintptr64}, - {"CompareAndSwapPointer", hammerCompareAndSwapPointer64}, +var hammer64 = map[string]func(*uint64, int){ + "SwapInt64": hammerSwapInt64, + "SwapUint64": hammerSwapUint64, + "SwapUintptr": hammerSwapUintptr64, + "SwapPointer": hammerSwapPointer64, + "AddInt64": hammerAddInt64, + "AddUint64": hammerAddUint64, + "AddUintptr": hammerAddUintptr64, + "CompareAndSwapInt64": hammerCompareAndSwapInt64, + "CompareAndSwapUint64": hammerCompareAndSwapUint64, + "CompareAndSwapUintptr": hammerCompareAndSwapUintptr64, + "CompareAndSwapPointer": hammerCompareAndSwapPointer64, } func init() { var v uint64 = 1 << 50 if uintptr(v) == 0 { // 32-bit system; clear uintptr tests - hammer64[2].f = nil - hammer64[5].f = nil - hammer64[6].f = nil + delete(hammer64, "SwapUintptr") + delete(hammer64, "SwapPointer") + delete(hammer64, "AddUintptr") + delete(hammer64, "CompareAndSwapUintptr") + delete(hammer64, "CompareAndSwapPointer") + } +} + +func hammerSwapInt64(uaddr *uint64, count int) { + addr := (*int64)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uint64(seed+i)<<32 | uint64(seed+i)<<32>>32 + old := uint64(SwapInt64(addr, int64(new))) + if old>>32 != old<<32>>32 { + panic(fmt.Sprintf("SwapInt64 is not atomic: %v", old)) + } + } +} + +func hammerSwapUint64(addr *uint64, count int) { + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uint64(seed+i)<<32 | uint64(seed+i)<<32>>32 + old := SwapUint64(addr, new) + if old>>32 != old<<32>>32 { + panic(fmt.Sprintf("SwapUint64 is not atomic: %v", old)) + } + } +} + +func hammerSwapUintptr64(uaddr *uint64, count int) { + // only safe when uintptr is 64-bit. + // not called on 32-bit systems. + addr := (*uintptr)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uintptr(seed+i)<<32 | uintptr(seed+i)<<32>>32 + old := SwapUintptr(addr, new) + if old>>32 != old<<32>>32 { + panic(fmt.Sprintf("SwapUintptr is not atomic: %v", old)) + } + } +} + +func hammerSwapPointer64(uaddr *uint64, count int) { + // only safe when uintptr is 64-bit. + // not called on 32-bit systems. + addr := (*unsafe.Pointer)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uintptr(seed+i)<<32 | uintptr(seed+i)<<32>>32 + old := uintptr(SwapPointer(addr, unsafe.Pointer(new))) + if old>>32 != old<<32>>32 { + panic(fmt.Sprintf("SwapPointer is not atomic: %v", old)) + } } } @@ -841,23 +1097,25 @@ func TestHammer64(t *testing.T) { } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(p)) - for _, tt := range hammer64 { - if tt.f == nil { - continue - } + for name, testf := range hammer64 { c := make(chan int) var val uint64 for i := 0; i < p; i++ { go func() { - tt.f(&val, n) - c <- 1 + defer func() { + if err := recover(); err != nil { + t.Error(err.(string)) + } + c <- 1 + }() + testf(&val, n) }() } for i := 0; i < p; i++ { <-c } - if val != uint64(n)*p { - t.Fatalf("%s: val=%d want %d", tt.name, val, n*p) + if !strings.HasPrefix(name, "Swap") && val != uint64(n)*p { + t.Fatalf("%s: val=%d want %d", name, val, n*p) } } } @@ -1205,3 +1463,46 @@ func TestUnaligned64(t *testing.T) { shouldPanic(t, "CompareAndSwapUint64", func() { CompareAndSwapUint64(p, 1, 2) }) shouldPanic(t, "AddUint64", func() { AddUint64(p, 3) }) } + +func TestNilDeref(t *testing.T) { + funcs := [...]func(){ + func() { CompareAndSwapInt32(nil, 0, 0) }, + func() { CompareAndSwapInt64(nil, 0, 0) }, + func() { CompareAndSwapUint32(nil, 0, 0) }, + func() { CompareAndSwapUint64(nil, 0, 0) }, + func() { CompareAndSwapUintptr(nil, 0, 0) }, + func() { CompareAndSwapPointer(nil, nil, nil) }, + func() { SwapInt32(nil, 0) }, + func() { SwapUint32(nil, 0) }, + func() { SwapInt64(nil, 0) }, + func() { SwapUint64(nil, 0) }, + func() { SwapUintptr(nil, 0) }, + func() { SwapPointer(nil, nil) }, + func() { AddInt32(nil, 0) }, + func() { AddUint32(nil, 0) }, + func() { AddInt64(nil, 0) }, + func() { AddUint64(nil, 0) }, + func() { AddUintptr(nil, 0) }, + func() { LoadInt32(nil) }, + func() { LoadInt64(nil) }, + func() { LoadUint32(nil) }, + func() { LoadUint64(nil) }, + func() { LoadUintptr(nil) }, + func() { LoadPointer(nil) }, + func() { StoreInt32(nil, 0) }, + func() { StoreInt64(nil, 0) }, + func() { StoreUint32(nil, 0) }, + func() { StoreUint64(nil, 0) }, + func() { StoreUintptr(nil, 0) }, + func() { StorePointer(nil, nil) }, + } + for _, f := range funcs { + func() { + defer func() { + runtime.GC() + recover() + }() + f() + }() + } +} diff --git a/libgo/go/sync/atomic/doc.go b/libgo/go/sync/atomic/doc.go index 27a12c98488..17ba72fa171 100644 --- a/libgo/go/sync/atomic/doc.go +++ b/libgo/go/sync/atomic/doc.go @@ -13,6 +13,13 @@ // Share memory by communicating; // don't communicate by sharing memory. // +// The swap operation, implemented by the SwapT functions, is the atomic +// equivalent of: +// +// old = *addr +// *addr = new +// return old +// // The compare-and-swap operation, implemented by the CompareAndSwapT // functions, is the atomic equivalent of: // @@ -40,11 +47,31 @@ import ( // BUG(rsc): On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX. // +// On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core. +// // On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit // alignment of 64-bit words accessed atomically. The first word in a global // variable or in an allocated struct or slice can be relied upon to be // 64-bit aligned. +// SwapInt32 atomically stores new into *addr and returns the previous *addr value. +func SwapInt32(addr *int32, new int32) (old int32) + +// SwapInt64 atomically stores new into *addr and returns the previous *addr value. +func SwapInt64(addr *int64, new int64) (old int64) + +// SwapUint32 atomically stores new into *addr and returns the previous *addr value. +func SwapUint32(addr *uint32, new uint32) (old uint32) + +// SwapUint64 atomically stores new into *addr and returns the previous *addr value. +func SwapUint64(addr *uint64, new uint64) (old uint64) + +// SwapUintptr atomically stores new into *addr and returns the previous *addr value. +func SwapUintptr(addr *uintptr, new uintptr) (old uintptr) + +// SwapPointer atomically stores new into *addr and returns the previous *addr value. +func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer) + // CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value. func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool) @@ -67,12 +94,16 @@ func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapp func AddInt32(addr *int32, delta int32) (new int32) // AddUint32 atomically adds delta to *addr and returns the new value. +// To subtract a signed positive constant value c from x, do AddUint32(&x, ^uint32(c-1)). +// In particular, to decrement x, do AddUint32(&x, ^uint32(0)). func AddUint32(addr *uint32, delta uint32) (new uint32) // AddInt64 atomically adds delta to *addr and returns the new value. func AddInt64(addr *int64, delta int64) (new int64) // AddUint64 atomically adds delta to *addr and returns the new value. +// To subtract a signed positive constant value c from x, do AddUint64(&x, ^uint64(c-1)). +// In particular, to decrement x, do AddUint64(&x, ^uint64(0)). func AddUint64(addr *uint64, delta uint64) (new uint64) // AddUintptr atomically adds delta to *addr and returns the new value. diff --git a/libgo/go/sync/atomic/race.go b/libgo/go/sync/atomic/race.go index 2320b57070e..6cbbf12cb64 100644 --- a/libgo/go/sync/atomic/race.go +++ b/libgo/go/sync/atomic/race.go @@ -20,6 +20,54 @@ import ( var mtx uint32 = 1 // same for all +func SwapInt32(addr *int32, new int32) (old int32) { + return int32(SwapUint32((*uint32)(unsafe.Pointer(addr)), uint32(new))) +} + +func SwapUint32(addr *uint32, new uint32) (old uint32) { + _ = *addr + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + runtime.RaceAcquire(unsafe.Pointer(addr)) + old = *addr + *addr = new + runtime.RaceReleaseMerge(unsafe.Pointer(addr)) + runtime.RaceSemrelease(&mtx) + return +} + +func SwapInt64(addr *int64, new int64) (old int64) { + return int64(SwapUint64((*uint64)(unsafe.Pointer(addr)), uint64(new))) +} + +func SwapUint64(addr *uint64, new uint64) (old uint64) { + _ = *addr + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + runtime.RaceAcquire(unsafe.Pointer(addr)) + old = *addr + *addr = new + runtime.RaceReleaseMerge(unsafe.Pointer(addr)) + runtime.RaceSemrelease(&mtx) + return +} + +func SwapUintptr(addr *uintptr, new uintptr) (old uintptr) { + return uintptr(SwapPointer((*unsafe.Pointer)(unsafe.Pointer(addr)), unsafe.Pointer(new))) +} + +func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer) { + _ = *addr + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + runtime.RaceAcquire(unsafe.Pointer(addr)) + old = *addr + *addr = new + runtime.RaceReleaseMerge(unsafe.Pointer(addr)) + runtime.RaceSemrelease(&mtx) + return +} + func CompareAndSwapInt32(val *int32, old, new int32) bool { return CompareAndSwapUint32((*uint32)(unsafe.Pointer(val)), uint32(old), uint32(new)) } |

