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) */];
45 addfintab(Fintab
*t
, void *k
, void (*fn
)(void*), const struct __go_func_type
*ft
)
49 i
= (uintptr
)k
% (uintptr
)t
->max
;
50 for(j
=0; j
<t
->max
; j
++) {
51 if(t
->fkey
[i
] == nil
) {
55 if(t
->fkey
[i
] == (void*)-1) {
63 // cannot happen - table is known to be non-full
64 runtime_throw("finalizer table inconsistent");
73 lookfintab(Fintab
*t
, void *k
, bool del
, Fin
*f
)
79 i
= (uintptr
)k
% (uintptr
)t
->max
;
80 for(j
=0; j
<t
->max
; j
++) {
87 t
->fkey
[i
] = (void*)-1;
98 // cannot happen - table is known to be non-full
99 runtime_throw("finalizer table inconsistent");
104 resizefintab(Fintab
*tab
)
110 runtime_memclr((byte
*)&newtab
, sizeof newtab
);
111 newtab
.max
= tab
->max
;
114 else if(tab
->ndead
< tab
->nkey
/2) {
115 // grow table if not many dead values.
116 // otherwise just rehash into table of same size.
120 newtab
.fkey
= runtime_mallocgc(newtab
.max
*sizeof newtab
.fkey
[0], FlagNoPointers
, 0, 1);
121 newtab
.val
= runtime_mallocgc(newtab
.max
*sizeof newtab
.val
[0], 0, 0, 1);
123 for(i
=0; i
<tab
->max
; i
++) {
125 if(k
!= nil
&& k
!= (void*)-1)
126 addfintab(&newtab
, k
, tab
->val
[i
].fn
, tab
->val
[i
].ft
);
129 runtime_free(tab
->fkey
);
130 runtime_free(tab
->val
);
132 tab
->fkey
= newtab
.fkey
;
133 tab
->val
= newtab
.val
;
134 tab
->nkey
= newtab
.nkey
;
135 tab
->ndead
= newtab
.ndead
;
136 tab
->max
= newtab
.max
;
140 runtime_addfinalizer(void *p
, void (*f
)(void*), const struct __go_func_type
*ft
)
146 if(!runtime_mlookup(p
, &base
, nil
, nil
) || p
!= base
)
147 runtime_throw("addfinalizer on invalid pointer");
153 lookfintab(tab
, p
, true, nil
);
158 if(lookfintab(tab
, p
, false, nil
)) {
163 if(tab
->nkey
>= tab
->max
/2+tab
->max
/4) {
164 // keep table at most 3/4 full:
165 // allocate new table and rehash.
169 addfintab(tab
, p
, f
, ft
);
170 runtime_setblockspecial(p
, true);
175 // get finalizer; if del, delete finalizer.
176 // caller is responsible for updating RefHasFinalizer (special) bit.
178 runtime_getfinalizer(void *p
, bool del
, void (**fn
)(void*), const struct __go_func_type
**ft
)
186 res
= lookfintab(tab
, p
, del
, &f
);
196 runtime_walkfintab(void (*fn
)(void*), void (*addroot
)(Obj
))
202 for(i
=0; i
<TABSZ
; i
++) {
203 runtime_lock(&fintab
[i
]);
204 key
= fintab
[i
].fkey
;
205 ekey
= key
+ fintab
[i
].max
;
206 for(; key
< ekey
; key
++)
207 if(*key
!= nil
&& *key
!= ((void*)-1))
209 addroot((Obj
){(byte
*)&fintab
[i
].fkey
, sizeof(void*), 0});
210 addroot((Obj
){(byte
*)&fintab
[i
].val
, sizeof(void*), 0});
211 runtime_unlock(&fintab
[i
]);