]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: update docs, code for SetFinalizer
authorRuss Cox <rsc@golang.org>
Mon, 6 Oct 2014 18:18:09 +0000 (14:18 -0400)
committerRuss Cox <rsc@golang.org>
Mon, 6 Oct 2014 18:18:09 +0000 (14:18 -0400)
At last minute before 1.3 we relaxed SetFinalizer to avoid
crashes when you pass the result of a global alloc to it.
This avoids the crash but makes SetFinalizer a bit too relaxed.

Document that the finalizer of a global allocation may not run.

Tighten the SetFinalizer check to ignore a global allocation but
not ignore everything else.

Fixes #7656.

LGTM=r, iant
R=golang-codereviews, iant, r
CC=dvyukov, golang-codereviews, khr, rlh
https://golang.org/cl/145930043

src/runtime/malloc.go
src/runtime/mfinal_test.go

index fc22cc29e44f6c787044159c13929e5f0387d0d7..99d14e31459a223a42cc2a322adfe8cd3a0a2cb2 100644 (file)
@@ -488,6 +488,10 @@ func GC() {
        gogc(2)
 }
 
+// linker-provided
+var noptrdata struct{}
+var enoptrbss struct{}
+
 // SetFinalizer sets the finalizer associated with x to f.
 // When the garbage collector finds an unreachable block
 // with an associated finalizer, it clears the association and runs
@@ -527,6 +531,10 @@ func GC() {
 // It is not guaranteed that a finalizer will run if the size of *x is
 // zero bytes.
 //
+// It is not guaranteed that a finalizer will run for objects allocated
+// in initializers for package-level variables. Such objects may be
+// linker-allocated, not heap-allocated.
+//
 // A single goroutine runs all finalizers for a program, sequentially.
 // If a finalizer must run for a long time, it should do so by starting
 // a new goroutine.
@@ -544,24 +552,25 @@ func SetFinalizer(obj interface{}, finalizer interface{}) {
                gothrow("nil elem type!")
        }
 
-       // As an implementation detail we do not run finalizers for zero-sized objects,
-       // because we use &runtime·zerobase for all such allocations.
-       if ot.elem.size == 0 {
-               return
-       }
-
        // find the containing object
        _, base, _ := findObject(e.data)
 
-       // The following check is required for cases when a user passes a pointer to composite
-       // literal, but compiler makes it a pointer to global. For example:
-       //      var Foo = &Object{}
-       //      func main() {
-       //              runtime.SetFinalizer(Foo, nil)
-       //      }
-       // See issue 7656.
        if base == nil {
-               return
+               // 0-length objects are okay.
+               if e.data == unsafe.Pointer(&zerobase) {
+                       return
+               }
+
+               // Global initializers might be linker-allocated.
+               //      var Foo = &Object{}
+               //      func main() {
+               //              runtime.SetFinalizer(Foo, nil)
+               //      }
+               // The segments are, in order: text, rodata, noptrdata, data, bss, noptrbss.
+               if uintptr(unsafe.Pointer(&noptrdata)) <= uintptr(e.data) && uintptr(e.data) < uintptr(unsafe.Pointer(&enoptrbss)) {
+                       return
+               }
+               gothrow("runtime.SetFinalizer: pointer not in allocated block")
        }
 
        if e.data != base {
index 6b53888ab6a276ab8947bf8e0837cf56dfb7297e..d2cead2876af9112eecf2494c3f47b37e5a5883f 100644 (file)
@@ -44,10 +44,17 @@ func TestFinalizerType(t *testing.T) {
                {func(x *int) interface{} { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }},
        }
 
-       for _, tt := range finalizerTests {
+       for i, tt := range finalizerTests {
                done := make(chan bool, 1)
                go func() {
-                       v := new(int)
+                       // allocate struct with pointer to avoid hitting tinyalloc.
+                       // Otherwise we can't be sure when the allocation will
+                       // be freed.
+                       type T struct {
+                               v int
+                               p unsafe.Pointer
+                       }
+                       v := &new(T).v
                        *v = 97531
                        runtime.SetFinalizer(tt.convert(v), tt.finalizer)
                        v = nil
@@ -58,7 +65,7 @@ func TestFinalizerType(t *testing.T) {
                select {
                case <-ch:
                case <-time.After(time.Second * 4):
-                       t.Errorf("finalizer for type %T didn't run", tt.finalizer)
+                       t.Errorf("#%d: finalizer for type %T didn't run", i, tt.finalizer)
                }
        }
 }