In gcc/objc/: 2011-04-12 Nicola Pero <nicola.pero@meta-innovation.com>
[official-gcc.git] / libgo / runtime / mfinal.c
blob04d58dddda97bb104715b2ed41c3542df60d3da2
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.
5 #include "runtime.h"
6 #include "malloc.h"
8 // Lock to protect finalizer data structures.
9 // Cannot reuse mheap.Lock because the finalizer
10 // maintenance requires allocation.
11 static Lock finlock;
13 void
14 runtime_initfintab()
16 runtime_initlock(&finlock);
19 // Finalizer hash table. Direct hash, linear scan, at most 3/4 full.
20 // Table size is power of 3 so that hash can be key % max.
21 // Key[i] == (void*)-1 denotes free but formerly occupied entry
22 // (doesn't stop the linear scan).
23 // Key and val are separate tables because the garbage collector
24 // must be instructed to ignore the pointers in key but follow the
25 // pointers in val.
26 typedef struct Fintab Fintab;
27 struct Fintab
29 void **key;
30 Finalizer **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
36 static void
37 addfintab(Fintab *t, void *k, Finalizer *v)
39 int32 i, j;
41 i = (uintptr)k % (uintptr)t->max;
42 for(j=0; j<t->max; j++) {
43 if(t->key[i] == nil) {
44 t->nkey++;
45 goto ret;
47 if(t->key[i] == (void*)-1) {
48 t->ndead--;
49 goto ret;
51 if(++i == t->max)
52 i = 0;
55 // cannot happen - table is known to be non-full
56 runtime_throw("finalizer table inconsistent");
58 ret:
59 t->key[i] = k;
60 t->val[i] = v;
63 static Finalizer*
64 lookfintab(Fintab *t, void *k, bool del)
66 int32 i, j;
67 Finalizer *v;
69 if(t->max == 0)
70 return nil;
71 i = (uintptr)k % (uintptr)t->max;
72 for(j=0; j<t->max; j++) {
73 if(t->key[i] == nil)
74 return nil;
75 if(t->key[i] == k) {
76 v = t->val[i];
77 if(del) {
78 t->key[i] = (void*)-1;
79 t->val[i] = nil;
80 t->ndead++;
82 return v;
84 if(++i == t->max)
85 i = 0;
88 // cannot happen - table is known to be non-full
89 runtime_throw("finalizer table inconsistent");
90 return nil;
93 static Fintab fintab;
95 // add finalizer; caller is responsible for making sure not already in table
96 void
97 runtime_addfinalizer(void *p, void (*f)(void*), const struct __go_func_type *ft)
99 Fintab newtab;
100 int32 i;
101 byte *base;
102 Finalizer *e;
104 e = nil;
105 if(f != nil) {
106 e = runtime_mal(sizeof *e);
107 e->fn = f;
108 e->ft = ft;
111 if(!__sync_bool_compare_and_swap(&m->holds_finlock, 0, 1))
112 runtime_throw("finalizer deadlock");
114 runtime_lock(&finlock);
115 if(!runtime_mlookup(p, &base, nil, nil) || p != base) {
116 runtime_unlock(&finlock);
117 __sync_bool_compare_and_swap(&m->holds_finlock, 1, 0);
118 runtime_throw("addfinalizer on invalid pointer");
120 if(f == nil) {
121 lookfintab(&fintab, p, 1);
122 goto unlock;
125 if(lookfintab(&fintab, p, 0)) {
126 runtime_unlock(&finlock);
127 __sync_bool_compare_and_swap(&m->holds_finlock, 1, 0);
128 runtime_throw("double finalizer");
130 runtime_setblockspecial(p);
132 if(fintab.nkey >= fintab.max/2+fintab.max/4) {
133 // keep table at most 3/4 full:
134 // allocate new table and rehash.
136 runtime_memclr((byte*)&newtab, sizeof newtab);
137 newtab.max = fintab.max;
138 if(newtab.max == 0)
139 newtab.max = 3*3*3;
140 else if(fintab.ndead < fintab.nkey/2) {
141 // grow table if not many dead values.
142 // otherwise just rehash into table of same size.
143 newtab.max *= 3;
146 newtab.key = runtime_mallocgc(newtab.max*sizeof newtab.key[0], FlagNoPointers, 0, 1);
147 newtab.val = runtime_mallocgc(newtab.max*sizeof newtab.val[0], 0, 0, 1);
149 for(i=0; i<fintab.max; i++) {
150 void *k;
152 k = fintab.key[i];
153 if(k != nil && k != (void*)-1)
154 addfintab(&newtab, k, fintab.val[i]);
156 runtime_free(fintab.key);
157 runtime_free(fintab.val);
158 fintab = newtab;
161 addfintab(&fintab, p, e);
162 unlock:
163 runtime_unlock(&finlock);
165 __sync_bool_compare_and_swap(&m->holds_finlock, 1, 0);
167 if(__sync_bool_compare_and_swap(&m->gcing_for_finlock, 1, 0)) {
168 __go_run_goroutine_gc(200);
172 // get finalizer; if del, delete finalizer.
173 // caller is responsible for updating RefHasFinalizer bit.
174 Finalizer*
175 runtime_getfinalizer(void *p, bool del)
177 Finalizer *f;
179 if(!__sync_bool_compare_and_swap(&m->holds_finlock, 0, 1))
180 runtime_throw("finalizer deadlock");
182 runtime_lock(&finlock);
183 f = lookfintab(&fintab, p, del);
184 runtime_unlock(&finlock);
186 __sync_bool_compare_and_swap(&m->holds_finlock, 1, 0);
187 if(__sync_bool_compare_and_swap(&m->gcing_for_finlock, 1, 0)) {
188 __go_run_goroutine_gc(201);
191 return f;
194 void
195 runtime_walkfintab(void (*fn)(void*), void (*scan)(byte *, int64))
197 void **key;
198 void **ekey;
200 if(!__sync_bool_compare_and_swap(&m->holds_finlock, 0, 1))
201 runtime_throw("finalizer deadlock");
203 scan((byte*)&fintab, sizeof fintab);
204 runtime_lock(&finlock);
205 key = fintab.key;
206 ekey = key + fintab.max;
207 for(; key < ekey; key++)
208 if(*key != nil && *key != ((void*)-1))
209 fn(*key);
210 runtime_unlock(&finlock);
212 __sync_bool_compare_and_swap(&m->holds_finlock, 1, 0);
213 if(__sync_bool_compare_and_swap(&m->gcing_for_finlock, 1, 0)) {
214 runtime_throw("walkfintab not called from gc");