Commit d6bf2d7b authored by Cherry Zhang's avatar Cherry Zhang

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/+/212627Reviewed-by: default avatarAustin Clements <austin@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 822094f0
......@@ -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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment