mirror of
https://github.com/golang/go
synced 2024-10-04 20:11:22 -06:00
d0dd420a24
the use of the flag, especially for objects which actually do have pointers but we don't want the GC to scan them. R=golang-dev, cshapiro CC=golang-dev https://golang.org/cl/13181045
220 lines
4.5 KiB
C
220 lines
4.5 KiB
C
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
#include "runtime.h"
|
|
#include "arch_GOARCH.h"
|
|
#include "malloc.h"
|
|
#include "type.h"
|
|
|
|
enum { debug = 0 };
|
|
|
|
typedef struct Fin Fin;
|
|
struct Fin
|
|
{
|
|
FuncVal *fn;
|
|
uintptr nret;
|
|
Type *fint;
|
|
PtrType *ot;
|
|
};
|
|
|
|
// Finalizer hash table. Direct hash, linear scan, at most 3/4 full.
|
|
// Table size is power of 3 so that hash can be key % max.
|
|
// Key[i] == (void*)-1 denotes free but formerly occupied entry
|
|
// (doesn't stop the linear scan).
|
|
// Key and val are separate tables because the garbage collector
|
|
// must be instructed to ignore the pointers in key but follow the
|
|
// pointers in val.
|
|
typedef struct Fintab Fintab;
|
|
struct Fintab
|
|
{
|
|
Lock;
|
|
void **key;
|
|
Fin *val;
|
|
int32 nkey; // number of non-nil entries in key
|
|
int32 ndead; // number of dead (-1) entries in key
|
|
int32 max; // size of key, val allocations
|
|
};
|
|
|
|
#define TABSZ 17
|
|
#define TAB(p) (&fintab[((uintptr)(p)>>3)%TABSZ])
|
|
|
|
static struct {
|
|
Fintab;
|
|
uint8 pad[CacheLineSize - sizeof(Fintab)];
|
|
} fintab[TABSZ];
|
|
|
|
static void
|
|
addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret, Type *fint, PtrType *ot)
|
|
{
|
|
int32 i, j;
|
|
|
|
i = (uintptr)k % (uintptr)t->max;
|
|
for(j=0; j<t->max; j++) {
|
|
if(t->key[i] == nil) {
|
|
t->nkey++;
|
|
goto ret;
|
|
}
|
|
if(t->key[i] == (void*)-1) {
|
|
t->ndead--;
|
|
goto ret;
|
|
}
|
|
if(++i == t->max)
|
|
i = 0;
|
|
}
|
|
|
|
// cannot happen - table is known to be non-full
|
|
runtime·throw("finalizer table inconsistent");
|
|
|
|
ret:
|
|
t->key[i] = k;
|
|
t->val[i].fn = fn;
|
|
t->val[i].nret = nret;
|
|
t->val[i].fint = fint;
|
|
t->val[i].ot = ot;
|
|
}
|
|
|
|
static bool
|
|
lookfintab(Fintab *t, void *k, bool del, Fin *f)
|
|
{
|
|
int32 i, j;
|
|
|
|
if(t->max == 0)
|
|
return false;
|
|
i = (uintptr)k % (uintptr)t->max;
|
|
for(j=0; j<t->max; j++) {
|
|
if(t->key[i] == nil)
|
|
return false;
|
|
if(t->key[i] == k) {
|
|
if(f)
|
|
*f = t->val[i];
|
|
if(del) {
|
|
t->key[i] = (void*)-1;
|
|
t->val[i].fn = nil;
|
|
t->val[i].nret = 0;
|
|
t->val[i].ot = nil;
|
|
t->ndead++;
|
|
}
|
|
return true;
|
|
}
|
|
if(++i == t->max)
|
|
i = 0;
|
|
}
|
|
|
|
// cannot happen - table is known to be non-full
|
|
runtime·throw("finalizer table inconsistent");
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
resizefintab(Fintab *tab)
|
|
{
|
|
Fintab newtab;
|
|
void *k;
|
|
int32 i;
|
|
|
|
runtime·memclr((byte*)&newtab, sizeof newtab);
|
|
newtab.max = tab->max;
|
|
if(newtab.max == 0)
|
|
newtab.max = 3*3*3;
|
|
else if(tab->ndead < tab->nkey/2) {
|
|
// grow table if not many dead values.
|
|
// otherwise just rehash into table of same size.
|
|
newtab.max *= 3;
|
|
}
|
|
|
|
newtab.key = runtime·mallocgc(newtab.max*sizeof newtab.key[0], 0, FlagNoInvokeGC|FlagNoScan);
|
|
newtab.val = runtime·mallocgc(newtab.max*sizeof newtab.val[0], 0, FlagNoInvokeGC);
|
|
|
|
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, tab->val[i].fint, tab->val[i].ot);
|
|
}
|
|
|
|
runtime·free(tab->key);
|
|
runtime·free(tab->val);
|
|
|
|
tab->key = newtab.key;
|
|
tab->val = newtab.val;
|
|
tab->nkey = newtab.nkey;
|
|
tab->ndead = newtab.ndead;
|
|
tab->max = newtab.max;
|
|
}
|
|
|
|
bool
|
|
runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, Type *fint, PtrType *ot)
|
|
{
|
|
Fintab *tab;
|
|
byte *base;
|
|
|
|
if(debug) {
|
|
if(!runtime·mlookup(p, &base, nil, nil) || p != base)
|
|
runtime·throw("addfinalizer on invalid pointer");
|
|
}
|
|
|
|
tab = TAB(p);
|
|
runtime·lock(tab);
|
|
if(f == nil) {
|
|
lookfintab(tab, p, true, nil);
|
|
runtime·unlock(tab);
|
|
return true;
|
|
}
|
|
|
|
if(lookfintab(tab, p, false, nil)) {
|
|
runtime·unlock(tab);
|
|
return false;
|
|
}
|
|
|
|
if(tab->nkey >= tab->max/2+tab->max/4) {
|
|
// keep table at most 3/4 full:
|
|
// allocate new table and rehash.
|
|
resizefintab(tab);
|
|
}
|
|
|
|
addfintab(tab, p, f, nret, fint, 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, Type **fint, PtrType **ot)
|
|
{
|
|
Fintab *tab;
|
|
bool res;
|
|
Fin f;
|
|
|
|
tab = TAB(p);
|
|
runtime·lock(tab);
|
|
res = lookfintab(tab, p, del, &f);
|
|
runtime·unlock(tab);
|
|
if(res==false)
|
|
return false;
|
|
*fn = f.fn;
|
|
*nret = f.nret;
|
|
*fint = f.fint;
|
|
*ot = f.ot;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
runtime·walkfintab(void (*fn)(void*))
|
|
{
|
|
void **key;
|
|
void **ekey;
|
|
int32 i;
|
|
|
|
for(i=0; i<TABSZ; i++) {
|
|
runtime·lock(&fintab[i]);
|
|
key = fintab[i].key;
|
|
ekey = key + fintab[i].max;
|
|
for(; key < ekey; key++)
|
|
if(*key != nil && *key != ((void*)-1))
|
|
fn(*key);
|
|
runtime·unlock(&fintab[i]);
|
|
}
|
|
}
|