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.
12 typedef struct Fin Fin
;
16 const struct __go_func_type
*ft
;
17 const struct __go_ptr_type
*ot
;
20 // Finalizer hash table. Direct hash, linear scan, at most 3/4 full.
21 // Table size is power of 3 so that hash can be key % max.
22 // Key[i] == (void*)-1 denotes free but formerly occupied entry
23 // (doesn't stop the linear scan).
24 // Key and val are separate tables because the garbage collector
25 // must be instructed to ignore the pointers in key but follow the
27 typedef struct Fintab Fintab
;
33 int32 nkey
; // number of non-nil entries in key
34 int32 ndead
; // number of dead (-1) entries in key
35 int32 max
; // size of key, val allocations
39 #define TAB(p) (&fintab[((uintptr)(p)>>3)%TABSZ])
43 uint8 pad
[0 /* CacheLineSize - sizeof(Fintab) */];
47 addfintab(Fintab
*t
, void *k
, FuncVal
*fn
, const struct __go_func_type
*ft
, const struct __go_ptr_type
*ot
)
51 i
= (uintptr
)k
% (uintptr
)t
->max
;
52 for(j
=0; j
<t
->max
; j
++) {
53 if(t
->fkey
[i
] == nil
) {
57 if(t
->fkey
[i
] == (void*)-1) {
65 // cannot happen - table is known to be non-full
66 runtime_throw("finalizer table inconsistent");
76 lookfintab(Fintab
*t
, void *k
, bool del
, Fin
*f
)
82 i
= (uintptr
)k
% (uintptr
)t
->max
;
83 for(j
=0; j
<t
->max
; j
++) {
90 t
->fkey
[i
] = (void*)-1;
102 // cannot happen - table is known to be non-full
103 runtime_throw("finalizer table inconsistent");
108 resizefintab(Fintab
*tab
)
114 runtime_memclr((byte
*)&newtab
, sizeof newtab
);
115 newtab
.max
= tab
->max
;
118 else if(tab
->ndead
< tab
->nkey
/2) {
119 // grow table if not many dead values.
120 // otherwise just rehash into table of same size.
124 newtab
.fkey
= runtime_mallocgc(newtab
.max
*sizeof newtab
.fkey
[0], 0, FlagNoInvokeGC
|FlagNoScan
);
125 newtab
.val
= runtime_mallocgc(newtab
.max
*sizeof newtab
.val
[0], 0, FlagNoInvokeGC
);
127 for(i
=0; i
<tab
->max
; i
++) {
129 if(k
!= nil
&& k
!= (void*)-1)
130 addfintab(&newtab
, k
, tab
->val
[i
].fn
, tab
->val
[i
].ft
, tab
->val
[i
].ot
);
133 runtime_free(tab
->fkey
);
134 runtime_free(tab
->val
);
136 tab
->fkey
= newtab
.fkey
;
137 tab
->val
= newtab
.val
;
138 tab
->nkey
= newtab
.nkey
;
139 tab
->ndead
= newtab
.ndead
;
140 tab
->max
= newtab
.max
;
144 runtime_addfinalizer(void *p
, FuncVal
*f
, const struct __go_func_type
*ft
, const struct __go_ptr_type
*ot
)
150 if(!runtime_mlookup(p
, &base
, nil
, nil
) || p
!= base
)
151 runtime_throw("addfinalizer on invalid pointer");
157 lookfintab(tab
, p
, true, nil
);
162 if(lookfintab(tab
, p
, false, nil
)) {
167 if(tab
->nkey
>= tab
->max
/2+tab
->max
/4) {
168 // keep table at most 3/4 full:
169 // allocate new table and rehash.
173 addfintab(tab
, p
, f
, ft
, ot
);
174 runtime_setblockspecial(p
, true);
179 // get finalizer; if del, delete finalizer.
180 // caller is responsible for updating RefHasFinalizer (special) bit.
182 runtime_getfinalizer(void *p
, bool del
, FuncVal
**fn
, const struct __go_func_type
**ft
, const struct __go_ptr_type
**ot
)
190 res
= lookfintab(tab
, p
, del
, &f
);
201 runtime_walkfintab(void (*fn
)(void*), void (*addroot
)(Obj
))
207 for(i
=0; i
<TABSZ
; i
++) {
208 runtime_lock(&fintab
[i
]);
209 key
= fintab
[i
].fkey
;
210 ekey
= key
+ fintab
[i
].max
;
211 for(; key
< ekey
; key
++)
212 if(*key
!= nil
&& *key
!= ((void*)-1))
214 addroot((Obj
){(byte
*)&fintab
[i
].fkey
, sizeof(void*), 0});
215 addroot((Obj
){(byte
*)&fintab
[i
].val
, sizeof(void*), 0});
216 runtime_unlock(&fintab
[i
]);