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.
13 runtime_initlock(&finlock
);
16 // Finalizer hash table. Direct hash, linear scan, at most 3/4 full.
17 // Table size is power of 3 so that hash can be key % max.
18 // Key[i] == (void*)-1 denotes free but formerly occupied entry
19 // (doesn't stop the linear scan).
20 // Key and val are separate tables because the garbage collector
21 // must be instructed to ignore the pointers in key but follow the
23 typedef struct Fintab Fintab
;
28 int32 nkey
; // number of non-nil entries in key
29 int32 ndead
; // number of dead (-1) entries in key
30 int32 max
; // size of key, val allocations
34 addfintab(Fintab
*t
, void *k
, Finalizer
*v
)
38 i
= (uintptr
)k
% (uintptr
)t
->max
;
39 for(j
=0; j
<t
->max
; j
++) {
40 if(t
->key
[i
] == nil
) {
44 if(t
->key
[i
] == (void*)-1) {
52 // cannot happen - table is known to be non-full
53 runtime_throw("finalizer table inconsistent");
61 lookfintab(Fintab
*t
, void *k
, bool del
)
68 i
= (uintptr
)k
% (uintptr
)t
->max
;
69 for(j
=0; j
<t
->max
; j
++) {
75 t
->key
[i
] = (void*)-1;
85 // cannot happen - table is known to be non-full
86 runtime_throw("finalizer table inconsistent");
92 // add finalizer; caller is responsible for making sure not already in table
94 runtime_addfinalizer(void *p
, void (*f
)(void*), const struct __go_func_type
*ft
)
104 e
= runtime_mal(sizeof *e
);
109 if(!__sync_bool_compare_and_swap(&m
->holds_finlock
, 0, 1))
110 runtime_throw("finalizer deadlock");
112 runtime_lock(&finlock
);
113 if(!runtime_mlookup(p
, &base
, nil
, nil
, &ref
) || p
!= base
) {
114 runtime_unlock(&finlock
);
115 __sync_bool_compare_and_swap(&m
->holds_finlock
, 1, 0);
116 runtime_throw("addfinalizer on invalid pointer");
119 if(*ref
& RefHasFinalizer
) {
120 lookfintab(&fintab
, p
, 1);
121 *ref
&= ~RefHasFinalizer
;
126 if(*ref
& RefHasFinalizer
) {
127 runtime_unlock(&finlock
);
128 __sync_bool_compare_and_swap(&m
->holds_finlock
, 1, 0);
129 runtime_throw("double finalizer");
131 *ref
|= RefHasFinalizer
;
133 if(fintab
.nkey
>= fintab
.max
/2+fintab
.max
/4) {
134 // keep table at most 3/4 full:
135 // allocate new table and rehash.
137 runtime_memclr((byte
*)&newtab
, sizeof newtab
);
138 newtab
.max
= fintab
.max
;
141 else if(fintab
.ndead
< fintab
.nkey
/2) {
142 // grow table if not many dead values.
143 // otherwise just rehash into table of same size.
147 newtab
.key
= runtime_mallocgc(newtab
.max
*sizeof newtab
.key
[0], RefNoPointers
, 0, 1);
148 newtab
.val
= runtime_mallocgc(newtab
.max
*sizeof newtab
.val
[0], 0, 0, 1);
150 for(i
=0; i
<fintab
.max
; i
++) {
154 if(k
!= nil
&& k
!= (void*)-1)
155 addfintab(&newtab
, k
, fintab
.val
[i
]);
157 runtime_free(fintab
.key
);
158 runtime_free(fintab
.val
);
162 addfintab(&fintab
, p
, e
);
164 runtime_unlock(&finlock
);
166 __sync_bool_compare_and_swap(&m
->holds_finlock
, 1, 0);
168 if(__sync_bool_compare_and_swap(&m
->gcing_for_finlock
, 1, 0)) {
169 __go_run_goroutine_gc(200);
173 // get finalizer; if del, delete finalizer.
174 // caller is responsible for updating RefHasFinalizer bit.
176 runtime_getfinalizer(void *p
, bool del
)
180 if(!__sync_bool_compare_and_swap(&m
->holds_finlock
, 0, 1))
181 runtime_throw("finalizer deadlock");
183 runtime_lock(&finlock
);
184 f
= lookfintab(&fintab
, p
, del
);
185 runtime_unlock(&finlock
);
187 __sync_bool_compare_and_swap(&m
->holds_finlock
, 1, 0);
188 if(__sync_bool_compare_and_swap(&m
->gcing_for_finlock
, 1, 0)) {
189 __go_run_goroutine_gc(201);
196 runtime_walkfintab(void (*fn
)(void*), void (*scan
)(byte
*, int64
))
201 if(!__sync_bool_compare_and_swap(&m
->holds_finlock
, 0, 1))
202 runtime_throw("finalizer deadlock");
204 scan((byte
*)&fintab
, sizeof fintab
);
205 runtime_lock(&finlock
);
207 ekey
= key
+ fintab
.max
;
208 for(; key
< ekey
; key
++)
209 if(*key
!= nil
&& *key
!= ((void*)-1))
211 runtime_unlock(&finlock
);
213 __sync_bool_compare_and_swap(&m
->holds_finlock
, 1, 0);
214 if(__sync_bool_compare_and_swap(&m
->gcing_for_finlock
, 1, 0)) {
215 runtime_throw("walkfintab not called from gc");