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.
11 typedef struct Fin Fin
;
15 const struct __go_func_type
*ft
;
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
25 typedef struct Fintab Fintab
;
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
37 #define TAB(p) (&fintab[((uintptr)(p)>>3)%TABSZ])
41 uint8 pad
[0 /* CacheLineSize - sizeof(Fintab) */];
49 for(i
=0; i
<TABSZ
; i
++)
50 runtime_initlock(&fintab
[i
]);
54 addfintab(Fintab
*t
, void *k
, void (*fn
)(void*), const struct __go_func_type
*ft
)
58 i
= (uintptr
)k
% (uintptr
)t
->max
;
59 for(j
=0; j
<t
->max
; j
++) {
60 if(t
->fkey
[i
] == nil
) {
64 if(t
->fkey
[i
] == (void*)-1) {
72 // cannot happen - table is known to be non-full
73 runtime_throw("finalizer table inconsistent");
82 lookfintab(Fintab
*t
, void *k
, bool del
, Fin
*f
)
88 i
= (uintptr
)k
% (uintptr
)t
->max
;
89 for(j
=0; j
<t
->max
; j
++) {
96 t
->fkey
[i
] = (void*)-1;
107 // cannot happen - table is known to be non-full
108 runtime_throw("finalizer table inconsistent");
113 resizefintab(Fintab
*tab
)
119 runtime_memclr((byte
*)&newtab
, sizeof newtab
);
120 newtab
.max
= tab
->max
;
123 else if(tab
->ndead
< tab
->nkey
/2) {
124 // grow table if not many dead values.
125 // otherwise just rehash into table of same size.
129 newtab
.fkey
= runtime_mallocgc(newtab
.max
*sizeof newtab
.fkey
[0], FlagNoPointers
, 0, 1);
130 newtab
.val
= runtime_mallocgc(newtab
.max
*sizeof newtab
.val
[0], 0, 0, 1);
132 for(i
=0; i
<tab
->max
; i
++) {
134 if(k
!= nil
&& k
!= (void*)-1)
135 addfintab(&newtab
, k
, tab
->val
[i
].fn
, tab
->val
[i
].ft
);
138 runtime_free(tab
->fkey
);
139 runtime_free(tab
->val
);
141 tab
->fkey
= newtab
.fkey
;
142 tab
->val
= newtab
.val
;
143 tab
->nkey
= newtab
.nkey
;
144 tab
->ndead
= newtab
.ndead
;
145 tab
->max
= newtab
.max
;
149 runtime_addfinalizer(void *p
, void (*f
)(void*), const struct __go_func_type
*ft
)
156 if(!runtime_mlookup(p
, &base
, nil
, nil
) || p
!= base
)
157 runtime_throw("addfinalizer on invalid pointer");
160 if(!__sync_bool_compare_and_swap(&m
->holds_finlock
, 0, 1))
161 runtime_throw("finalizer deadlock");
166 if(lookfintab(tab
, p
, true, nil
))
167 runtime_setblockspecial(p
, false);
172 if(lookfintab(tab
, p
, false, nil
)) {
177 if(tab
->nkey
>= tab
->max
/2+tab
->max
/4) {
178 // keep table at most 3/4 full:
179 // allocate new table and rehash.
183 addfintab(tab
, p
, f
, ft
);
184 runtime_setblockspecial(p
, true);
190 __sync_bool_compare_and_swap(&m
->holds_finlock
, 1, 0);
192 if(__sync_bool_compare_and_swap(&m
->gcing_for_finlock
, 1, 0)) {
193 __go_run_goroutine_gc(200);
199 // get finalizer; if del, delete finalizer.
200 // caller is responsible for updating RefHasFinalizer (special) bit.
202 runtime_getfinalizer(void *p
, bool del
, void (**fn
)(void*), const struct __go_func_type
**ft
)
208 if(!__sync_bool_compare_and_swap(&m
->holds_finlock
, 0, 1))
209 runtime_throw("finalizer deadlock");
213 res
= lookfintab(tab
, p
, del
, &f
);
216 __sync_bool_compare_and_swap(&m
->holds_finlock
, 1, 0);
217 if(__sync_bool_compare_and_swap(&m
->gcing_for_finlock
, 1, 0)) {
218 __go_run_goroutine_gc(201);
229 runtime_walkfintab(void (*fn
)(void*), void (*scan
)(byte
*, int64
))
235 if(!__sync_bool_compare_and_swap(&m
->holds_finlock
, 0, 1))
236 runtime_throw("finalizer deadlock");
238 for(i
=0; i
<TABSZ
; i
++) {
239 runtime_lock(&fintab
[i
]);
240 key
= fintab
[i
].fkey
;
241 ekey
= key
+ fintab
[i
].max
;
242 for(; key
< ekey
; key
++)
243 if(*key
!= nil
&& *key
!= ((void*)-1))
245 scan((byte
*)&fintab
[i
].fkey
, sizeof(void*));
246 scan((byte
*)&fintab
[i
].val
, sizeof(void*));
247 runtime_unlock(&fintab
[i
]);
250 __sync_bool_compare_and_swap(&m
->holds_finlock
, 1, 0);
251 if(__sync_bool_compare_and_swap(&m
->gcing_for_finlock
, 1, 0)) {
252 runtime_throw("walkfintab not called from gc");