github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/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 #include "type.h" 9 10 enum { debug = 0 }; 11 12 typedef struct Fin Fin; 13 struct Fin 14 { 15 FuncVal *fn; 16 uintptr nret; 17 Type *fint; 18 PtrType *ot; 19 }; 20 21 // Finalizer hash table. Direct hash, linear scan, at most 3/4 full. 22 // Table size is power of 3 so that hash can be key % max. 23 // Key[i] == (void*)-1 denotes free but formerly occupied entry 24 // (doesn't stop the linear scan). 25 // Key and val are separate tables because the garbage collector 26 // must be instructed to ignore the pointers in key but follow the 27 // pointers in val. 28 typedef struct Fintab Fintab; 29 struct Fintab 30 { 31 Lock; 32 void **key; 33 Fin *val; 34 int32 nkey; // number of non-nil entries in key 35 int32 ndead; // number of dead (-1) entries in key 36 int32 max; // size of key, val allocations 37 }; 38 39 #define TABSZ 17 40 #define TAB(p) (&fintab[((uintptr)(p)>>3)%TABSZ]) 41 42 static struct { 43 Fintab; 44 uint8 pad[CacheLineSize - sizeof(Fintab)]; 45 } fintab[TABSZ]; 46 47 static void 48 addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret, Type *fint, PtrType *ot) 49 { 50 int32 i, j; 51 52 i = (uintptr)k % (uintptr)t->max; 53 for(j=0; j<t->max; j++) { 54 if(t->key[i] == nil) { 55 t->nkey++; 56 goto ret; 57 } 58 if(t->key[i] == (void*)-1) { 59 t->ndead--; 60 goto ret; 61 } 62 if(++i == t->max) 63 i = 0; 64 } 65 66 // cannot happen - table is known to be non-full 67 runtime·throw("finalizer table inconsistent"); 68 69 ret: 70 t->key[i] = k; 71 t->val[i].fn = fn; 72 t->val[i].nret = nret; 73 t->val[i].fint = fint; 74 t->val[i].ot = ot; 75 } 76 77 static bool 78 lookfintab(Fintab *t, void *k, bool del, Fin *f) 79 { 80 int32 i, j; 81 82 if(t->max == 0) 83 return false; 84 i = (uintptr)k % (uintptr)t->max; 85 for(j=0; j<t->max; j++) { 86 if(t->key[i] == nil) 87 return false; 88 if(t->key[i] == k) { 89 if(f) 90 *f = t->val[i]; 91 if(del) { 92 t->key[i] = (void*)-1; 93 t->val[i].fn = nil; 94 t->val[i].nret = 0; 95 t->val[i].ot = nil; 96 t->ndead++; 97 } 98 return true; 99 } 100 if(++i == t->max) 101 i = 0; 102 } 103 104 // cannot happen - table is known to be non-full 105 runtime·throw("finalizer table inconsistent"); 106 return false; 107 } 108 109 static void 110 resizefintab(Fintab *tab) 111 { 112 Fintab newtab; 113 void *k; 114 int32 i; 115 116 runtime·memclr((byte*)&newtab, sizeof newtab); 117 newtab.max = tab->max; 118 if(newtab.max == 0) 119 newtab.max = 3*3*3; 120 else if(tab->ndead < tab->nkey/2) { 121 // grow table if not many dead values. 122 // otherwise just rehash into table of same size. 123 newtab.max *= 3; 124 } 125 126 newtab.key = runtime·mallocgc(newtab.max*sizeof newtab.key[0], 0, FlagNoInvokeGC|FlagNoScan); 127 newtab.val = runtime·mallocgc(newtab.max*sizeof newtab.val[0], 0, FlagNoInvokeGC); 128 129 for(i=0; i<tab->max; i++) { 130 k = tab->key[i]; 131 if(k != nil && k != (void*)-1) 132 addfintab(&newtab, k, tab->val[i].fn, tab->val[i].nret, tab->val[i].fint, tab->val[i].ot); 133 } 134 135 runtime·free(tab->key); 136 runtime·free(tab->val); 137 138 tab->key = newtab.key; 139 tab->val = newtab.val; 140 tab->nkey = newtab.nkey; 141 tab->ndead = newtab.ndead; 142 tab->max = newtab.max; 143 } 144 145 bool 146 runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, Type *fint, PtrType *ot) 147 { 148 Fintab *tab; 149 byte *base; 150 151 if(debug) { 152 if(!runtime·mlookup(p, &base, nil, nil) || p != base) 153 runtime·throw("addfinalizer on invalid pointer"); 154 } 155 156 tab = TAB(p); 157 runtime·lock(tab); 158 if(f == nil) { 159 lookfintab(tab, p, true, nil); 160 runtime·unlock(tab); 161 return true; 162 } 163 164 if(lookfintab(tab, p, false, nil)) { 165 runtime·unlock(tab); 166 return false; 167 } 168 169 if(tab->nkey >= tab->max/2+tab->max/4) { 170 // keep table at most 3/4 full: 171 // allocate new table and rehash. 172 resizefintab(tab); 173 } 174 175 addfintab(tab, p, f, nret, fint, ot); 176 runtime·setblockspecial(p, true); 177 runtime·unlock(tab); 178 return true; 179 } 180 181 // get finalizer; if del, delete finalizer. 182 // caller is responsible for updating RefHasFinalizer (special) bit. 183 bool 184 runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, Type **fint, PtrType **ot) 185 { 186 Fintab *tab; 187 bool res; 188 Fin f; 189 190 tab = TAB(p); 191 runtime·lock(tab); 192 res = lookfintab(tab, p, del, &f); 193 runtime·unlock(tab); 194 if(res==false) 195 return false; 196 *fn = f.fn; 197 *nret = f.nret; 198 *fint = f.fint; 199 *ot = f.ot; 200 return true; 201 } 202 203 void 204 runtime·walkfintab(void (*fn)(void*)) 205 { 206 void **key; 207 void **ekey; 208 int32 i; 209 210 for(i=0; i<TABSZ; i++) { 211 runtime·lock(&fintab[i]); 212 key = fintab[i].key; 213 ekey = key + fintab[i].max; 214 for(; key < ekey; key++) 215 if(*key != nil && *key != ((void*)-1)) 216 fn(*key); 217 runtime·unlock(&fintab[i]); 218 } 219 }