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.
8 // Lock to protect finalizer data structures.
9 // Cannot reuse mheap.Lock because the finalizer
10 // maintenance requires allocation.
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
26 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 addfintab(Fintab
*t
, void *k
, Finalizer
*v
)
41 i
= (uintptr
)k
% (uintptr
)t
->max
;
42 for(j
=0; j
<t
->max
; j
++) {
43 if(t
->key
[i
] == nil
) {
47 if(t
->key
[i
] == (void*)-1) {
55 // cannot happen - table is known to be non-full
56 runtime_throw("finalizer table inconsistent");
64 lookfintab(Fintab
*t
, void *k
, bool del
)
71 i
= (uintptr
)k
% (uintptr
)t
->max
;
72 for(j
=0; j
<t
->max
; j
++) {
78 t
->key
[i
] = (void*)-1;
88 // cannot happen - table is known to be non-full
89 runtime_throw("finalizer table inconsistent");
95 // add finalizer; caller is responsible for making sure not already in table
97 runtime_addfinalizer(void *p
, void (*f
)(void*), const struct __go_func_type
*ft
)
106 e
= runtime_mal(sizeof *e
);
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");
121 lookfintab(&fintab
, p
, 1);
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
;
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.
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
++) {
153 if(k
!= nil
&& k
!= (void*)-1)
154 addfintab(&newtab
, k
, fintab
.val
[i
]);
156 runtime_free(fintab
.key
);
157 runtime_free(fintab
.val
);
161 addfintab(&fintab
, p
, e
);
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.
175 runtime_getfinalizer(void *p
, bool del
)
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);
195 runtime_walkfintab(void (*fn
)(void*), void (*scan
)(byte
*, int64
))
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
);
206 ekey
= key
+ fintab
.max
;
207 for(; key
< ekey
; key
++)
208 if(*key
!= nil
&& *key
!= ((void*)-1))
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");