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.
8 static Lock finlock = LOCK_INITIALIZER;
10 // Finalizer hash table. Direct hash, linear scan, at most 3/4 full.
11 // Table size is power of 3 so that hash can be key % max.
12 // Key[i] == (void*)-1 denotes free but formerly occupied entry
13 // (doesn't stop the linear scan).
14 // Key and val are separate tables because the garbage collector
15 // must be instructed to ignore the pointers in key but follow the
17 typedef struct Fintab Fintab;
22 int32 nkey; // number of non-nil entries in key
23 int32 ndead; // number of dead (-1) entries in key
24 int32 max; // size of key, val allocations
28 addfintab(Fintab *t, void *k, Finalizer *v)
32 i = (uintptr)k % (uintptr)t->max;
33 for(j=0; j<t->max; j++) {
34 if(t->key[i] == nil) {
38 if(t->key[i] == (void*)-1) {
46 // cannot happen - table is known to be non-full
47 runtime_throw("finalizer table inconsistent");
55 lookfintab(Fintab *t, void *k, bool del)
62 i = (uintptr)k % (uintptr)t->max;
63 for(j=0; j<t->max; j++) {
69 t->key[i] = (void*)-1;
79 // cannot happen - table is known to be non-full
80 runtime_throw("finalizer table inconsistent");
86 // add finalizer; caller is responsible for making sure not already in table
88 runtime_addfinalizer(void *p, void (*f)(void*), const struct __go_func_type *ft)
98 e = runtime_mal(sizeof *e);
103 runtime_lock(&finlock);
104 if(!runtime_mlookup(p, &base, nil, nil, &ref) || p != base) {
105 runtime_unlock(&finlock);
106 runtime_throw("addfinalizer on invalid pointer");
109 if(*ref & RefHasFinalizer) {
110 lookfintab(&fintab, p, 1);
111 *ref &= ~RefHasFinalizer;
113 runtime_unlock(&finlock);
117 if(*ref & RefHasFinalizer) {
118 runtime_unlock(&finlock);
119 runtime_throw("double finalizer");
121 *ref |= RefHasFinalizer;
123 if(fintab.nkey >= fintab.max/2+fintab.max/4) {
124 // keep table at most 3/4 full:
125 // allocate new table and rehash.
127 runtime_memclr((byte*)&newtab, sizeof newtab);
128 newtab.max = fintab.max;
131 else if(fintab.ndead < fintab.nkey/2) {
132 // grow table if not many dead values.
133 // otherwise just rehash into table of same size.
137 newtab.key = runtime_mallocgc(newtab.max*sizeof newtab.key[0], RefNoPointers, 0, 1);
138 newtab.val = runtime_mallocgc(newtab.max*sizeof newtab.val[0], 0, 0, 1);
140 for(i=0; i<fintab.max; i++) {
144 if(k != nil && k != (void*)-1)
145 addfintab(&newtab, k, fintab.val[i]);
147 runtime_free(fintab.key);
148 runtime_free(fintab.val);
152 addfintab(&fintab, p, e);
153 runtime_unlock(&finlock);
156 // get finalizer; if del, delete finalizer.
157 // caller is responsible for updating RefHasFinalizer bit.
159 runtime_getfinalizer(void *p, bool del)
163 runtime_lock(&finlock);
164 f = lookfintab(&fintab, p, del);
165 runtime_unlock(&finlock);
170 runtime_walkfintab(void (*fn)(void*), void (*scan)(byte *, int64))
175 scan((byte*)&fintab, sizeof fintab);
176 runtime_lock(&finlock);
178 ekey = key + fintab.max;
179 for(; key < ekey; key++)
180 if(*key != nil && *key != ((void*)-1))
182 runtime_unlock(&finlock);