]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: test memmove writes pointers atomically
authorCherry Zhang <cherryyz@google.com>
Fri, 27 Dec 2019 17:02:50 +0000 (12:02 -0500)
committerCherry Zhang <cherryyz@google.com>
Mon, 6 Jan 2020 18:32:21 +0000 (18:32 +0000)
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>

src/runtime/export_test.go
src/runtime/memmove_test.go

index ce9c6a0ba742f5a684ab1f46e25448b2cab28f94..1f5533c90e6bdd0597190efd8c81bf2c2aa5e081 100644 (file)
@@ -45,6 +45,9 @@ var NetpollGenericInit = netpollGenericInit
 
 var ParseRelease = parseRelease
 
+var Memmove = memmove
+var MemclrNoHeapPointers = memclrNoHeapPointers
+
 const PreemptMSupported = preemptMSupported
 
 type LFNode struct {
index 0b2e19123d350cd7dac32fb469269f3fb1c93300..396c1304c56c397921c63e68f1fadf718d0b2df9 100644 (file)
@@ -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) {