// The argument x must be a pointer to an object allocated by
// calling new or by taking the address of a composite literal.
// The argument f must be a function that takes a single argument
-// of x's type and can have arbitrary ignored return values.
-// If either of these is not true, SetFinalizer aborts the program.
+// of x's type or interface{}, and can have arbitrary ignored return
+// 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, and they are otherwise unreachable, only the finalizer
int32 i;
uintptr nret;
Type *t;
+ Type *fint;
+ PtrType *ot;
if(obj.type == nil) {
runtime·printf("runtime.SetFinalizer: first argument is nil interface\n");
goto throw;
}
nret = 0;
+ ot = nil;
if(finalizer.type != nil) {
if(finalizer.type->kind != KindFunc)
goto badfunc;
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;
// compute size needed for return parameters
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");
goto throw;
}
return;
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:
runtime·throw("runtime.SetFinalizer");
}
void runtime·helpgc(int32 nproc);
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*));
enum
{
FuncVal *fn;
uintptr nret;
+ void *ot;
};
// Finalizer hash table. Direct hash, linear scan, at most 3/4 full.
} fintab[TABSZ];
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;
t->key[i] = k;
t->val[i].fn = fn;
t->val[i].nret = nret;
+ t->val[i].ot = ot;
}
static bool
t->key[i] = (void*)-1;
t->val[i].fn = nil;
t->val[i].nret = 0;
+ t->val[i].ot = nil;
t->ndead++;
}
return true;
for(i=0; i<tab->max; i++) {
k = tab->key[i];
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);
}
bool
-runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
+runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, void *ot)
{
Fintab *tab;
byte *base;
resizefintab(tab);
}
- addfintab(tab, p, f, nret);
+ addfintab(tab, p, f, nret, ot);
runtime·setblockspecial(p, true);
runtime·unlock(tab);
return true;
// get finalizer; if del, delete finalizer.
// caller is responsible for updating RefHasFinalizer (special) bit.
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;
bool res;
return false;
*fn = f.fn;
*nret = f.nret;
+ *ot = f.ot;
return true;
}
"sync"
"sync/atomic"
"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) {
}
FuncVal *fn;
void *arg;
uintptr nret;
+ PtrType *ot;
};
typedef struct FinBlock FinBlock;
{
FuncVal *fn;
uintptr nret;
+ PtrType *ot;
FinBlock *block;
Finalizer *f;
- if(!runtime·getfinalizer(p, true, &fn, &nret)) {
+ if(!runtime·getfinalizer(p, true, &fn, &nret, &ot)) {
runtime·setblockspecial(p, false);
runtime·MProf_Free(p, size);
return false;
finq->cnt++;
f->fn = fn;
f->nret = nret;
+ f->ot = ot;
f->arg = p;
runtime·unlock(&finlock);
return true;
FinBlock *fb, *next;
byte *frame;
uint32 framesz, framecap, i;
+ Eface *ef;
frame = nil;
framecap = 0;
next = fb->next;
for(i=0; i<fb->cnt; i++) {
f = &fb->fin[i];
- framesz = sizeof(uintptr) + f->nret;
+ framesz = sizeof(Eface) + f->nret;
if(framecap < framesz) {
runtime·free(frame);
// The frame does not contain pointers interesting for GC,
frame = runtime·mallocgc(framesz, 0, FlagNoPointers|FlagNoInvokeGC);
framecap = framesz;
}
- *(void**)frame = f->arg;
- reflect·call(f->fn, frame, sizeof(uintptr) + f->nret);
+ if(f->ot == nil)
+ *(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->arg = nil;
+ f->ot = nil;
}
fb->cnt = 0;
fb->next = finc;
uintptr runtime·efacehash(Eface, uintptr);
void* runtime·malloc(uintptr size);
void runtime·free(void *v);
-bool runtime·addfinalizer(void*, FuncVal *fn, uintptr);
+bool runtime·addfinalizer(void*, FuncVal *fn, uintptr, void*);
void runtime·runpanic(Panic*);
uintptr runtime·getcallersp(void*);
int32 runtime·mcount(void);