runtime: make SetFinalizer(x, f) accept any f for which f(x) is valid
Originally the requirement was f(x) where f's argument is
exactly x's type.
CL 11858043 relaxed the requirement in a non-standard
way: f's argument must be exactly x's type or interface{}.
If we're going to relax the requirement, it should be done
in a way consistent with the rest of Go. This CL allows f's
argument to have any type for which x is assignable;
that's the same requirement the compiler would impose
if compiling f(x) directly.
Fixes #5368.
R=dvyukov, bradfitz, pieter
CC=golang-dev
https://golang.org/cl/12895043
diff --git a/src/pkg/runtime/mfinal_test.go b/src/pkg/runtime/mfinal_test.go
index 98874a5..0d9b41b 100644
--- a/src/pkg/runtime/mfinal_test.go
+++ b/src/pkg/runtime/mfinal_test.go
@@ -12,55 +12,52 @@
"time"
)
-func TestFinalizerTypeSucceed(t *testing.T) {
- if runtime.GOARCH != "amd64" {
- t.Skipf("Skipping on non-amd64 machine")
- }
- ch := make(chan bool)
- func() {
- v := new(int)
- *v = 97531
- runtime.SetFinalizer(v, func(v *int) {
- if *v != 97531 {
- t.Errorf("*int in finalizer has the wrong value: %d\n", *v)
- }
- close(ch)
- })
- v = nil
- }()
- runtime.GC()
- select {
- case <-ch:
- case <-time.After(time.Second * 4):
- t.Errorf("Finalizer set by SetFinalizer(*int, func(*int)) didn't run")
- }
+type Tintptr *int // assignable to *int
+type Tint int // *Tint implements Tinter, interface{}
+
+func (t *Tint) m() {}
+
+type Tinter interface {
+ m()
}
-func TestFinalizerInterface(t *testing.T) {
+func TestFinalizerType(t *testing.T) {
if runtime.GOARCH != "amd64" {
t.Skipf("Skipping on non-amd64 machine")
}
- ch := make(chan bool)
- func() {
- v := new(int)
- *v = 97531
- runtime.SetFinalizer(v, func(v interface{}) {
- i, ok := v.(*int)
- if !ok {
- t.Errorf("Expected *int from interface{} in finalizer, got %v", *i)
- }
- if *i != 97531 {
- t.Errorf("*int from interface{} has the wrong value: %d\n", *i)
- }
- close(ch)
- })
- v = nil
- }()
- runtime.GC()
- select {
- case <-ch:
- case <-time.After(time.Second * 4):
- t.Errorf("Finalizer set by SetFinalizer(*int, func(interface{})) didn't run")
+
+ ch := make(chan bool, 10)
+ finalize := func(x *int) {
+ if *x != 97531 {
+ t.Errorf("finalizer %d, want %d", *x, 97531)
+ }
+ ch <- true
+ }
+
+ var finalizerTests = []struct {
+ convert func(*int) interface{}
+ finalizer interface{}
+ }{
+ {func(x *int) interface{} { return x }, func(v *int) { finalize(v) }},
+ {func(x *int) interface{} { return Tintptr(x) }, func(v Tintptr) { finalize(v) }},
+ {func(x *int) interface{} { return Tintptr(x) }, func(v *int) { finalize(v) }},
+ {func(x *int) interface{} { return (*Tint)(x) }, func(v *Tint) { finalize((*int)(v)) }},
+ {func(x *int) interface{} { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }},
+ }
+
+ for _, tt := range finalizerTests {
+ func() {
+ v := new(int)
+ *v = 97531
+ runtime.SetFinalizer(tt.convert(v), tt.finalizer)
+ v = nil
+ }()
+ runtime.GC()
+ select {
+ case <-ch:
+ case <-time.After(time.Second * 4):
+ t.Errorf("Finalizer of type %T didn't run", tt.finalizer)
+ }
}
}