3f3a610414319baced9faa0a50810cc88f3ecd0e
[platform/upstream/gcc.git] / libgo / runtime / mfinal.c
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 "malloc.h"
7
8 static Lock finlock = LOCK_INITIALIZER;
9
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
16 // pointers in val.
17 typedef struct Fintab Fintab;
18 struct Fintab
19 {
20         void **key;
21         Finalizer **val;
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
25 };
26
27 static void
28 addfintab(Fintab *t, void *k, Finalizer *v)
29 {
30         int32 i, j;
31
32         i = (uintptr)k % (uintptr)t->max;
33         for(j=0; j<t->max; j++) {
34                 if(t->key[i] == nil) {
35                         t->nkey++;
36                         goto ret;
37                 }
38                 if(t->key[i] == (void*)-1) {
39                         t->ndead--;
40                         goto ret;
41                 }
42                 if(++i == t->max)
43                         i = 0;
44         }
45
46         // cannot happen - table is known to be non-full
47         runtime_throw("finalizer table inconsistent");
48
49 ret:
50         t->key[i] = k;
51         t->val[i] = v;
52 }
53
54 static Finalizer*
55 lookfintab(Fintab *t, void *k, bool del)
56 {
57         int32 i, j;
58         Finalizer *v;
59
60         if(t->max == 0)
61                 return nil;
62         i = (uintptr)k % (uintptr)t->max;
63         for(j=0; j<t->max; j++) {
64                 if(t->key[i] == nil)
65                         return nil;
66                 if(t->key[i] == k) {
67                         v = t->val[i];
68                         if(del) {
69                                 t->key[i] = (void*)-1;
70                                 t->val[i] = nil;
71                                 t->ndead++;
72                         }
73                         return v;
74                 }
75                 if(++i == t->max)
76                         i = 0;
77         }
78
79         // cannot happen - table is known to be non-full
80         runtime_throw("finalizer table inconsistent");
81         return nil;
82 }
83
84 static Fintab fintab;
85
86 // add finalizer; caller is responsible for making sure not already in table
87 void
88 runtime_addfinalizer(void *p, void (*f)(void*), const struct __go_func_type *ft)
89 {
90         Fintab newtab;
91         int32 i;
92         uint32 *ref;
93         byte *base;
94         Finalizer *e;
95         
96         e = nil;
97         if(f != nil) {
98                 e = runtime_mal(sizeof *e);
99                 e->fn = f;
100                 e->ft = ft;
101         }
102
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");
107         }
108         if(f == nil) {
109                 if(*ref & RefHasFinalizer) {
110                         lookfintab(&fintab, p, 1);
111                         *ref &= ~RefHasFinalizer;
112                 }
113                 runtime_unlock(&finlock);
114                 return;
115         }
116
117         if(*ref & RefHasFinalizer) {
118                 runtime_unlock(&finlock);
119                 runtime_throw("double finalizer");
120         }
121         *ref |= RefHasFinalizer;
122
123         if(fintab.nkey >= fintab.max/2+fintab.max/4) {
124                 // keep table at most 3/4 full:
125                 // allocate new table and rehash.
126
127                 runtime_memclr((byte*)&newtab, sizeof newtab);
128                 newtab.max = fintab.max;
129                 if(newtab.max == 0)
130                         newtab.max = 3*3*3;
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.
134                         newtab.max *= 3;
135                 }
136
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);
139
140                 for(i=0; i<fintab.max; i++) {
141                         void *k;
142
143                         k = fintab.key[i];
144                         if(k != nil && k != (void*)-1)
145                                 addfintab(&newtab, k, fintab.val[i]);
146                 }
147                 runtime_free(fintab.key);
148                 runtime_free(fintab.val);
149                 fintab = newtab;
150         }
151
152         addfintab(&fintab, p, e);
153         runtime_unlock(&finlock);
154 }
155
156 // get finalizer; if del, delete finalizer.
157 // caller is responsible for updating RefHasFinalizer bit.
158 Finalizer*
159 runtime_getfinalizer(void *p, bool del)
160 {
161         Finalizer *f;
162         
163         runtime_lock(&finlock);
164         f = lookfintab(&fintab, p, del);
165         runtime_unlock(&finlock);
166         return f;
167 }
168
169 void
170 runtime_walkfintab(void (*fn)(void*), void (*scan)(byte *, int64))
171 {
172         void **key;
173         void **ekey;
174
175         scan((byte*)&fintab, sizeof fintab);
176         runtime_lock(&finlock);
177         key = fintab.key;
178         ekey = key + fintab.max;
179         for(; key < ekey; key++)
180                 if(*key != nil && *key != ((void*)-1))
181                         fn(*key);
182         runtime_unlock(&finlock);
183 }