mirror of
https://github.com/golang/go
synced 2024-11-18 16:54:43 -07:00
runtime: update docs, code for SetFinalizer
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
This commit is contained in:
parent
609d996fac
commit
9a5b055b95
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user