mirror of
https://github.com/golang/go
synced 2024-09-25 07:20:12 -06:00
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
This commit is contained in:
parent
2560f8fe22
commit
5822e7848a
@ -130,7 +130,7 @@ func funcentry_go(*Func) uintptr
|
|||||||
// The argument x must be a pointer to an object allocated by
|
// The argument x must be a pointer to an object allocated by
|
||||||
// calling new or by taking the address of a composite literal.
|
// calling new or by taking the address of a composite literal.
|
||||||
// The argument f must be a function that takes a single argument
|
// The argument f must be a function that takes a single argument
|
||||||
// of x's type or interface{}, and can have arbitrary ignored return
|
// to which x's type can be assigned, and can have arbitrary ignored return
|
||||||
// values. If either of these is not true, SetFinalizer aborts the
|
// values. If either of these is not true, SetFinalizer aborts the
|
||||||
// program.
|
// program.
|
||||||
//
|
//
|
||||||
|
@ -482,6 +482,16 @@ runtime·ifaceE2I(InterfaceType *inter, Eface e, Iface *ret)
|
|||||||
ret->tab = itab(inter, t, 0);
|
ret->tab = itab(inter, t, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
runtime·ifaceE2I2(InterfaceType *inter, Eface e, Iface *ret)
|
||||||
|
{
|
||||||
|
ret->tab = itab(inter, e.type, 1);
|
||||||
|
if(ret->tab == nil)
|
||||||
|
return false;
|
||||||
|
ret->data = e.data;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// For reflect
|
// For reflect
|
||||||
// func ifaceE2I(t *InterfaceType, e interface{}, dst *Iface)
|
// func ifaceE2I(t *InterfaceType, e interface{}, dst *Iface)
|
||||||
void
|
void
|
||||||
|
@ -741,6 +741,7 @@ func SetFinalizer(obj Eface, finalizer Eface) {
|
|||||||
Type *t;
|
Type *t;
|
||||||
Type *fint;
|
Type *fint;
|
||||||
PtrType *ot;
|
PtrType *ot;
|
||||||
|
Iface iface;
|
||||||
|
|
||||||
if(obj.type == nil) {
|
if(obj.type == nil) {
|
||||||
runtime·printf("runtime.SetFinalizer: first argument is nil interface\n");
|
runtime·printf("runtime.SetFinalizer: first argument is nil interface\n");
|
||||||
@ -755,7 +756,8 @@ func SetFinalizer(obj Eface, finalizer Eface) {
|
|||||||
goto throw;
|
goto throw;
|
||||||
}
|
}
|
||||||
nret = 0;
|
nret = 0;
|
||||||
ot = nil;
|
ot = (PtrType*)obj.type;
|
||||||
|
fint = nil;
|
||||||
if(finalizer.type != nil) {
|
if(finalizer.type != nil) {
|
||||||
if(finalizer.type->kind != KindFunc)
|
if(finalizer.type->kind != KindFunc)
|
||||||
goto badfunc;
|
goto badfunc;
|
||||||
@ -763,9 +765,16 @@ func SetFinalizer(obj Eface, finalizer Eface) {
|
|||||||
if(ft->dotdotdot || ft->in.len != 1)
|
if(ft->dotdotdot || ft->in.len != 1)
|
||||||
goto badfunc;
|
goto badfunc;
|
||||||
fint = *(Type**)ft->in.array;
|
fint = *(Type**)ft->in.array;
|
||||||
if(fint->kind == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0)
|
if(fint == obj.type) {
|
||||||
ot = (PtrType*)obj.type;
|
// ok - same type
|
||||||
else if(fint != obj.type)
|
} else if(fint->kind == KindPtr && (fint->x == nil || fint->x->name == nil || obj.type->x == nil || obj.type->x->name == nil) && ((PtrType*)fint)->elem == ((PtrType*)obj.type)->elem) {
|
||||||
|
// ok - not same type, but both pointers,
|
||||||
|
// one or the other is unnamed, and same element type, so assignable.
|
||||||
|
} else if(fint->kind == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0) {
|
||||||
|
// ok - satisfies empty interface
|
||||||
|
} else if(fint->kind == KindInterface && runtime·ifaceE2I2((InterfaceType*)fint, obj, &iface)) {
|
||||||
|
// ok - satisfies non-empty interface
|
||||||
|
} else
|
||||||
goto badfunc;
|
goto badfunc;
|
||||||
|
|
||||||
// compute size needed for return parameters
|
// compute size needed for return parameters
|
||||||
@ -776,14 +785,14 @@ func SetFinalizer(obj Eface, finalizer Eface) {
|
|||||||
nret = ROUND(nret, sizeof(void*));
|
nret = ROUND(nret, sizeof(void*));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!runtime·addfinalizer(obj.data, finalizer.data, nret, ot)) {
|
if(!runtime·addfinalizer(obj.data, finalizer.data, nret, fint, ot)) {
|
||||||
runtime·printf("runtime.SetFinalizer: finalizer already set\n");
|
runtime·printf("runtime.SetFinalizer: finalizer already set\n");
|
||||||
goto throw;
|
goto throw;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
badfunc:
|
badfunc:
|
||||||
runtime·printf("runtime.SetFinalizer: second argument is %S, not func(%S) or func(interface{})\n", *finalizer.type->string, *obj.type->string);
|
runtime·printf("runtime.SetFinalizer: cannot pass %S to finalizer %S\n", *obj.type->string, *finalizer.type->string);
|
||||||
throw:
|
throw:
|
||||||
runtime·throw("runtime.SetFinalizer");
|
runtime·throw("runtime.SetFinalizer");
|
||||||
}
|
}
|
||||||
|
@ -481,7 +481,6 @@ int32 runtime·gcprocs(void);
|
|||||||
void runtime·helpgc(int32 nproc);
|
void runtime·helpgc(int32 nproc);
|
||||||
void runtime·gchelper(void);
|
void runtime·gchelper(void);
|
||||||
|
|
||||||
bool runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, void **ot);
|
|
||||||
void runtime·walkfintab(void (*fn)(void*));
|
void runtime·walkfintab(void (*fn)(void*));
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
#include "arch_GOARCH.h"
|
#include "arch_GOARCH.h"
|
||||||
#include "malloc.h"
|
#include "malloc.h"
|
||||||
|
#include "type.h"
|
||||||
|
|
||||||
enum { debug = 0 };
|
enum { debug = 0 };
|
||||||
|
|
||||||
@ -13,7 +14,8 @@ struct Fin
|
|||||||
{
|
{
|
||||||
FuncVal *fn;
|
FuncVal *fn;
|
||||||
uintptr nret;
|
uintptr nret;
|
||||||
void *ot;
|
Type *fint;
|
||||||
|
PtrType *ot;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Finalizer hash table. Direct hash, linear scan, at most 3/4 full.
|
// Finalizer hash table. Direct hash, linear scan, at most 3/4 full.
|
||||||
@ -43,7 +45,7 @@ static struct {
|
|||||||
} fintab[TABSZ];
|
} fintab[TABSZ];
|
||||||
|
|
||||||
static void
|
static void
|
||||||
addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret, void *ot)
|
addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret, Type *fint, PtrType *ot)
|
||||||
{
|
{
|
||||||
int32 i, j;
|
int32 i, j;
|
||||||
|
|
||||||
@ -68,6 +70,7 @@ ret:
|
|||||||
t->key[i] = k;
|
t->key[i] = k;
|
||||||
t->val[i].fn = fn;
|
t->val[i].fn = fn;
|
||||||
t->val[i].nret = nret;
|
t->val[i].nret = nret;
|
||||||
|
t->val[i].fint = fint;
|
||||||
t->val[i].ot = ot;
|
t->val[i].ot = ot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +129,7 @@ resizefintab(Fintab *tab)
|
|||||||
for(i=0; i<tab->max; i++) {
|
for(i=0; i<tab->max; i++) {
|
||||||
k = tab->key[i];
|
k = tab->key[i];
|
||||||
if(k != nil && k != (void*)-1)
|
if(k != nil && k != (void*)-1)
|
||||||
addfintab(&newtab, k, tab->val[i].fn, tab->val[i].nret, tab->val[i].ot);
|
addfintab(&newtab, k, tab->val[i].fn, tab->val[i].nret, tab->val[i].fint, tab->val[i].ot);
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime·free(tab->key);
|
runtime·free(tab->key);
|
||||||
@ -140,7 +143,7 @@ resizefintab(Fintab *tab)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, void *ot)
|
runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, Type *fint, PtrType *ot)
|
||||||
{
|
{
|
||||||
Fintab *tab;
|
Fintab *tab;
|
||||||
byte *base;
|
byte *base;
|
||||||
@ -169,7 +172,7 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, void *ot)
|
|||||||
resizefintab(tab);
|
resizefintab(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
addfintab(tab, p, f, nret, ot);
|
addfintab(tab, p, f, nret, fint, ot);
|
||||||
runtime·setblockspecial(p, true);
|
runtime·setblockspecial(p, true);
|
||||||
runtime·unlock(tab);
|
runtime·unlock(tab);
|
||||||
return true;
|
return true;
|
||||||
@ -178,7 +181,7 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, void *ot)
|
|||||||
// get finalizer; if del, delete finalizer.
|
// get finalizer; if del, delete finalizer.
|
||||||
// caller is responsible for updating RefHasFinalizer (special) bit.
|
// caller is responsible for updating RefHasFinalizer (special) bit.
|
||||||
bool
|
bool
|
||||||
runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, void **ot)
|
runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, Type **fint, PtrType **ot)
|
||||||
{
|
{
|
||||||
Fintab *tab;
|
Fintab *tab;
|
||||||
bool res;
|
bool res;
|
||||||
@ -192,6 +195,7 @@ runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, void **ot)
|
|||||||
return false;
|
return false;
|
||||||
*fn = f.fn;
|
*fn = f.fn;
|
||||||
*nret = f.nret;
|
*nret = f.nret;
|
||||||
|
*fint = f.fint;
|
||||||
*ot = f.ot;
|
*ot = f.ot;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -12,55 +12,52 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFinalizerTypeSucceed(t *testing.T) {
|
type Tintptr *int // assignable to *int
|
||||||
if runtime.GOARCH != "amd64" {
|
type Tint int // *Tint implements Tinter, interface{}
|
||||||
t.Skipf("Skipping on non-amd64 machine")
|
|
||||||
}
|
func (t *Tint) m() {}
|
||||||
ch := make(chan bool)
|
|
||||||
func() {
|
type Tinter interface {
|
||||||
v := new(int)
|
m()
|
||||||
*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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFinalizerInterface(t *testing.T) {
|
func TestFinalizerType(t *testing.T) {
|
||||||
if runtime.GOARCH != "amd64" {
|
if runtime.GOARCH != "amd64" {
|
||||||
t.Skipf("Skipping on non-amd64 machine")
|
t.Skipf("Skipping on non-amd64 machine")
|
||||||
}
|
}
|
||||||
ch := make(chan bool)
|
|
||||||
|
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() {
|
func() {
|
||||||
v := new(int)
|
v := new(int)
|
||||||
*v = 97531
|
*v = 97531
|
||||||
runtime.SetFinalizer(v, func(v interface{}) {
|
runtime.SetFinalizer(tt.convert(v), tt.finalizer)
|
||||||
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
|
v = nil
|
||||||
}()
|
}()
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
select {
|
select {
|
||||||
case <-ch:
|
case <-ch:
|
||||||
case <-time.After(time.Second * 4):
|
case <-time.After(time.Second * 4):
|
||||||
t.Errorf("Finalizer set by SetFinalizer(*int, func(interface{})) didn't run")
|
t.Errorf("Finalizer of type %T didn't run", tt.finalizer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +112,7 @@ struct Finalizer
|
|||||||
FuncVal *fn;
|
FuncVal *fn;
|
||||||
void *arg;
|
void *arg;
|
||||||
uintptr nret;
|
uintptr nret;
|
||||||
|
Type *fint;
|
||||||
PtrType *ot;
|
PtrType *ot;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1607,10 +1608,11 @@ handlespecial(byte *p, uintptr size)
|
|||||||
FuncVal *fn;
|
FuncVal *fn;
|
||||||
uintptr nret;
|
uintptr nret;
|
||||||
PtrType *ot;
|
PtrType *ot;
|
||||||
|
Type *fint;
|
||||||
FinBlock *block;
|
FinBlock *block;
|
||||||
Finalizer *f;
|
Finalizer *f;
|
||||||
|
|
||||||
if(!runtime·getfinalizer(p, true, &fn, &nret, &ot)) {
|
if(!runtime·getfinalizer(p, true, &fn, &nret, &fint, &ot)) {
|
||||||
runtime·setblockspecial(p, false);
|
runtime·setblockspecial(p, false);
|
||||||
runtime·MProf_Free(p, size);
|
runtime·MProf_Free(p, size);
|
||||||
return false;
|
return false;
|
||||||
@ -1633,6 +1635,7 @@ handlespecial(byte *p, uintptr size)
|
|||||||
finq->cnt++;
|
finq->cnt++;
|
||||||
f->fn = fn;
|
f->fn = fn;
|
||||||
f->nret = nret;
|
f->nret = nret;
|
||||||
|
f->fint = fint;
|
||||||
f->ot = ot;
|
f->ot = ot;
|
||||||
f->arg = p;
|
f->arg = p;
|
||||||
runtime·unlock(&finlock);
|
runtime·unlock(&finlock);
|
||||||
@ -2297,7 +2300,7 @@ runfinq(void)
|
|||||||
FinBlock *fb, *next;
|
FinBlock *fb, *next;
|
||||||
byte *frame;
|
byte *frame;
|
||||||
uint32 framesz, framecap, i;
|
uint32 framesz, framecap, i;
|
||||||
Eface *ef;
|
Eface *ef, ef1;
|
||||||
|
|
||||||
frame = nil;
|
frame = nil;
|
||||||
framecap = 0;
|
framecap = 0;
|
||||||
@ -2327,12 +2330,22 @@ runfinq(void)
|
|||||||
frame = runtime·mallocgc(framesz, 0, FlagNoPointers|FlagNoInvokeGC);
|
frame = runtime·mallocgc(framesz, 0, FlagNoPointers|FlagNoInvokeGC);
|
||||||
framecap = framesz;
|
framecap = framesz;
|
||||||
}
|
}
|
||||||
if(f->ot == nil)
|
if(f->fint == nil)
|
||||||
|
runtime·throw("missing type in runfinq");
|
||||||
|
if(f->fint->kind == KindPtr) {
|
||||||
|
// direct use of pointer
|
||||||
*(void**)frame = f->arg;
|
*(void**)frame = f->arg;
|
||||||
else {
|
} else if(((InterfaceType*)f->fint)->mhdr.len == 0) {
|
||||||
|
// convert to empty interface
|
||||||
ef = (Eface*)frame;
|
ef = (Eface*)frame;
|
||||||
ef->type = f->ot;
|
ef->type = f->ot;
|
||||||
ef->data = f->arg;
|
ef->data = f->arg;
|
||||||
|
} else {
|
||||||
|
// convert to interface with methods, via empty interface.
|
||||||
|
ef1.type = f->ot;
|
||||||
|
ef1.data = f->arg;
|
||||||
|
if(!runtime·ifaceE2I2((InterfaceType*)f->fint, ef1, (Iface*)frame))
|
||||||
|
runtime·throw("invalid type conversion in runfinq");
|
||||||
}
|
}
|
||||||
reflect·call(f->fn, frame, framesz);
|
reflect·call(f->fn, frame, framesz);
|
||||||
f->fn = nil;
|
f->fn = nil;
|
||||||
|
@ -810,7 +810,6 @@ uintptr runtime·ifacehash(Iface, uintptr);
|
|||||||
uintptr runtime·efacehash(Eface, uintptr);
|
uintptr runtime·efacehash(Eface, uintptr);
|
||||||
void* runtime·malloc(uintptr size);
|
void* runtime·malloc(uintptr size);
|
||||||
void runtime·free(void *v);
|
void runtime·free(void *v);
|
||||||
bool runtime·addfinalizer(void*, FuncVal *fn, uintptr, void*);
|
|
||||||
void runtime·runpanic(Panic*);
|
void runtime·runpanic(Panic*);
|
||||||
uintptr runtime·getcallersp(void*);
|
uintptr runtime·getcallersp(void*);
|
||||||
int32 runtime·mcount(void);
|
int32 runtime·mcount(void);
|
||||||
@ -1046,7 +1045,7 @@ bool runtime·showframe(Func*, G*);
|
|||||||
void runtime·printcreatedby(G*);
|
void runtime·printcreatedby(G*);
|
||||||
|
|
||||||
void runtime·ifaceE2I(InterfaceType*, Eface, Iface*);
|
void runtime·ifaceE2I(InterfaceType*, Eface, Iface*);
|
||||||
|
bool runtime·ifaceE2I2(InterfaceType*, Eface, Iface*);
|
||||||
uintptr runtime·memlimit(void);
|
uintptr runtime·memlimit(void);
|
||||||
|
|
||||||
// float.c
|
// float.c
|
||||||
|
@ -98,3 +98,7 @@ struct PtrType
|
|||||||
Type;
|
Type;
|
||||||
Type *elem;
|
Type *elem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Here instead of in runtime.h because it uses the type names.
|
||||||
|
bool runtime·addfinalizer(void*, FuncVal *fn, uintptr, Type*, PtrType*);
|
||||||
|
bool runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, Type**, PtrType**);
|
||||||
|
Loading…
Reference in New Issue
Block a user