summaryrefslogtreecommitdiff
path: root/src/pkg/runtime/mfinal_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/runtime/mfinal_test.go')
-rw-r--r--src/pkg/runtime/mfinal_test.go175
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{}
+)