mirror of
https://github.com/golang/go
synced 2024-11-18 01:54:45 -07:00
runtime: test memmove writes pointers atomically
In the previous CL we ensures that memmove writes pointers atomically, so the concurrent GC won't observe a partially updated pointer. This CL adds a test. Change-Id: Icd1124bf3a15ef25bac20c7fb8933f1a642d897c Reviewed-on: https://go-review.googlesource.com/c/go/+/212627 Reviewed-by: Austin Clements <austin@google.com> Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
822094f047
commit
d6bf2d7b83
@ -45,6 +45,9 @@ var NetpollGenericInit = netpollGenericInit
|
||||
|
||||
var ParseRelease = parseRelease
|
||||
|
||||
var Memmove = memmove
|
||||
var MemclrNoHeapPointers = memclrNoHeapPointers
|
||||
|
||||
const PreemptMSupported = preemptMSupported
|
||||
|
||||
type LFNode struct {
|
||||
|
@ -11,7 +11,9 @@ import (
|
||||
"internal/race"
|
||||
"internal/testenv"
|
||||
. "runtime"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func TestMemmove(t *testing.T) {
|
||||
@ -206,6 +208,71 @@ func cmpb(a, b []byte) int {
|
||||
return l
|
||||
}
|
||||
|
||||
// Ensure that memmove writes pointers atomically, so the GC won't
|
||||
// observe a partially updated pointer.
|
||||
func TestMemmoveAtomicity(t *testing.T) {
|
||||
if race.Enabled {
|
||||
t.Skip("skip under the race detector -- this test is intentionally racy")
|
||||
}
|
||||
|
||||
var x int
|
||||
|
||||
for _, backward := range []bool{true, false} {
|
||||
for _, n := range []int{3, 4, 5, 6, 7, 8, 9, 10, 15, 25, 49} {
|
||||
n := n
|
||||
|
||||
// test copying [N]*int.
|
||||
sz := uintptr(n * PtrSize)
|
||||
name := fmt.Sprint(sz)
|
||||
if backward {
|
||||
name += "-backward"
|
||||
} else {
|
||||
name += "-forward"
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Use overlapping src and dst to force forward/backward copy.
|
||||
var s [100]*int
|
||||
src := s[n-1 : 2*n-1]
|
||||
dst := s[:n]
|
||||
if backward {
|
||||
src, dst = dst, src
|
||||
}
|
||||
for i := range src {
|
||||
src[i] = &x
|
||||
}
|
||||
for i := range dst {
|
||||
dst[i] = nil
|
||||
}
|
||||
|
||||
var ready uint32
|
||||
go func() {
|
||||
sp := unsafe.Pointer(&src[0])
|
||||
dp := unsafe.Pointer(&dst[0])
|
||||
atomic.StoreUint32(&ready, 1)
|
||||
for i := 0; i < 10000; i++ {
|
||||
Memmove(dp, sp, sz)
|
||||
MemclrNoHeapPointers(dp, sz)
|
||||
}
|
||||
atomic.StoreUint32(&ready, 2)
|
||||
}()
|
||||
|
||||
for atomic.LoadUint32(&ready) == 0 {
|
||||
Gosched()
|
||||
}
|
||||
|
||||
for atomic.LoadUint32(&ready) != 2 {
|
||||
for i := range dst {
|
||||
p := dst[i]
|
||||
if p != nil && p != &x {
|
||||
t.Fatalf("got partially updated pointer %p at dst[%d], want either nil or %p", p, i, &x)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkSizes(b *testing.B, sizes []int, fn func(b *testing.B, n int)) {
|
||||
for _, n := range sizes {
|
||||
b.Run(fmt.Sprint(n), func(b *testing.B) {
|
||||
|
Loading…
Reference in New Issue
Block a user