1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
11 typedef struct Fin Fin;
15 const struct __go_func_type *ft;
18 // Finalizer hash table. Direct hash, linear scan, at most 3/4 full.
19 // Table size is power of 3 so that hash can be key % max.
20 // Key[i] == (void*)-1 denotes free but formerly occupied entry
21 // (doesn't stop the linear scan).
22 // Key and val are separate tables because the garbage collector
23 // must be instructed to ignore the pointers in key but follow the
25 typedef struct Fintab Fintab;
31 int32 nkey; // number of non-nil entries in key
32 int32 ndead; // number of dead (-1) entries in key
33 int32 max; // size of key, val allocations
37 #define TAB(p) (&fintab[((uintptr)(p)>>3)%TABSZ])
41 uint8 pad[0 /* CacheLineSize - sizeof(Fintab) */];
45 addfintab(Fintab *t, void *k, void (*fn)(void*), const struct __go_func_type *ft)
49 i = (uintptr)k % (uintptr)t->max;
50 for(j=0; j<t->max; j++) {
51 if(t->fkey[i] == nil) {
55 if(t->fkey[i] == (void*)-1) {
63 // cannot happen - table is known to be non-full
64 runtime_throw("finalizer table inconsistent");
73 lookfintab(Fintab *t, void *k, bool del, Fin *f)
79 i = (uintptr)k % (uintptr)t->max;
80 for(j=0; j<t->max; j++) {
87 t->fkey[i] = (void*)-1;
98 // cannot happen - table is known to be non-full
99 runtime_throw("finalizer table inconsistent");
104 resizefintab(Fintab *tab)
110 runtime_memclr((byte*)&newtab, sizeof newtab);
111 newtab.max = tab->max;
114 else if(tab->ndead < tab->nkey/2) {
115 // grow table if not many dead values.
116 // otherwise just rehash into table of same size.
120 newtab.fkey = runtime_mallocgc(newtab.max*sizeof newtab.fkey[0], FlagNoPointers, 0, 1);
121 newtab.val = runtime_mallocgc(newtab.max*sizeof newtab.val[0], 0, 0, 1);
123 for(i=0; i<tab->max; i++) {
125 if(k != nil && k != (void*)-1)
126 addfintab(&newtab, k, tab->val[i].fn, tab->val[i].ft);
129 runtime_free(tab->fkey);
130 runtime_free(tab->val);
132 tab->fkey = newtab.fkey;
133 tab->val = newtab.val;
134 tab->nkey = newtab.nkey;
135 tab->ndead = newtab.ndead;
136 tab->max = newtab.max;
140 runtime_addfinalizer(void *p, void (*f)(void*), const struct __go_func_type *ft)
146 if(!runtime_mlookup(p, &base, nil, nil) || p != base)
147 runtime_throw("addfinalizer on invalid pointer");
153 lookfintab(tab, p, true, nil);
158 if(lookfintab(tab, p, false, nil)) {
163 if(tab->nkey >= tab->max/2+tab->max/4) {
164 // keep table at most 3/4 full:
165 // allocate new table and rehash.
169 addfintab(tab, p, f, ft);
170 runtime_setblockspecial(p, true);
175 // get finalizer; if del, delete finalizer.
176 // caller is responsible for updating RefHasFinalizer (special) bit.
178 runtime_getfinalizer(void *p, bool del, void (**fn)(void*), const struct __go_func_type **ft)
186 res = lookfintab(tab, p, del, &f);
196 runtime_walkfintab(void (*fn)(void*), void (*addroot)(byte *, uintptr))
202 for(i=0; i<TABSZ; i++) {
203 runtime_lock(&fintab[i]);
204 key = fintab[i].fkey;
205 ekey = key + fintab[i].max;
206 for(; key < ekey; key++)
207 if(*key != nil && *key != ((void*)-1))
209 addroot((byte*)&fintab[i].fkey, sizeof(void*));
210 addroot((byte*)&fintab[i].val, sizeof(void*));
211 runtime_unlock(&fintab[i]);