Commit 6350e458 authored by Pieter Droogendijk's avatar Pieter Droogendijk Committed by Dmitriy Vyukov

runtime: allow SetFinalizer with a func(interface{})

Fixes #5368.

R=golang-dev, dvyukov
CC=golang-dev, rsc
https://golang.org/cl/11858043
parent 3398322d
...@@ -122,8 +122,9 @@ func funcentry_go(*Func) uintptr ...@@ -122,8 +122,9 @@ 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 and can have arbitrary ignored return values. // of x's type or interface{}, and can have arbitrary ignored return
// If either of these is not true, SetFinalizer aborts the program. // values. If either of these is not true, SetFinalizer aborts the
// program.
// //
// Finalizers are run in dependency order: if A points at B, both have // Finalizers are run in dependency order: if A points at B, both have
// finalizers, and they are otherwise unreachable, only the finalizer // finalizers, and they are otherwise unreachable, only the finalizer
......
...@@ -799,6 +799,8 @@ func SetFinalizer(obj Eface, finalizer Eface) { ...@@ -799,6 +799,8 @@ func SetFinalizer(obj Eface, finalizer Eface) {
int32 i; int32 i;
uintptr nret; uintptr nret;
Type *t; Type *t;
Type *fint;
PtrType *ot;
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");
...@@ -813,11 +815,17 @@ func SetFinalizer(obj Eface, finalizer Eface) { ...@@ -813,11 +815,17 @@ func SetFinalizer(obj Eface, finalizer Eface) {
goto throw; goto throw;
} }
nret = 0; nret = 0;
ot = nil;
if(finalizer.type != nil) { if(finalizer.type != nil) {
if(finalizer.type->kind != KindFunc) if(finalizer.type->kind != KindFunc)
goto badfunc; goto badfunc;
ft = (FuncType*)finalizer.type; ft = (FuncType*)finalizer.type;
if(ft->dotdotdot || ft->in.len != 1 || *(Type**)ft->in.array != obj.type) if(ft->dotdotdot || ft->in.len != 1)
goto badfunc;
fint = *(Type**)ft->in.array;
if(fint->kind == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0)
ot = (PtrType*)obj.type;
else if(fint != obj.type)
goto badfunc; goto badfunc;
// compute size needed for return parameters // compute size needed for return parameters
...@@ -828,14 +836,14 @@ func SetFinalizer(obj Eface, finalizer Eface) { ...@@ -828,14 +836,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)) { if(!runtime·addfinalizer(obj.data, finalizer.data, nret, 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)\n", *finalizer.type->string, *obj.type->string); runtime·printf("runtime.SetFinalizer: second argument is %S, not func(%S) or func(interface{})\n", *finalizer.type->string, *obj.type->string);
throw: throw:
runtime·throw("runtime.SetFinalizer"); runtime·throw("runtime.SetFinalizer");
} }
...@@ -480,7 +480,7 @@ int32 runtime·gcprocs(void); ...@@ -480,7 +480,7 @@ 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); 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
......
...@@ -13,6 +13,7 @@ struct Fin ...@@ -13,6 +13,7 @@ struct Fin
{ {
FuncVal *fn; FuncVal *fn;
uintptr nret; uintptr nret;
void *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.
...@@ -42,7 +43,7 @@ static struct { ...@@ -42,7 +43,7 @@ static struct {
} fintab[TABSZ]; } fintab[TABSZ];
static void static void
addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret) addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret, void *ot)
{ {
int32 i, j; int32 i, j;
...@@ -67,6 +68,7 @@ ret: ...@@ -67,6 +68,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].ot = ot;
} }
static bool static bool
...@@ -87,6 +89,7 @@ lookfintab(Fintab *t, void *k, bool del, Fin *f) ...@@ -87,6 +89,7 @@ lookfintab(Fintab *t, void *k, bool del, Fin *f)
t->key[i] = (void*)-1; t->key[i] = (void*)-1;
t->val[i].fn = nil; t->val[i].fn = nil;
t->val[i].nret = 0; t->val[i].nret = 0;
t->val[i].ot = nil;
t->ndead++; t->ndead++;
} }
return true; return true;
...@@ -123,7 +126,7 @@ resizefintab(Fintab *tab) ...@@ -123,7 +126,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); addfintab(&newtab, k, tab->val[i].fn, tab->val[i].nret, tab->val[i].ot);
} }
runtime·free(tab->key); runtime·free(tab->key);
...@@ -137,7 +140,7 @@ resizefintab(Fintab *tab) ...@@ -137,7 +140,7 @@ resizefintab(Fintab *tab)
} }
bool bool
runtime·addfinalizer(void *p, FuncVal *f, uintptr nret) runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, void *ot)
{ {
Fintab *tab; Fintab *tab;
byte *base; byte *base;
...@@ -166,7 +169,7 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret) ...@@ -166,7 +169,7 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
resizefintab(tab); resizefintab(tab);
} }
addfintab(tab, p, f, nret); addfintab(tab, p, f, nret, ot);
runtime·setblockspecial(p, true); runtime·setblockspecial(p, true);
runtime·unlock(tab); runtime·unlock(tab);
return true; return true;
...@@ -175,7 +178,7 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret) ...@@ -175,7 +178,7 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
// 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) runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, void **ot)
{ {
Fintab *tab; Fintab *tab;
bool res; bool res;
...@@ -189,6 +192,7 @@ runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret) ...@@ -189,6 +192,7 @@ runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret)
return false; return false;
*fn = f.fn; *fn = f.fn;
*nret = f.nret; *nret = f.nret;
*ot = f.ot;
return true; return true;
} }
......
...@@ -9,8 +9,94 @@ import ( ...@@ -9,8 +9,94 @@ import (
"sync" "sync"
"sync/atomic" "sync/atomic"
"testing" "testing"
"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")
}
}
func TestFinalizerInterface(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")
}
}
type bigValue struct {
fill uint64
it bool
up string
}
func TestFinalizerInterfaceBig(t *testing.T) {
if runtime.GOARCH != "amd64" {
t.Skipf("Skipping on non-amd64 machine")
}
ch := make(chan bool)
func() {
v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"}
runtime.SetFinalizer(v, func(v interface{}) {
i, ok := v.(*bigValue)
if !ok {
t.Errorf("Expected *bigValue from interface{} in finalizer, got %v", *i)
}
if i.fill != 0xDEADBEEFDEADBEEF && i.it != true && i.up != "It matters not how strait the gate" {
t.Errorf("*bigValue 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(*bigValue, func(interface{})) didn't run")
}
}
func fin(v *int) { func fin(v *int) {
} }
......
...@@ -109,6 +109,7 @@ struct Finalizer ...@@ -109,6 +109,7 @@ struct Finalizer
FuncVal *fn; FuncVal *fn;
void *arg; void *arg;
uintptr nret; uintptr nret;
PtrType *ot;
}; };
typedef struct FinBlock FinBlock; typedef struct FinBlock FinBlock;
...@@ -1583,10 +1584,11 @@ handlespecial(byte *p, uintptr size) ...@@ -1583,10 +1584,11 @@ handlespecial(byte *p, uintptr size)
{ {
FuncVal *fn; FuncVal *fn;
uintptr nret; uintptr nret;
PtrType *ot;
FinBlock *block; FinBlock *block;
Finalizer *f; Finalizer *f;
if(!runtime·getfinalizer(p, true, &fn, &nret)) { if(!runtime·getfinalizer(p, true, &fn, &nret, &ot)) {
runtime·setblockspecial(p, false); runtime·setblockspecial(p, false);
runtime·MProf_Free(p, size); runtime·MProf_Free(p, size);
return false; return false;
...@@ -1609,6 +1611,7 @@ handlespecial(byte *p, uintptr size) ...@@ -1609,6 +1611,7 @@ handlespecial(byte *p, uintptr size)
finq->cnt++; finq->cnt++;
f->fn = fn; f->fn = fn;
f->nret = nret; f->nret = nret;
f->ot = ot;
f->arg = p; f->arg = p;
runtime·unlock(&finlock); runtime·unlock(&finlock);
return true; return true;
...@@ -2272,6 +2275,7 @@ runfinq(void) ...@@ -2272,6 +2275,7 @@ runfinq(void)
FinBlock *fb, *next; FinBlock *fb, *next;
byte *frame; byte *frame;
uint32 framesz, framecap, i; uint32 framesz, framecap, i;
Eface *ef;
frame = nil; frame = nil;
framecap = 0; framecap = 0;
...@@ -2291,7 +2295,7 @@ runfinq(void) ...@@ -2291,7 +2295,7 @@ runfinq(void)
next = fb->next; next = fb->next;
for(i=0; i<fb->cnt; i++) { for(i=0; i<fb->cnt; i++) {
f = &fb->fin[i]; f = &fb->fin[i];
framesz = sizeof(uintptr) + f->nret; framesz = sizeof(Eface) + f->nret;
if(framecap < framesz) { if(framecap < framesz) {
runtime·free(frame); runtime·free(frame);
// The frame does not contain pointers interesting for GC, // The frame does not contain pointers interesting for GC,
...@@ -2301,10 +2305,17 @@ runfinq(void) ...@@ -2301,10 +2305,17 @@ runfinq(void)
frame = runtime·mallocgc(framesz, 0, FlagNoPointers|FlagNoInvokeGC); frame = runtime·mallocgc(framesz, 0, FlagNoPointers|FlagNoInvokeGC);
framecap = framesz; framecap = framesz;
} }
*(void**)frame = f->arg; if(f->ot == nil)
reflect·call(f->fn, frame, sizeof(uintptr) + f->nret); *(void**)frame = f->arg;
else {
ef = (Eface*)frame;
ef->type = f->ot;
ef->data = f->arg;
}
reflect·call(f->fn, frame, framesz);
f->fn = nil; f->fn = nil;
f->arg = nil; f->arg = nil;
f->ot = nil;
} }
fb->cnt = 0; fb->cnt = 0;
fb->next = finc; fb->next = finc;
......
...@@ -808,7 +808,7 @@ uintptr runtime·ifacehash(Iface, uintptr); ...@@ -808,7 +808,7 @@ 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); 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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment