diff options
Diffstat (limited to 'src/pkg/runtime/mfinal_test.go')
-rw-r--r-- | src/pkg/runtime/mfinal_test.go | 175 |
1 files changed, 132 insertions, 43 deletions
diff --git a/src/pkg/runtime/mfinal_test.go b/src/pkg/runtime/mfinal_test.go index 6efef9bb0..6b53888ab 100644 --- a/src/pkg/runtime/mfinal_test.go +++ b/src/pkg/runtime/mfinal_test.go @@ -6,10 +6,9 @@ package runtime_test import ( "runtime" - "sync" - "sync/atomic" "testing" "time" + "unsafe" ) type Tintptr *int // assignable to *int @@ -46,13 +45,15 @@ func TestFinalizerType(t *testing.T) { } for _, tt := range finalizerTests { + done := make(chan bool, 1) go func() { v := new(int) *v = 97531 runtime.SetFinalizer(tt.convert(v), tt.finalizer) v = nil + done <- true }() - time.Sleep(1 * time.Second) + <-done runtime.GC() select { case <-ch: @@ -73,6 +74,7 @@ func TestFinalizerInterfaceBig(t *testing.T) { t.Skipf("Skipping on non-amd64 machine") } ch := make(chan bool) + done := make(chan bool, 1) go func() { v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"} old := *v @@ -87,8 +89,9 @@ func TestFinalizerInterfaceBig(t *testing.T) { close(ch) }) v = nil + done <- true }() - time.Sleep(1 * time.Second) + <-done runtime.GC() select { case <-ch: @@ -100,51 +103,137 @@ func TestFinalizerInterfaceBig(t *testing.T) { func fin(v *int) { } +// Verify we don't crash at least. golang.org/issue/6857 +func TestFinalizerZeroSizedStruct(t *testing.T) { + type Z struct{} + z := new(Z) + runtime.SetFinalizer(z, func(*Z) {}) +} + func BenchmarkFinalizer(b *testing.B) { - const CallsPerSched = 1000 - procs := runtime.GOMAXPROCS(-1) - N := int32(b.N / CallsPerSched) - var wg sync.WaitGroup - wg.Add(procs) - for p := 0; p < procs; p++ { - go func() { - var data [CallsPerSched]*int - for i := 0; i < CallsPerSched; i++ { - data[i] = new(int) + const Batch = 1000 + b.RunParallel(func(pb *testing.PB) { + var data [Batch]*int + for i := 0; i < Batch; i++ { + data[i] = new(int) + } + for pb.Next() { + for i := 0; i < Batch; i++ { + runtime.SetFinalizer(data[i], fin) } - for atomic.AddInt32(&N, -1) >= 0 { - runtime.Gosched() - for i := 0; i < CallsPerSched; i++ { - runtime.SetFinalizer(data[i], fin) - } - for i := 0; i < CallsPerSched; i++ { - runtime.SetFinalizer(data[i], nil) - } + for i := 0; i < Batch; i++ { + runtime.SetFinalizer(data[i], nil) } - wg.Done() - }() - } - wg.Wait() + } + }) } func BenchmarkFinalizerRun(b *testing.B) { - const CallsPerSched = 1000 - procs := runtime.GOMAXPROCS(-1) - N := int32(b.N / CallsPerSched) - var wg sync.WaitGroup - wg.Add(procs) - for p := 0; p < procs; p++ { - go func() { - for atomic.AddInt32(&N, -1) >= 0 { - runtime.Gosched() - for i := 0; i < CallsPerSched; i++ { - v := new(int) - runtime.SetFinalizer(v, fin) - } - runtime.GC() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + v := new(int) + runtime.SetFinalizer(v, fin) + } + }) +} + +// One chunk must be exactly one sizeclass in size. +// It should be a sizeclass not used much by others, so we +// have a greater chance of finding adjacent ones. +// size class 19: 320 byte objects, 25 per page, 1 page alloc at a time +const objsize = 320 + +type objtype [objsize]byte + +func adjChunks() (*objtype, *objtype) { + var s []*objtype + + for { + c := new(objtype) + for _, d := range s { + if uintptr(unsafe.Pointer(c))+unsafe.Sizeof(*c) == uintptr(unsafe.Pointer(d)) { + return c, d } - wg.Done() - }() + if uintptr(unsafe.Pointer(d))+unsafe.Sizeof(*c) == uintptr(unsafe.Pointer(c)) { + return d, c + } + } + s = append(s, c) + } +} + +// Make sure an empty slice on the stack doesn't pin the next object in memory. +func TestEmptySlice(t *testing.T) { + if true { // disable until bug 7564 is fixed. + return + } + x, y := adjChunks() + + // the pointer inside xs points to y. + xs := x[objsize:] // change objsize to objsize-1 and the test passes + + fin := make(chan bool, 1) + runtime.SetFinalizer(y, func(z *objtype) { fin <- true }) + runtime.GC() + select { + case <-fin: + case <-time.After(4 * time.Second): + t.Errorf("finalizer of next object in memory didn't run") + } + xsglobal = xs // keep empty slice alive until here +} + +var xsglobal []byte + +func adjStringChunk() (string, *objtype) { + b := make([]byte, objsize) + for { + s := string(b) + t := new(objtype) + p := *(*uintptr)(unsafe.Pointer(&s)) + q := uintptr(unsafe.Pointer(t)) + if p+objsize == q { + return s, t + } + } +} + +// Make sure an empty string on the stack doesn't pin the next object in memory. +func TestEmptyString(t *testing.T) { + x, y := adjStringChunk() + + ss := x[objsize:] // change objsize to objsize-1 and the test passes + fin := make(chan bool, 1) + // set finalizer on string contents of y + runtime.SetFinalizer(y, func(z *objtype) { fin <- true }) + runtime.GC() + select { + case <-fin: + case <-time.After(4 * time.Second): + t.Errorf("finalizer of next string in memory didn't run") } - wg.Wait() + ssglobal = ss // keep 0-length string live until here +} + +var ssglobal string + +// Test for issue 7656. +func TestFinalizerOnGlobal(t *testing.T) { + runtime.SetFinalizer(Foo1, func(p *Object1) {}) + runtime.SetFinalizer(Foo2, func(p *Object2) {}) + runtime.SetFinalizer(Foo1, nil) + runtime.SetFinalizer(Foo2, nil) } + +type Object1 struct { + Something []byte +} + +type Object2 struct { + Something byte +} + +var ( + Foo2 = &Object2{} + Foo1 = &Object1{} +) |