Line data Source code
1 : /*
2 : * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3 : * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved.
4 : * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved.
5 : * Copyright (C) 2007 Free Software Foundation, Inc
6 :
7 : * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
8 : * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
9 : *
10 : * Permission is hereby granted to use or copy this program
11 : * for any purpose, provided the above notices are retained on all copies.
12 : * Permission to modify the code and to distribute modified code is granted,
13 : * provided the above notices are retained, and a notice that the code was
14 : * modified is included with the above copyright notice.
15 : */
16 :
17 : #include "private/gc_pmark.h"
18 :
19 : #ifndef GC_NO_FINALIZATION
20 :
21 : /* Type of mark procedure used for marking from finalizable object. */
22 : /* This procedure normally does not mark the object, only its */
23 : /* descendants. */
24 : typedef void (* finalization_mark_proc)(ptr_t /* finalizable_obj_ptr */);
25 :
26 : #define HASH3(addr,size,log_size) \
27 : ((((word)(addr) >> 3) ^ ((word)(addr) >> (3 + (log_size)))) \
28 : & ((size) - 1))
29 : #define HASH2(addr,log_size) HASH3(addr, 1 << (log_size), log_size)
30 :
31 : struct hash_chain_entry {
32 : word hidden_key;
33 : struct hash_chain_entry * next;
34 : };
35 :
36 : struct disappearing_link {
37 : struct hash_chain_entry prolog;
38 : # define dl_hidden_link prolog.hidden_key
39 : /* Field to be cleared. */
40 : # define dl_next(x) (struct disappearing_link *)((x) -> prolog.next)
41 : # define dl_set_next(x, y) \
42 : (void)((x)->prolog.next = (struct hash_chain_entry *)(y))
43 : word dl_hidden_obj; /* Pointer to object base */
44 : };
45 :
46 : struct dl_hashtbl_s {
47 : struct disappearing_link **head;
48 : signed_word log_size;
49 : word entries;
50 : };
51 :
52 : STATIC struct dl_hashtbl_s GC_dl_hashtbl = {
53 : /* head */ NULL, /* log_size */ -1, /* entries */ 0 };
54 : #ifndef GC_LONG_REFS_NOT_NEEDED
55 : STATIC struct dl_hashtbl_s GC_ll_hashtbl = { NULL, -1, 0 };
56 : #endif
57 :
58 : STATIC struct finalizable_object {
59 : struct hash_chain_entry prolog;
60 : # define fo_hidden_base prolog.hidden_key
61 : /* Pointer to object base. */
62 : /* No longer hidden once object */
63 : /* is on finalize_now queue. */
64 : # define fo_next(x) (struct finalizable_object *)((x) -> prolog.next)
65 : # define fo_set_next(x,y) ((x)->prolog.next = (struct hash_chain_entry *)(y))
66 : GC_finalization_proc fo_fn; /* Finalizer. */
67 : ptr_t fo_client_data;
68 : word fo_object_size; /* In bytes. */
69 : finalization_mark_proc fo_mark_proc; /* Mark-through procedure */
70 : } **GC_fo_head = 0;
71 :
72 : STATIC struct finalizable_object * GC_finalize_now = 0;
73 : /* List of objects that should be finalized now. */
74 :
75 : static signed_word log_fo_table_size = -1;
76 :
77 0 : GC_INNER void GC_push_finalizer_structures(void)
78 : {
79 : GC_ASSERT((word)&GC_dl_hashtbl.head % sizeof(word) == 0);
80 : GC_ASSERT((word)&GC_fo_head % sizeof(word) == 0);
81 : GC_ASSERT((word)&GC_finalize_now % sizeof(word) == 0);
82 :
83 : # ifndef GC_LONG_REFS_NOT_NEEDED
84 : GC_ASSERT((word)&GC_ll_hashtbl.head % sizeof(word) == 0);
85 0 : GC_push_all((ptr_t)(&GC_ll_hashtbl.head),
86 : (ptr_t)(&GC_ll_hashtbl.head) + sizeof(word));
87 : # endif
88 :
89 0 : GC_push_all((ptr_t)(&GC_dl_hashtbl.head),
90 : (ptr_t)(&GC_dl_hashtbl.head) + sizeof(word));
91 0 : GC_push_all((ptr_t)(&GC_fo_head), (ptr_t)(&GC_fo_head) + sizeof(word));
92 0 : GC_push_all((ptr_t)(&GC_finalize_now),
93 : (ptr_t)(&GC_finalize_now) + sizeof(word));
94 0 : }
95 :
96 : /* Double the size of a hash table. *size_ptr is the log of its current */
97 : /* size. May be a no-op. */
98 : /* *table is a pointer to an array of hash headers. If we succeed, we */
99 : /* update both *table and *log_size_ptr. Lock is held. */
100 804 : STATIC void GC_grow_table(struct hash_chain_entry ***table,
101 : signed_word *log_size_ptr)
102 : {
103 : register word i;
104 : register struct hash_chain_entry *p;
105 804 : signed_word log_old_size = *log_size_ptr;
106 804 : signed_word log_new_size = log_old_size + 1;
107 804 : word old_size = ((log_old_size == -1)? 0: (1 << log_old_size));
108 804 : word new_size = (word)1 << log_new_size;
109 : /* FIXME: Power of 2 size often gets rounded up to one more page. */
110 804 : struct hash_chain_entry **new_table = (struct hash_chain_entry **)
111 : GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE(
112 804 : (size_t)new_size * sizeof(struct hash_chain_entry *), NORMAL);
113 :
114 804 : if (new_table == 0) {
115 0 : if (*table == 0) {
116 0 : ABORT("Insufficient space for initial table allocation");
117 : } else {
118 0 : return;
119 : }
120 : }
121 69901 : for (i = 0; i < old_size; i++) {
122 69097 : p = (*table)[i];
123 207932 : while (p != 0) {
124 69738 : ptr_t real_key = GC_REVEAL_POINTER(p -> hidden_key);
125 69738 : struct hash_chain_entry *next = p -> next;
126 69738 : size_t new_hash = HASH3(real_key, new_size, log_new_size);
127 :
128 69738 : p -> next = new_table[new_hash];
129 69738 : new_table[new_hash] = p;
130 69738 : p = next;
131 : }
132 : }
133 804 : *log_size_ptr = log_new_size;
134 804 : *table = new_table;
135 : }
136 :
137 0 : GC_API int GC_CALL GC_register_disappearing_link(void * * link)
138 : {
139 : ptr_t base;
140 :
141 0 : base = (ptr_t)GC_base(link);
142 0 : if (base == 0)
143 0 : ABORT("Bad arg to GC_register_disappearing_link");
144 0 : return(GC_general_register_disappearing_link(link, base));
145 : }
146 :
147 0 : STATIC int GC_register_disappearing_link_inner(
148 : struct dl_hashtbl_s *dl_hashtbl, void **link,
149 : const void *obj)
150 : {
151 : struct disappearing_link *curr_dl;
152 : size_t index;
153 : struct disappearing_link * new_dl;
154 : DCL_LOCK_STATE;
155 :
156 0 : LOCK();
157 : GC_ASSERT(obj != NULL && GC_base_C(obj) == obj);
158 0 : if (dl_hashtbl -> log_size == -1
159 0 : || dl_hashtbl -> entries > ((word)1 << dl_hashtbl -> log_size)) {
160 0 : GC_grow_table((struct hash_chain_entry ***)&dl_hashtbl -> head,
161 : &dl_hashtbl -> log_size);
162 0 : GC_COND_LOG_PRINTF("Grew dl table to %u entries\n",
163 : 1 << (unsigned)dl_hashtbl -> log_size);
164 : }
165 0 : index = HASH2(link, dl_hashtbl -> log_size);
166 0 : for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0;
167 0 : curr_dl = dl_next(curr_dl)) {
168 0 : if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
169 0 : curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
170 0 : UNLOCK();
171 0 : return GC_DUPLICATE;
172 : }
173 : }
174 0 : new_dl = (struct disappearing_link *)
175 : GC_INTERNAL_MALLOC(sizeof(struct disappearing_link),NORMAL);
176 0 : if (0 == new_dl) {
177 0 : GC_oom_func oom_fn = GC_oom_fn;
178 0 : UNLOCK();
179 0 : new_dl = (struct disappearing_link *)
180 : (*oom_fn)(sizeof(struct disappearing_link));
181 0 : if (0 == new_dl) {
182 0 : return GC_NO_MEMORY;
183 : }
184 : /* It's not likely we'll make it here, but ... */
185 0 : LOCK();
186 : /* Recalculate index since the table may grow. */
187 0 : index = HASH2(link, dl_hashtbl -> log_size);
188 : /* Check again that our disappearing link not in the table. */
189 0 : for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0;
190 0 : curr_dl = dl_next(curr_dl)) {
191 0 : if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
192 0 : curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
193 0 : UNLOCK();
194 : # ifndef DBG_HDRS_ALL
195 : /* Free unused new_dl returned by GC_oom_fn() */
196 0 : GC_free((void *)new_dl);
197 : # endif
198 0 : return GC_DUPLICATE;
199 : }
200 : }
201 : }
202 0 : new_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
203 0 : new_dl -> dl_hidden_link = GC_HIDE_POINTER(link);
204 0 : dl_set_next(new_dl, dl_hashtbl -> head[index]);
205 0 : dl_hashtbl -> head[index] = new_dl;
206 0 : dl_hashtbl -> entries++;
207 0 : UNLOCK();
208 0 : return GC_SUCCESS;
209 : }
210 :
211 0 : GC_API int GC_CALL GC_general_register_disappearing_link(void * * link,
212 : const void * obj)
213 : {
214 0 : if (((word)link & (ALIGNMENT-1)) != 0 || NULL == link)
215 0 : ABORT("Bad arg to GC_general_register_disappearing_link");
216 0 : return GC_register_disappearing_link_inner(&GC_dl_hashtbl, link, obj);
217 : }
218 :
219 : #ifdef DBG_HDRS_ALL
220 : # define FREE_DL_ENTRY(curr_dl) dl_set_next(curr_dl, NULL)
221 : #else
222 : # define FREE_DL_ENTRY(curr_dl) GC_free(curr_dl)
223 : #endif
224 :
225 : /* Unregisters given link and returns the link entry to free. */
226 : /* Assume the lock is held. */
227 0 : GC_INLINE struct disappearing_link *GC_unregister_disappearing_link_inner(
228 : struct dl_hashtbl_s *dl_hashtbl, void **link)
229 : {
230 : struct disappearing_link *curr_dl;
231 0 : struct disappearing_link *prev_dl = NULL;
232 0 : size_t index = HASH2(link, dl_hashtbl->log_size);
233 :
234 0 : for (curr_dl = dl_hashtbl -> head[index]; curr_dl;
235 0 : curr_dl = dl_next(curr_dl)) {
236 0 : if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
237 : /* Remove found entry from the table. */
238 0 : if (NULL == prev_dl) {
239 0 : dl_hashtbl -> head[index] = dl_next(curr_dl);
240 : } else {
241 0 : dl_set_next(prev_dl, dl_next(curr_dl));
242 : }
243 0 : dl_hashtbl -> entries--;
244 0 : break;
245 : }
246 0 : prev_dl = curr_dl;
247 : }
248 0 : return curr_dl;
249 : }
250 :
251 0 : GC_API int GC_CALL GC_unregister_disappearing_link(void * * link)
252 : {
253 : struct disappearing_link *curr_dl;
254 : DCL_LOCK_STATE;
255 :
256 0 : if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */
257 :
258 0 : LOCK();
259 0 : curr_dl = GC_unregister_disappearing_link_inner(&GC_dl_hashtbl, link);
260 0 : UNLOCK();
261 0 : if (NULL == curr_dl) return 0;
262 0 : FREE_DL_ENTRY(curr_dl);
263 0 : return 1;
264 : }
265 :
266 : #ifndef GC_LONG_REFS_NOT_NEEDED
267 0 : GC_API int GC_CALL GC_register_long_link(void * * link, const void * obj)
268 : {
269 0 : if (((word)link & (ALIGNMENT-1)) != 0 || NULL == link)
270 0 : ABORT("Bad arg to GC_register_long_link");
271 0 : return GC_register_disappearing_link_inner(&GC_ll_hashtbl, link, obj);
272 : }
273 :
274 0 : GC_API int GC_CALL GC_unregister_long_link(void * * link)
275 : {
276 : struct disappearing_link *curr_dl;
277 : DCL_LOCK_STATE;
278 :
279 0 : if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */
280 :
281 0 : LOCK();
282 0 : curr_dl = GC_unregister_disappearing_link_inner(&GC_ll_hashtbl, link);
283 0 : UNLOCK();
284 0 : if (NULL == curr_dl) return 0;
285 0 : FREE_DL_ENTRY(curr_dl);
286 0 : return 1;
287 : }
288 : #endif /* !GC_LONG_REFS_NOT_NEEDED */
289 :
290 : #ifndef GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED
291 : /* Moves a link. Assume the lock is held. */
292 0 : STATIC int GC_move_disappearing_link_inner(
293 : struct dl_hashtbl_s *dl_hashtbl,
294 : void **link, void **new_link)
295 : {
296 : struct disappearing_link *curr_dl, *prev_dl, *new_dl;
297 : size_t curr_index, new_index;
298 : word curr_hidden_link;
299 : word new_hidden_link;
300 :
301 : /* Find current link. */
302 0 : curr_index = HASH2(link, dl_hashtbl -> log_size);
303 0 : curr_hidden_link = GC_HIDE_POINTER(link);
304 0 : prev_dl = NULL;
305 0 : for (curr_dl = dl_hashtbl -> head[curr_index]; curr_dl;
306 0 : curr_dl = dl_next(curr_dl)) {
307 0 : if (curr_dl -> dl_hidden_link == curr_hidden_link)
308 0 : break;
309 0 : prev_dl = curr_dl;
310 : }
311 :
312 0 : if (NULL == curr_dl) {
313 0 : return GC_NOT_FOUND;
314 : }
315 :
316 0 : if (link == new_link) {
317 0 : return GC_SUCCESS; /* Nothing to do. */
318 : }
319 :
320 : /* link found; now check new_link not present. */
321 0 : new_index = HASH2(new_link, dl_hashtbl -> log_size);
322 0 : new_hidden_link = GC_HIDE_POINTER(new_link);
323 0 : for (new_dl = dl_hashtbl -> head[new_index]; new_dl;
324 0 : new_dl = dl_next(new_dl)) {
325 0 : if (new_dl -> dl_hidden_link == new_hidden_link) {
326 : /* Target already registered; bail. */
327 0 : return GC_DUPLICATE;
328 : }
329 : }
330 :
331 : /* Remove from old, add to new, update link. */
332 0 : if (NULL == prev_dl) {
333 0 : dl_hashtbl -> head[curr_index] = dl_next(curr_dl);
334 : } else {
335 0 : dl_set_next(prev_dl, dl_next(curr_dl));
336 : }
337 0 : curr_dl -> dl_hidden_link = new_hidden_link;
338 0 : dl_set_next(curr_dl, dl_hashtbl -> head[new_index]);
339 0 : dl_hashtbl -> head[new_index] = curr_dl;
340 0 : return GC_SUCCESS;
341 : }
342 :
343 0 : GC_API int GC_CALL GC_move_disappearing_link(void **link, void **new_link)
344 : {
345 : int result;
346 : DCL_LOCK_STATE;
347 :
348 0 : if (((word)new_link & (ALIGNMENT-1)) != 0 || new_link == NULL)
349 0 : ABORT("Bad new_link arg to GC_move_disappearing_link");
350 0 : if (((word)link & (ALIGNMENT-1)) != 0)
351 0 : return GC_NOT_FOUND; /* Nothing to do. */
352 :
353 0 : LOCK();
354 0 : result = GC_move_disappearing_link_inner(&GC_dl_hashtbl, link, new_link);
355 0 : UNLOCK();
356 0 : return result;
357 : }
358 :
359 : # ifndef GC_LONG_REFS_NOT_NEEDED
360 0 : GC_API int GC_CALL GC_move_long_link(void **link, void **new_link)
361 : {
362 : int result;
363 : DCL_LOCK_STATE;
364 :
365 0 : if (((word)new_link & (ALIGNMENT-1)) != 0 || new_link == NULL)
366 0 : ABORT("Bad new_link arg to GC_move_disappearing_link");
367 0 : if (((word)link & (ALIGNMENT-1)) != 0)
368 0 : return GC_NOT_FOUND; /* Nothing to do. */
369 :
370 0 : LOCK();
371 0 : result = GC_move_disappearing_link_inner(&GC_ll_hashtbl, link, new_link);
372 0 : UNLOCK();
373 0 : return result;
374 : }
375 : # endif /* !GC_LONG_REFS_NOT_NEEDED */
376 : #endif /* !GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED */
377 :
378 : /* Possible finalization_marker procedures. Note that mark stack */
379 : /* overflow is handled by the caller, and is not a disaster. */
380 35093 : STATIC void GC_normal_finalize_mark_proc(ptr_t p)
381 : {
382 35093 : hdr * hhdr = HDR(p);
383 :
384 35093 : PUSH_OBJ(p, hhdr, GC_mark_stack_top,
385 : &(GC_mark_stack[GC_mark_stack_size]));
386 35093 : }
387 :
388 : /* This only pays very partial attention to the mark descriptor. */
389 : /* It does the right thing for normal and atomic objects, and treats */
390 : /* most others as normal. */
391 0 : STATIC void GC_ignore_self_finalize_mark_proc(ptr_t p)
392 : {
393 0 : hdr * hhdr = HDR(p);
394 0 : word descr = hhdr -> hb_descr;
395 : ptr_t q;
396 : word r;
397 : ptr_t scan_limit;
398 0 : ptr_t target_limit = p + hhdr -> hb_sz - 1;
399 :
400 0 : if ((descr & GC_DS_TAGS) == GC_DS_LENGTH) {
401 0 : scan_limit = p + descr - sizeof(word);
402 : } else {
403 0 : scan_limit = target_limit + 1 - sizeof(word);
404 : }
405 0 : for (q = p; (word)q <= (word)scan_limit; q += ALIGNMENT) {
406 0 : r = *(word *)q;
407 0 : if (r < (word)p || r > (word)target_limit) {
408 0 : GC_PUSH_ONE_HEAP(r, q, GC_mark_stack_top);
409 : }
410 : }
411 0 : }
412 :
413 18287 : STATIC void GC_null_finalize_mark_proc(ptr_t p GC_ATTR_UNUSED) {}
414 :
415 : /* Possible finalization_marker procedures. Note that mark stack */
416 : /* overflow is handled by the caller, and is not a disaster. */
417 :
418 : /* GC_unreachable_finalize_mark_proc is an alias for normal marking, */
419 : /* but it is explicitly tested for, and triggers different */
420 : /* behavior. Objects registered in this way are not finalized */
421 : /* if they are reachable by other finalizable objects, even if those */
422 : /* other objects specify no ordering. */
423 17167 : STATIC void GC_unreachable_finalize_mark_proc(ptr_t p)
424 : {
425 17167 : GC_normal_finalize_mark_proc(p);
426 17167 : }
427 :
428 : /* Register a finalization function. See gc.h for details. */
429 : /* The last parameter is a procedure that determines */
430 : /* marking for finalization ordering. Any objects marked */
431 : /* by that procedure will be guaranteed to not have been */
432 : /* finalized when this finalizer is invoked. */
433 43959 : STATIC void GC_register_finalizer_inner(void * obj,
434 : GC_finalization_proc fn, void *cd,
435 : GC_finalization_proc *ofn, void **ocd,
436 : finalization_mark_proc mp)
437 : {
438 : ptr_t base;
439 : struct finalizable_object * curr_fo, * prev_fo;
440 : size_t index;
441 43959 : struct finalizable_object *new_fo = 0;
442 43959 : hdr *hhdr = NULL; /* initialized to prevent warning. */
443 : GC_oom_func oom_fn;
444 : DCL_LOCK_STATE;
445 :
446 43959 : LOCK();
447 87755 : if (log_fo_table_size == -1
448 87755 : || GC_fo_entries > ((word)1 << log_fo_table_size)) {
449 804 : GC_grow_table((struct hash_chain_entry ***)&GC_fo_head,
450 : &log_fo_table_size);
451 804 : GC_COND_LOG_PRINTF("Grew fo table to %u entries\n",
452 : 1 << (unsigned)log_fo_table_size);
453 : }
454 : /* in the THREADS case we hold allocation lock. */
455 43959 : base = (ptr_t)obj;
456 : for (;;) {
457 43959 : index = HASH2(base, log_fo_table_size);
458 43959 : prev_fo = 0;
459 43959 : curr_fo = GC_fo_head[index];
460 149663 : while (curr_fo != 0) {
461 : GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object));
462 61745 : if (curr_fo -> fo_hidden_base == GC_HIDE_POINTER(base)) {
463 : /* Interruption by a signal in the middle of this */
464 : /* should be safe. The client may see only *ocd */
465 : /* updated, but we'll declare that to be his problem. */
466 0 : if (ocd) *ocd = (void *) (curr_fo -> fo_client_data);
467 0 : if (ofn) *ofn = curr_fo -> fo_fn;
468 : /* Delete the structure for base. */
469 0 : if (prev_fo == 0) {
470 0 : GC_fo_head[index] = fo_next(curr_fo);
471 : } else {
472 0 : fo_set_next(prev_fo, fo_next(curr_fo));
473 : }
474 0 : if (fn == 0) {
475 0 : GC_fo_entries--;
476 : /* May not happen if we get a signal. But a high */
477 : /* estimate will only make the table larger than */
478 : /* necessary. */
479 : # if !defined(THREADS) && !defined(DBG_HDRS_ALL)
480 : GC_free((void *)curr_fo);
481 : # endif
482 : } else {
483 0 : curr_fo -> fo_fn = fn;
484 0 : curr_fo -> fo_client_data = (ptr_t)cd;
485 0 : curr_fo -> fo_mark_proc = mp;
486 : /* Reinsert it. We deleted it first to maintain */
487 : /* consistency in the event of a signal. */
488 0 : if (prev_fo == 0) {
489 0 : GC_fo_head[index] = curr_fo;
490 : } else {
491 0 : fo_set_next(prev_fo, curr_fo);
492 : }
493 : }
494 0 : UNLOCK();
495 : # ifndef DBG_HDRS_ALL
496 0 : if (EXPECT(new_fo != 0, FALSE)) {
497 : /* Free unused new_fo returned by GC_oom_fn() */
498 0 : GC_free((void *)new_fo);
499 : }
500 : # endif
501 0 : return;
502 : }
503 61745 : prev_fo = curr_fo;
504 61745 : curr_fo = fo_next(curr_fo);
505 : }
506 43959 : if (EXPECT(new_fo != 0, FALSE)) {
507 : /* new_fo is returned by GC_oom_fn(), so fn != 0 and hhdr != 0. */
508 0 : break;
509 : }
510 43959 : if (fn == 0) {
511 0 : if (ocd) *ocd = 0;
512 0 : if (ofn) *ofn = 0;
513 0 : UNLOCK();
514 0 : return;
515 : }
516 43959 : GET_HDR(base, hhdr);
517 43959 : if (EXPECT(0 == hhdr, FALSE)) {
518 : /* We won't collect it, hence finalizer wouldn't be run. */
519 0 : if (ocd) *ocd = 0;
520 0 : if (ofn) *ofn = 0;
521 0 : UNLOCK();
522 0 : return;
523 : }
524 43959 : new_fo = (struct finalizable_object *)
525 : GC_INTERNAL_MALLOC(sizeof(struct finalizable_object),NORMAL);
526 43959 : if (EXPECT(new_fo != 0, TRUE))
527 43959 : break;
528 0 : oom_fn = GC_oom_fn;
529 0 : UNLOCK();
530 0 : new_fo = (struct finalizable_object *)
531 : (*oom_fn)(sizeof(struct finalizable_object));
532 0 : if (0 == new_fo) {
533 : /* No enough memory. *ocd and *ofn remains unchanged. */
534 0 : return;
535 : }
536 : /* It's not likely we'll make it here, but ... */
537 0 : LOCK();
538 : /* Recalculate index since the table may grow and */
539 : /* check again that our finalizer is not in the table. */
540 0 : }
541 : GC_ASSERT(GC_size(new_fo) >= sizeof(struct finalizable_object));
542 43959 : if (ocd) *ocd = 0;
543 43959 : if (ofn) *ofn = 0;
544 43959 : new_fo -> fo_hidden_base = GC_HIDE_POINTER(base);
545 43959 : new_fo -> fo_fn = fn;
546 43959 : new_fo -> fo_client_data = (ptr_t)cd;
547 43959 : new_fo -> fo_object_size = hhdr -> hb_sz;
548 43959 : new_fo -> fo_mark_proc = mp;
549 43959 : fo_set_next(new_fo, GC_fo_head[index]);
550 43959 : GC_fo_entries++;
551 43959 : GC_fo_head[index] = new_fo;
552 43959 : UNLOCK();
553 : }
554 :
555 0 : GC_API void GC_CALL GC_register_finalizer(void * obj,
556 : GC_finalization_proc fn, void * cd,
557 : GC_finalization_proc *ofn, void ** ocd)
558 : {
559 0 : GC_register_finalizer_inner(obj, fn, cd, ofn,
560 : ocd, GC_normal_finalize_mark_proc);
561 0 : }
562 :
563 0 : GC_API void GC_CALL GC_register_finalizer_ignore_self(void * obj,
564 : GC_finalization_proc fn, void * cd,
565 : GC_finalization_proc *ofn, void ** ocd)
566 : {
567 0 : GC_register_finalizer_inner(obj, fn, cd, ofn,
568 : ocd, GC_ignore_self_finalize_mark_proc);
569 0 : }
570 :
571 23453 : GC_API void GC_CALL GC_register_finalizer_no_order(void * obj,
572 : GC_finalization_proc fn, void * cd,
573 : GC_finalization_proc *ofn, void ** ocd)
574 : {
575 23453 : GC_register_finalizer_inner(obj, fn, cd, ofn,
576 : ocd, GC_null_finalize_mark_proc);
577 23453 : }
578 :
579 : static GC_bool need_unreachable_finalization = FALSE;
580 : /* Avoid the work if this isn't used. */
581 :
582 20506 : GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj,
583 : GC_finalization_proc fn, void * cd,
584 : GC_finalization_proc *ofn, void ** ocd)
585 : {
586 20506 : need_unreachable_finalization = TRUE;
587 : GC_ASSERT(GC_java_finalization);
588 20506 : GC_register_finalizer_inner(obj, fn, cd, ofn,
589 : ocd, GC_unreachable_finalize_mark_proc);
590 20506 : }
591 :
592 : #ifndef NO_DEBUGGING
593 0 : STATIC void GC_dump_finalization_links(
594 : const struct dl_hashtbl_s *dl_hashtbl)
595 : {
596 : struct disappearing_link *curr_dl;
597 : ptr_t real_ptr, real_link;
598 : size_t dl_size = dl_hashtbl->log_size == -1 ? 0 :
599 0 : 1 << dl_hashtbl->log_size;
600 : size_t i;
601 :
602 0 : for (i = 0; i < dl_size; i++) {
603 0 : for (curr_dl = dl_hashtbl -> head[i]; curr_dl != 0;
604 0 : curr_dl = dl_next(curr_dl)) {
605 0 : real_ptr = GC_REVEAL_POINTER(curr_dl -> dl_hidden_obj);
606 0 : real_link = GC_REVEAL_POINTER(curr_dl -> dl_hidden_link);
607 0 : GC_printf("Object: %p, link: %p\n", real_ptr, real_link);
608 : }
609 : }
610 0 : }
611 :
612 0 : void GC_dump_finalization(void)
613 : {
614 : struct finalizable_object * curr_fo;
615 0 : size_t fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size;
616 : ptr_t real_ptr;
617 : size_t i;
618 :
619 0 : GC_printf("Disappearing (short) links:\n");
620 0 : GC_dump_finalization_links(&GC_dl_hashtbl);
621 : # ifndef GC_LONG_REFS_NOT_NEEDED
622 0 : GC_printf("Disappearing long links:\n");
623 0 : GC_dump_finalization_links(&GC_ll_hashtbl);
624 : # endif
625 0 : GC_printf("Finalizers:\n");
626 0 : for (i = 0; i < fo_size; i++) {
627 0 : for (curr_fo = GC_fo_head[i]; curr_fo != 0;
628 0 : curr_fo = fo_next(curr_fo)) {
629 0 : real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
630 0 : GC_printf("Finalizable object: %p\n", real_ptr);
631 : }
632 : }
633 0 : }
634 : #endif /* !NO_DEBUGGING */
635 :
636 : #ifndef SMALL_CONFIG
637 : STATIC word GC_old_dl_entries = 0; /* for stats printing */
638 : # ifndef GC_LONG_REFS_NOT_NEEDED
639 : STATIC word GC_old_ll_entries = 0;
640 : # endif
641 : #endif /* !SMALL_CONFIG */
642 :
643 : #ifndef THREADS
644 : /* Global variables to minimize the level of recursion when a client */
645 : /* finalizer allocates memory. */
646 : STATIC int GC_finalizer_nested = 0;
647 : /* Only the lowest byte is used, the rest is */
648 : /* padding for proper global data alignment */
649 : /* required for some compilers (like Watcom). */
650 : STATIC unsigned GC_finalizer_skipped = 0;
651 :
652 : /* Checks and updates the level of finalizers recursion. */
653 : /* Returns NULL if GC_invoke_finalizers() should not be called by the */
654 : /* collector (to minimize the risk of a deep finalizers recursion), */
655 : /* otherwise returns a pointer to GC_finalizer_nested. */
656 : STATIC unsigned char *GC_check_finalizer_nested(void)
657 : {
658 : unsigned nesting_level = *(unsigned char *)&GC_finalizer_nested;
659 : if (nesting_level) {
660 : /* We are inside another GC_invoke_finalizers(). */
661 : /* Skip some implicitly-called GC_invoke_finalizers() */
662 : /* depending on the nesting (recursion) level. */
663 : if (++GC_finalizer_skipped < (1U << nesting_level)) return NULL;
664 : GC_finalizer_skipped = 0;
665 : }
666 : *(char *)&GC_finalizer_nested = (char)(nesting_level + 1);
667 : return (unsigned char *)&GC_finalizer_nested;
668 : }
669 : #endif /* THREADS */
670 :
671 : #define ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr_dl, prev_dl) \
672 : { \
673 : size_t i; \
674 : size_t dl_size = dl_hashtbl->log_size == -1 ? 0 : \
675 : 1 << dl_hashtbl->log_size; \
676 : for (i = 0; i < dl_size; i++) { \
677 : curr_dl = dl_hashtbl -> head[i]; \
678 : prev_dl = NULL; \
679 : while (curr_dl) {
680 :
681 : #define ITERATE_DL_HASHTBL_END(curr_dl, prev_dl) \
682 : prev_dl = curr_dl; \
683 : curr_dl = dl_next(curr_dl); \
684 : } \
685 : } \
686 : }
687 :
688 : #define DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr_dl, prev_dl, next_dl) \
689 : { \
690 : next_dl = dl_next(curr_dl); \
691 : if (NULL == prev_dl) { \
692 : dl_hashtbl -> head[i] = next_dl; \
693 : } else { \
694 : dl_set_next(prev_dl, next_dl); \
695 : } \
696 : GC_clear_mark_bit(curr_dl); \
697 : dl_hashtbl -> entries--; \
698 : curr_dl = next_dl; \
699 : continue; \
700 : }
701 :
702 488 : GC_INLINE void GC_make_disappearing_links_disappear(
703 : struct dl_hashtbl_s* dl_hashtbl)
704 : {
705 : struct disappearing_link *curr, *prev, *next;
706 : ptr_t real_ptr, real_link;
707 :
708 488 : ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr, prev)
709 0 : real_ptr = GC_REVEAL_POINTER(curr -> dl_hidden_obj);
710 0 : real_link = GC_REVEAL_POINTER(curr -> dl_hidden_link);
711 0 : if (!GC_is_marked(real_ptr)) {
712 0 : *(word *)real_link = 0;
713 0 : GC_clear_mark_bit(curr);
714 0 : DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr, prev, next);
715 : }
716 0 : ITERATE_DL_HASHTBL_END(curr, prev)
717 488 : }
718 :
719 488 : GC_INLINE void GC_remove_dangling_disappearing_links(
720 : struct dl_hashtbl_s* dl_hashtbl)
721 : {
722 : struct disappearing_link *curr, *prev, *next;
723 : ptr_t real_link;
724 :
725 488 : ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr, prev)
726 0 : real_link = GC_base(GC_REVEAL_POINTER(curr -> dl_hidden_link));
727 0 : if (NULL != real_link && !GC_is_marked(real_link)) {
728 0 : GC_clear_mark_bit(curr);
729 0 : DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr, prev, next);
730 : }
731 0 : ITERATE_DL_HASHTBL_END(curr, prev)
732 488 : }
733 :
734 : /* Called with held lock (but the world is running). */
735 : /* Cause disappearing links to disappear and unreachable objects to be */
736 : /* enqueued for finalization. */
737 244 : GC_INNER void GC_finalize(void)
738 : {
739 : struct finalizable_object * curr_fo, * prev_fo, * next_fo;
740 : ptr_t real_ptr;
741 : size_t i;
742 244 : size_t fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size;
743 :
744 : # ifndef SMALL_CONFIG
745 : /* Save current GC_[dl/ll]_entries value for stats printing */
746 244 : GC_old_dl_entries = GC_dl_hashtbl.entries;
747 : # ifndef GC_LONG_REFS_NOT_NEEDED
748 244 : GC_old_ll_entries = GC_ll_hashtbl.entries;
749 : # endif
750 : # endif
751 :
752 244 : GC_make_disappearing_links_disappear(&GC_dl_hashtbl);
753 :
754 : /* Mark all objects reachable via chains of 1 or more pointers */
755 : /* from finalizable objects. */
756 : GC_ASSERT(GC_mark_state == MS_NONE);
757 72708 : for (i = 0; i < fo_size; i++) {
758 182331 : for (curr_fo = GC_fo_head[i]; curr_fo != 0;
759 37403 : curr_fo = fo_next(curr_fo)) {
760 : GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object));
761 37403 : real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
762 37403 : if (!GC_is_marked(real_ptr)) {
763 : GC_MARKED_FOR_FINALIZATION(real_ptr);
764 35454 : GC_MARK_FO(real_ptr, curr_fo -> fo_mark_proc);
765 35454 : if (GC_is_marked(real_ptr)) {
766 0 : WARN("Finalization cycle involving %p\n", real_ptr);
767 : }
768 : }
769 : }
770 : }
771 : /* Enqueue for finalization all objects that are still */
772 : /* unreachable. */
773 244 : GC_bytes_finalized = 0;
774 72708 : for (i = 0; i < fo_size; i++) {
775 72464 : curr_fo = GC_fo_head[i];
776 72464 : prev_fo = 0;
777 182331 : while (curr_fo != 0) {
778 37403 : real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
779 37403 : if (!GC_is_marked(real_ptr)) {
780 35454 : if (!GC_java_finalization) {
781 0 : GC_set_mark_bit(real_ptr);
782 : }
783 : /* Delete from hash table */
784 35454 : next_fo = fo_next(curr_fo);
785 35454 : if (prev_fo == 0) {
786 35341 : GC_fo_head[i] = next_fo;
787 : } else {
788 113 : fo_set_next(prev_fo, next_fo);
789 : }
790 35454 : GC_fo_entries--;
791 : /* Add to list of objects awaiting finalization. */
792 35454 : fo_set_next(curr_fo, GC_finalize_now);
793 35454 : GC_finalize_now = curr_fo;
794 : /* unhide object pointer so any future collections will */
795 : /* see it. */
796 35454 : curr_fo -> fo_hidden_base =
797 35454 : (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
798 35454 : GC_bytes_finalized +=
799 : curr_fo -> fo_object_size
800 : + sizeof(struct finalizable_object);
801 : GC_ASSERT(GC_is_marked(GC_base(curr_fo)));
802 35454 : curr_fo = next_fo;
803 : } else {
804 1949 : prev_fo = curr_fo;
805 1949 : curr_fo = fo_next(curr_fo);
806 : }
807 : }
808 : }
809 :
810 244 : if (GC_java_finalization) {
811 : /* make sure we mark everything reachable from objects finalized
812 : using the no_order mark_proc */
813 35962 : for (curr_fo = GC_finalize_now;
814 35474 : curr_fo != NULL; curr_fo = fo_next(curr_fo)) {
815 35474 : real_ptr = (ptr_t)curr_fo -> fo_hidden_base;
816 35474 : if (!GC_is_marked(real_ptr)) {
817 25513 : if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) {
818 17926 : GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc);
819 : }
820 25513 : if (curr_fo -> fo_mark_proc != GC_unreachable_finalize_mark_proc) {
821 17926 : GC_set_mark_bit(real_ptr);
822 : }
823 : }
824 : }
825 :
826 : /* now revive finalize-when-unreachable objects reachable from
827 : other finalizable objects */
828 244 : if (need_unreachable_finalization) {
829 81 : curr_fo = GC_finalize_now;
830 81 : prev_fo = 0;
831 35636 : while (curr_fo != 0) {
832 35474 : next_fo = fo_next(curr_fo);
833 35474 : if (curr_fo -> fo_mark_proc == GC_unreachable_finalize_mark_proc) {
834 17167 : real_ptr = (ptr_t)curr_fo -> fo_hidden_base;
835 17167 : if (!GC_is_marked(real_ptr)) {
836 0 : GC_set_mark_bit(real_ptr);
837 : } else {
838 17167 : if (prev_fo == 0)
839 0 : GC_finalize_now = next_fo;
840 : else
841 17167 : fo_set_next(prev_fo, next_fo);
842 :
843 17167 : curr_fo -> fo_hidden_base =
844 17167 : GC_HIDE_POINTER(curr_fo -> fo_hidden_base);
845 17167 : GC_bytes_finalized -=
846 : curr_fo->fo_object_size + sizeof(struct finalizable_object);
847 :
848 17167 : i = HASH2(real_ptr, log_fo_table_size);
849 17167 : fo_set_next (curr_fo, GC_fo_head[i]);
850 17167 : GC_fo_entries++;
851 17167 : GC_fo_head[i] = curr_fo;
852 17167 : curr_fo = prev_fo;
853 : }
854 : }
855 35474 : prev_fo = curr_fo;
856 35474 : curr_fo = next_fo;
857 : }
858 : }
859 : }
860 :
861 244 : GC_remove_dangling_disappearing_links(&GC_dl_hashtbl);
862 : # ifndef GC_LONG_REFS_NOT_NEEDED
863 244 : GC_make_disappearing_links_disappear(&GC_ll_hashtbl);
864 244 : GC_remove_dangling_disappearing_links(&GC_ll_hashtbl);
865 : # endif
866 :
867 244 : if (GC_fail_count) {
868 : /* Don't prevent running finalizers if there has been an allocation */
869 : /* failure recently. */
870 : # ifdef THREADS
871 3 : GC_reset_finalizer_nested();
872 : # else
873 : GC_finalizer_nested = 0;
874 : # endif
875 : }
876 244 : }
877 :
878 : #ifndef JAVA_FINALIZATION_NOT_NEEDED
879 :
880 : /* Enqueue all remaining finalizers to be run - Assumes lock is held. */
881 0 : STATIC void GC_enqueue_all_finalizers(void)
882 : {
883 : struct finalizable_object * curr_fo, * prev_fo, * next_fo;
884 : ptr_t real_ptr;
885 : register int i;
886 : int fo_size;
887 :
888 0 : fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size;
889 0 : GC_bytes_finalized = 0;
890 0 : for (i = 0; i < fo_size; i++) {
891 0 : curr_fo = GC_fo_head[i];
892 0 : prev_fo = 0;
893 0 : while (curr_fo != 0) {
894 0 : real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
895 0 : GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc);
896 0 : GC_set_mark_bit(real_ptr);
897 :
898 : /* Delete from hash table */
899 0 : next_fo = fo_next(curr_fo);
900 0 : if (prev_fo == 0) {
901 0 : GC_fo_head[i] = next_fo;
902 : } else {
903 0 : fo_set_next(prev_fo, next_fo);
904 : }
905 0 : GC_fo_entries--;
906 :
907 : /* Add to list of objects awaiting finalization. */
908 0 : fo_set_next(curr_fo, GC_finalize_now);
909 0 : GC_finalize_now = curr_fo;
910 :
911 : /* unhide object pointer so any future collections will */
912 : /* see it. */
913 0 : curr_fo -> fo_hidden_base =
914 0 : (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
915 0 : GC_bytes_finalized +=
916 : curr_fo -> fo_object_size + sizeof(struct finalizable_object);
917 0 : curr_fo = next_fo;
918 : }
919 : }
920 0 : }
921 :
922 : /* Invoke all remaining finalizers that haven't yet been run.
923 : * This is needed for strict compliance with the Java standard,
924 : * which can make the runtime guarantee that all finalizers are run.
925 : * Unfortunately, the Java standard implies we have to keep running
926 : * finalizers until there are no more left, a potential infinite loop.
927 : * YUCK.
928 : * Note that this is even more dangerous than the usual Java
929 : * finalizers, in that objects reachable from static variables
930 : * may have been finalized when these finalizers are run.
931 : * Finalizers run at this point must be prepared to deal with a
932 : * mostly broken world.
933 : * This routine is externally callable, so is called without
934 : * the allocation lock.
935 : */
936 0 : GC_API void GC_CALL GC_finalize_all(void)
937 : {
938 : DCL_LOCK_STATE;
939 :
940 0 : LOCK();
941 0 : while (GC_fo_entries > 0) {
942 0 : GC_enqueue_all_finalizers();
943 0 : UNLOCK();
944 0 : GC_invoke_finalizers();
945 : /* Running the finalizers in this thread is arguably not a good */
946 : /* idea when we should be notifying another thread to run them. */
947 : /* But otherwise we don't have a great way to wait for them to */
948 : /* run. */
949 0 : LOCK();
950 : }
951 0 : UNLOCK();
952 0 : }
953 :
954 : #endif /* !JAVA_FINALIZATION_NOT_NEEDED */
955 :
956 : /* Returns true if it is worth calling GC_invoke_finalizers. (Useful if */
957 : /* finalizers can only be called from some kind of "safe state" and */
958 : /* getting into that safe state is expensive.) */
959 0 : GC_API int GC_CALL GC_should_invoke_finalizers(void)
960 : {
961 0 : return GC_finalize_now != 0;
962 : }
963 :
964 : /* Invoke finalizers for all objects that are ready to be finalized. */
965 : /* Should be called without allocation lock. */
966 49 : GC_API int GC_CALL GC_invoke_finalizers(void)
967 : {
968 : struct finalizable_object * curr_fo;
969 49 : int count = 0;
970 49 : word bytes_freed_before = 0; /* initialized to prevent warning. */
971 : DCL_LOCK_STATE;
972 :
973 18385 : while (GC_finalize_now != 0) {
974 : # ifdef THREADS
975 18287 : LOCK();
976 : # endif
977 18287 : if (count == 0) {
978 47 : bytes_freed_before = GC_bytes_freed;
979 : /* Don't do this outside, since we need the lock. */
980 : }
981 18287 : curr_fo = GC_finalize_now;
982 : # ifdef THREADS
983 18287 : if (curr_fo != 0) GC_finalize_now = fo_next(curr_fo);
984 18287 : UNLOCK();
985 18287 : if (curr_fo == 0) break;
986 : # else
987 : GC_finalize_now = fo_next(curr_fo);
988 : # endif
989 18287 : fo_set_next(curr_fo, 0);
990 36574 : (*(curr_fo -> fo_fn))((ptr_t)(curr_fo -> fo_hidden_base),
991 18287 : curr_fo -> fo_client_data);
992 18287 : curr_fo -> fo_client_data = 0;
993 18287 : ++count;
994 : # ifdef UNDEFINED
995 : /* This is probably a bad idea. It throws off accounting if */
996 : /* nearly all objects are finalizable. O.w. it shouldn't */
997 : /* matter. */
998 : GC_free((void *)curr_fo);
999 : # endif
1000 : }
1001 : /* bytes_freed_before is initialized whenever count != 0 */
1002 49 : if (count != 0 && bytes_freed_before != GC_bytes_freed) {
1003 0 : LOCK();
1004 0 : GC_finalizer_bytes_freed += (GC_bytes_freed - bytes_freed_before);
1005 0 : UNLOCK();
1006 : }
1007 49 : return count;
1008 : }
1009 :
1010 : static GC_word last_finalizer_notification = 0;
1011 :
1012 59190 : GC_INNER void GC_notify_or_invoke_finalizers(void)
1013 : {
1014 59190 : GC_finalizer_notifier_proc notifier_fn = 0;
1015 : # if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
1016 : static word last_back_trace_gc_no = 1; /* Skip first one. */
1017 : # endif
1018 : DCL_LOCK_STATE;
1019 :
1020 : # if defined(THREADS) && !defined(KEEP_BACK_PTRS) \
1021 : && !defined(MAKE_BACK_GRAPH)
1022 : /* Quick check (while unlocked) for an empty finalization queue. */
1023 59190 : if (GC_finalize_now == 0) return;
1024 : # endif
1025 782 : LOCK();
1026 :
1027 : /* This is a convenient place to generate backtraces if appropriate, */
1028 : /* since that code is not callable with the allocation lock. */
1029 : # if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
1030 : if (GC_gc_no > last_back_trace_gc_no) {
1031 : # ifdef KEEP_BACK_PTRS
1032 : long i;
1033 : /* Stops when GC_gc_no wraps; that's OK. */
1034 : last_back_trace_gc_no = (word)(-1); /* disable others. */
1035 : for (i = 0; i < GC_backtraces; ++i) {
1036 : /* FIXME: This tolerates concurrent heap mutation, */
1037 : /* which may cause occasional mysterious results. */
1038 : /* We need to release the GC lock, since GC_print_callers */
1039 : /* acquires it. It probably shouldn't. */
1040 : UNLOCK();
1041 : GC_generate_random_backtrace_no_gc();
1042 : LOCK();
1043 : }
1044 : last_back_trace_gc_no = GC_gc_no;
1045 : # endif
1046 : # ifdef MAKE_BACK_GRAPH
1047 : if (GC_print_back_height) {
1048 : UNLOCK();
1049 : GC_print_back_graph_stats();
1050 : LOCK();
1051 : }
1052 : # endif
1053 : }
1054 : # endif
1055 782 : if (GC_finalize_now == 0) {
1056 0 : UNLOCK();
1057 0 : return;
1058 : }
1059 :
1060 782 : if (!GC_finalize_on_demand) {
1061 0 : unsigned char *pnested = GC_check_finalizer_nested();
1062 0 : UNLOCK();
1063 : /* Skip GC_invoke_finalizers() if nested */
1064 0 : if (pnested != NULL) {
1065 0 : (void) GC_invoke_finalizers();
1066 0 : *pnested = 0; /* Reset since no more finalizers. */
1067 : # ifndef THREADS
1068 : GC_ASSERT(GC_finalize_now == 0);
1069 : # endif /* Otherwise GC can run concurrently and add more */
1070 : }
1071 0 : return;
1072 : }
1073 :
1074 : /* These variables require synchronization to avoid data races. */
1075 782 : if (last_finalizer_notification != GC_gc_no) {
1076 48 : last_finalizer_notification = GC_gc_no;
1077 48 : notifier_fn = GC_finalizer_notifier;
1078 : }
1079 782 : UNLOCK();
1080 782 : if (notifier_fn != 0)
1081 48 : (*notifier_fn)(); /* Invoke the notifier */
1082 : }
1083 :
1084 : #ifndef SMALL_CONFIG
1085 : # ifndef GC_LONG_REFS_NOT_NEEDED
1086 : # define IF_LONG_REFS_PRESENT_ELSE(x,y) (x)
1087 : # else
1088 : # define IF_LONG_REFS_PRESENT_ELSE(x,y) (y)
1089 : # endif
1090 :
1091 0 : GC_INNER void GC_print_finalization_stats(void)
1092 : {
1093 : struct finalizable_object *fo;
1094 0 : unsigned long ready = 0;
1095 :
1096 0 : GC_log_printf("%lu finalization entries;"
1097 : " %lu/%lu short/long disappearing links alive\n",
1098 : (unsigned long)GC_fo_entries,
1099 : (unsigned long)GC_dl_hashtbl.entries,
1100 : (unsigned long)IF_LONG_REFS_PRESENT_ELSE(
1101 : GC_ll_hashtbl.entries, 0));
1102 :
1103 0 : for (fo = GC_finalize_now; 0 != fo; fo = fo_next(fo))
1104 0 : ++ready;
1105 0 : GC_log_printf("%lu finalization-ready objects;"
1106 : " %ld/%ld short/long links cleared\n",
1107 : ready,
1108 : (long)GC_old_dl_entries - (long)GC_dl_hashtbl.entries,
1109 0 : (long)IF_LONG_REFS_PRESENT_ELSE(
1110 : GC_old_ll_entries - GC_ll_hashtbl.entries, 0));
1111 0 : }
1112 : #endif /* !SMALL_CONFIG */
1113 :
1114 : #endif /* !GC_NO_FINALIZATION */
|