aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/runtime/chan_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/runtime/chan_test.go')
-rw-r--r--libgo/go/runtime/chan_test.go109
1 files changed, 107 insertions, 2 deletions
diff --git a/libgo/go/runtime/chan_test.go b/libgo/go/runtime/chan_test.go
index 6553509..4bd061d 100644
--- a/libgo/go/runtime/chan_test.go
+++ b/libgo/go/runtime/chan_test.go
@@ -578,7 +578,7 @@ func TestSelectDuplicateChannel(t *testing.T) {
}
e <- 9
}()
- time.Sleep(time.Millisecond) // make sure goroutine A gets qeueued first on c
+ time.Sleep(time.Millisecond) // make sure goroutine A gets queued first on c
// goroutine B
go func() {
@@ -591,6 +591,84 @@ func TestSelectDuplicateChannel(t *testing.T) {
c <- 8 // wake up B. This operation used to fail because c.recvq was corrupted (it tries to wake up an already running G instead of B)
}
+var selectSink interface{}
+
+func TestSelectStackAdjust(t *testing.T) {
+ // Test that channel receive slots that contain local stack
+ // pointers are adjusted correctly by stack shrinking.
+ c := make(chan *int)
+ d := make(chan *int)
+ ready1 := make(chan bool)
+ ready2 := make(chan bool)
+
+ f := func(ready chan bool, dup bool) {
+ // Temporarily grow the stack to 10K.
+ stackGrowthRecursive((10 << 10) / (128 * 8))
+
+ // We're ready to trigger GC and stack shrink.
+ ready <- true
+
+ val := 42
+ var cx *int
+ cx = &val
+
+ var c2 chan *int
+ var d2 chan *int
+ if dup {
+ c2 = c
+ d2 = d
+ }
+
+ // Receive from d. cx won't be affected.
+ select {
+ case cx = <-c:
+ case <-c2:
+ case <-d:
+ case <-d2:
+ }
+
+ // Check that pointer in cx was adjusted correctly.
+ if cx != &val {
+ t.Error("cx no longer points to val")
+ } else if val != 42 {
+ t.Error("val changed")
+ } else {
+ *cx = 43
+ if val != 43 {
+ t.Error("changing *cx failed to change val")
+ }
+ }
+ ready <- true
+ }
+
+ go f(ready1, false)
+ go f(ready2, true)
+
+ // Let the goroutines get into the select.
+ <-ready1
+ <-ready2
+ time.Sleep(10 * time.Millisecond)
+
+ // Force concurrent GC a few times.
+ var before, after runtime.MemStats
+ runtime.ReadMemStats(&before)
+ for i := 0; i < 100; i++ {
+ selectSink = new([1 << 20]byte)
+ runtime.ReadMemStats(&after)
+ if after.NumGC-before.NumGC >= 2 {
+ goto done
+ }
+ }
+ t.Fatal("failed to trigger concurrent GC")
+done:
+ selectSink = nil
+
+ // Wake selects.
+ close(d)
+ <-ready1
+ <-ready2
+}
+
func BenchmarkChanNonblocking(b *testing.B) {
myc := make(chan int)
b.RunParallel(func(pb *testing.PB) {
@@ -721,7 +799,7 @@ func BenchmarkChanContended(b *testing.B) {
})
}
-func BenchmarkChanSync(b *testing.B) {
+func benchmarkChanSync(b *testing.B, work int) {
const CallsPerSched = 1000
procs := 2
N := int32(b.N / CallsPerSched / procs * procs)
@@ -737,10 +815,14 @@ func BenchmarkChanSync(b *testing.B) {
for g := 0; g < CallsPerSched; g++ {
if i%2 == 0 {
<-myc
+ localWork(work)
myc <- 0
+ localWork(work)
} else {
myc <- 0
+ localWork(work)
<-myc
+ localWork(work)
}
}
}
@@ -752,6 +834,14 @@ func BenchmarkChanSync(b *testing.B) {
}
}
+func BenchmarkChanSync(b *testing.B) {
+ benchmarkChanSync(b, 0)
+}
+
+func BenchmarkChanSyncWork(b *testing.B) {
+ benchmarkChanSync(b, 1000)
+}
+
func benchmarkChanProdCons(b *testing.B, chanSize, localWork int) {
const CallsPerSched = 1000
procs := runtime.GOMAXPROCS(-1)
@@ -925,3 +1015,18 @@ func BenchmarkChanPopular(b *testing.B) {
}
wg.Wait()
}
+
+var (
+ alwaysFalse = false
+ workSink = 0
+)
+
+func localWork(w int) {
+ foo := 0
+ for i := 0; i < w; i++ {
+ foo /= (foo + 1)
+ }
+ if alwaysFalse {
+ workSink += foo
+ }
+}