github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/runtime/mfinal.c (about) 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. 4 5 #include "runtime.h" 6 #include "arch_GOARCH.h" 7 #include "malloc.h" 8 9 enum { debug = 0 }; 10 11 typedef struct Fin Fin; 12 struct Fin 13 { 14 FuncVal *fn; 15 uintptr nret; 16 }; 17 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 24 // pointers in val. 25 typedef struct Fintab Fintab; 26 struct Fintab 27 { 28 Lock; 29 void **key; 30 Fin *val; 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 34 }; 35 36 #define TABSZ 17 37 #define TAB(p) (&fintab[((uintptr)(p)>>3)%TABSZ]) 38 39 static struct { 40 Fintab; 41 uint8 pad[CacheLineSize - sizeof(Fintab)]; 42 } fintab[TABSZ]; 43 44 static void 45 addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret) 46 { 47 int32 i, j; 48 49 i = (uintptr)k % (uintptr)t->max; 50 for(j=0; j<t->max; j++) { 51 if(t->key[i] == nil) { 52 t->nkey++; 53 goto ret; 54 } 55 if(t->key[i] == (void*)-1) { 56 t->ndead--; 57 goto ret; 58 } 59 if(++i == t->max) 60 i = 0; 61 } 62 63 // cannot happen - table is known to be non-full 64 runtime·throw("finalizer table inconsistent"); 65 66 ret: 67 t->key[i] = k; 68 t->val[i].fn = fn; 69 t->val[i].nret = nret; 70 } 71 72 static bool 73 lookfintab(Fintab *t, void *k, bool del, Fin *f) 74 { 75 int32 i, j; 76 77 if(t->max == 0) 78 return false; 79 i = (uintptr)k % (uintptr)t->max; 80 for(j=0; j<t->max; j++) { 81 if(t->key[i] == nil) 82 return false; 83 if(t->key[i] == k) { 84 if(f) 85 *f = t->val[i]; 86 if(del) { 87 t->key[i] = (void*)-1; 88 t->val[i].fn = nil; 89 t->val[i].nret = 0; 90 t->ndead++; 91 } 92 return true; 93 } 94 if(++i == t->max) 95 i = 0; 96 } 97 98 // cannot happen - table is known to be non-full 99 runtime·throw("finalizer table inconsistent"); 100 return false; 101 } 102 103 static void 104 resizefintab(Fintab *tab) 105 { 106 Fintab newtab; 107 void *k; 108 int32 i; 109 110 runtime·memclr((byte*)&newtab, sizeof newtab); 111 newtab.max = tab->max; 112 if(newtab.max == 0) 113 newtab.max = 3*3*3; 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. 117 newtab.max *= 3; 118 } 119 120 newtab.key = runtime·mallocgc(newtab.max*sizeof newtab.key[0], FlagNoPointers, 0, 1); 121 newtab.val = runtime·mallocgc(newtab.max*sizeof newtab.val[0], 0, 0, 1); 122 123 for(i=0; i<tab->max; i++) { 124 k = tab->key[i]; 125 if(k != nil && k != (void*)-1) 126 addfintab(&newtab, k, tab->val[i].fn, tab->val[i].nret); 127 } 128 129 runtime·free(tab->key); 130 runtime·free(tab->val); 131 132 tab->key = newtab.key; 133 tab->val = newtab.val; 134 tab->nkey = newtab.nkey; 135 tab->ndead = newtab.ndead; 136 tab->max = newtab.max; 137 } 138 139 bool 140 runtime·addfinalizer(void *p, FuncVal *f, uintptr nret) 141 { 142 Fintab *tab; 143 byte *base; 144 145 if(debug) { 146 if(!runtime·mlookup(p, &base, nil, nil) || p != base) 147 runtime·throw("addfinalizer on invalid pointer"); 148 } 149 150 tab = TAB(p); 151 runtime·lock(tab); 152 if(f == nil) { 153 lookfintab(tab, p, true, nil); 154 runtime·unlock(tab); 155 return true; 156 } 157 158 if(lookfintab(tab, p, false, nil)) { 159 runtime·unlock(tab); 160 return false; 161 } 162 163 if(tab->nkey >= tab->max/2+tab->max/4) { 164 // keep table at most 3/4 full: 165 // allocate new table and rehash. 166 resizefintab(tab); 167 } 168 169 addfintab(tab, p, f, nret); 170 runtime·setblockspecial(p, true); 171 runtime·unlock(tab); 172 return true; 173 } 174 175 // get finalizer; if del, delete finalizer. 176 // caller is responsible for updating RefHasFinalizer (special) bit. 177 bool 178 runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret) 179 { 180 Fintab *tab; 181 bool res; 182 Fin f; 183 184 tab = TAB(p); 185 runtime·lock(tab); 186 res = lookfintab(tab, p, del, &f); 187 runtime·unlock(tab); 188 if(res==false) 189 return false; 190 *fn = f.fn; 191 *nret = f.nret; 192 return true; 193 } 194 195 void 196 runtime·walkfintab(void (*fn)(void*)) 197 { 198 void **key; 199 void **ekey; 200 int32 i; 201 202 for(i=0; i<TABSZ; i++) { 203 runtime·lock(&fintab[i]); 204 key = fintab[i].key; 205 ekey = key + fintab[i].max; 206 for(; key < ekey; key++) 207 if(*key != nil && *key != ((void*)-1)) 208 fn(*key); 209 runtime·unlock(&fintab[i]); 210 } 211 }