]>
Commit | Line | Data |
---|---|---|
73ffefd0 TT |
1 | /* |
2 | * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers | |
3 | * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. | |
20bbd3cd | 4 | * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. |
73ffefd0 TT |
5 | |
6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED | |
7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. | |
8 | * | |
9 | * Permission is hereby granted to use or copy this program | |
10 | * for any purpose, provided the above notices are retained on all copies. | |
11 | * Permission to modify the code and to distribute modified code is granted, | |
12 | * provided the above notices are retained, and a notice that the code was | |
13 | * modified is included with the above copyright notice. | |
14 | */ | |
15 | /* Boehm, February 1, 1996 1:19 pm PST */ | |
16 | # define I_HIDE_POINTERS | |
9110a741 | 17 | # include "private/gc_pmark.h" |
73ffefd0 | 18 | |
20bbd3cd TT |
19 | # ifdef FINALIZE_ON_DEMAND |
20 | int GC_finalize_on_demand = 1; | |
21 | # else | |
22 | int GC_finalize_on_demand = 0; | |
23 | # endif | |
24 | ||
25 | # ifdef JAVA_FINALIZATION | |
26 | int GC_java_finalization = 1; | |
27 | # else | |
28 | int GC_java_finalization = 0; | |
29 | # endif | |
30 | ||
73ffefd0 TT |
31 | /* Type of mark procedure used for marking from finalizable object. */ |
32 | /* This procedure normally does not mark the object, only its */ | |
33 | /* descendents. */ | |
34 | typedef void finalization_mark_proc(/* ptr_t finalizable_obj_ptr */); | |
35 | ||
36 | # define HASH3(addr,size,log_size) \ | |
37 | ((((word)(addr) >> 3) ^ ((word)(addr) >> (3+(log_size)))) \ | |
38 | & ((size) - 1)) | |
39 | #define HASH2(addr,log_size) HASH3(addr, 1 << log_size, log_size) | |
40 | ||
41 | struct hash_chain_entry { | |
42 | word hidden_key; | |
43 | struct hash_chain_entry * next; | |
44 | }; | |
45 | ||
46 | unsigned GC_finalization_failures = 0; | |
47 | /* Number of finalization requests that failed for lack of memory. */ | |
48 | ||
49 | static struct disappearing_link { | |
50 | struct hash_chain_entry prolog; | |
51 | # define dl_hidden_link prolog.hidden_key | |
52 | /* Field to be cleared. */ | |
53 | # define dl_next(x) (struct disappearing_link *)((x) -> prolog.next) | |
54 | # define dl_set_next(x,y) (x) -> prolog.next = (struct hash_chain_entry *)(y) | |
55 | ||
56 | word dl_hidden_obj; /* Pointer to object base */ | |
57 | } **dl_head = 0; | |
58 | ||
59 | static signed_word log_dl_table_size = -1; | |
60 | /* Binary log of */ | |
61 | /* current size of array pointed to by dl_head. */ | |
62 | /* -1 ==> size is 0. */ | |
63 | ||
64 | word GC_dl_entries = 0; /* Number of entries currently in disappearing */ | |
65 | /* link table. */ | |
66 | ||
67 | static struct finalizable_object { | |
68 | struct hash_chain_entry prolog; | |
69 | # define fo_hidden_base prolog.hidden_key | |
70 | /* Pointer to object base. */ | |
71 | /* No longer hidden once object */ | |
72 | /* is on finalize_now queue. */ | |
73 | # define fo_next(x) (struct finalizable_object *)((x) -> prolog.next) | |
74 | # define fo_set_next(x,y) (x) -> prolog.next = (struct hash_chain_entry *)(y) | |
75 | GC_finalization_proc fo_fn; /* Finalizer. */ | |
76 | ptr_t fo_client_data; | |
77 | word fo_object_size; /* In bytes. */ | |
78 | finalization_mark_proc * fo_mark_proc; /* Mark-through procedure */ | |
79 | } **fo_head = 0; | |
80 | ||
81 | struct finalizable_object * GC_finalize_now = 0; | |
82 | /* LIst of objects that should be finalized now. */ | |
83 | ||
84 | static signed_word log_fo_table_size = -1; | |
85 | ||
86 | word GC_fo_entries = 0; | |
87 | ||
9110a741 | 88 | void GC_push_finalizer_structures GC_PROTO((void)) |
73ffefd0 TT |
89 | { |
90 | GC_push_all((ptr_t)(&dl_head), (ptr_t)(&dl_head) + sizeof(word)); | |
91 | GC_push_all((ptr_t)(&fo_head), (ptr_t)(&fo_head) + sizeof(word)); | |
9110a741 BM |
92 | GC_push_all((ptr_t)(&GC_finalize_now), |
93 | (ptr_t)(&GC_finalize_now) + sizeof(word)); | |
73ffefd0 | 94 | } |
73ffefd0 TT |
95 | |
96 | /* Double the size of a hash table. *size_ptr is the log of its current */ | |
97 | /* size. May be a noop. */ | |
98 | /* *table is a pointer to an array of hash headers. If we succeed, we */ | |
99 | /* update both *table and *log_size_ptr. */ | |
100 | /* Lock is held. Signals are disabled. */ | |
101 | void GC_grow_table(table, log_size_ptr) | |
102 | struct hash_chain_entry ***table; | |
103 | signed_word * log_size_ptr; | |
104 | { | |
105 | register word i; | |
106 | register struct hash_chain_entry *p; | |
107 | int log_old_size = *log_size_ptr; | |
108 | register int log_new_size = log_old_size + 1; | |
109 | word old_size = ((log_old_size == -1)? 0: (1 << log_old_size)); | |
110 | register word new_size = 1 << log_new_size; | |
111 | struct hash_chain_entry **new_table = (struct hash_chain_entry **) | |
9110a741 | 112 | GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( |
73ffefd0 TT |
113 | (size_t)new_size * sizeof(struct hash_chain_entry *), NORMAL); |
114 | ||
115 | if (new_table == 0) { | |
116 | if (table == 0) { | |
117 | ABORT("Insufficient space for initial table allocation"); | |
118 | } else { | |
119 | return; | |
120 | } | |
121 | } | |
122 | for (i = 0; i < old_size; i++) { | |
123 | p = (*table)[i]; | |
124 | while (p != 0) { | |
125 | register ptr_t real_key = (ptr_t)REVEAL_POINTER(p -> hidden_key); | |
126 | register struct hash_chain_entry *next = p -> next; | |
127 | register int new_hash = HASH3(real_key, new_size, log_new_size); | |
128 | ||
129 | p -> next = new_table[new_hash]; | |
130 | new_table[new_hash] = p; | |
131 | p = next; | |
132 | } | |
133 | } | |
134 | *log_size_ptr = log_new_size; | |
135 | *table = new_table; | |
136 | } | |
137 | ||
138 | # if defined(__STDC__) || defined(__cplusplus) | |
139 | int GC_register_disappearing_link(GC_PTR * link) | |
140 | # else | |
141 | int GC_register_disappearing_link(link) | |
142 | GC_PTR * link; | |
143 | # endif | |
144 | { | |
145 | ptr_t base; | |
146 | ||
147 | base = (ptr_t)GC_base((GC_PTR)link); | |
148 | if (base == 0) | |
149 | ABORT("Bad arg to GC_register_disappearing_link"); | |
150 | return(GC_general_register_disappearing_link(link, base)); | |
151 | } | |
152 | ||
153 | # if defined(__STDC__) || defined(__cplusplus) | |
154 | int GC_general_register_disappearing_link(GC_PTR * link, | |
155 | GC_PTR obj) | |
156 | # else | |
157 | int GC_general_register_disappearing_link(link, obj) | |
158 | GC_PTR * link; | |
159 | GC_PTR obj; | |
160 | # endif | |
161 | ||
162 | { | |
163 | struct disappearing_link *curr_dl; | |
164 | int index; | |
165 | struct disappearing_link * new_dl; | |
166 | DCL_LOCK_STATE; | |
167 | ||
168 | if ((word)link & (ALIGNMENT-1)) | |
169 | ABORT("Bad arg to GC_general_register_disappearing_link"); | |
170 | # ifdef THREADS | |
171 | DISABLE_SIGNALS(); | |
172 | LOCK(); | |
173 | # endif | |
174 | if (log_dl_table_size == -1 | |
175 | || GC_dl_entries > ((word)1 << log_dl_table_size)) { | |
176 | # ifndef THREADS | |
177 | DISABLE_SIGNALS(); | |
178 | # endif | |
179 | GC_grow_table((struct hash_chain_entry ***)(&dl_head), | |
180 | &log_dl_table_size); | |
9110a741 BM |
181 | # ifdef CONDPRINT |
182 | if (GC_print_stats) { | |
73ffefd0 TT |
183 | GC_printf1("Grew dl table to %lu entries\n", |
184 | (unsigned long)(1 << log_dl_table_size)); | |
9110a741 | 185 | } |
73ffefd0 TT |
186 | # endif |
187 | # ifndef THREADS | |
188 | ENABLE_SIGNALS(); | |
189 | # endif | |
190 | } | |
191 | index = HASH2(link, log_dl_table_size); | |
192 | curr_dl = dl_head[index]; | |
193 | for (curr_dl = dl_head[index]; curr_dl != 0; curr_dl = dl_next(curr_dl)) { | |
194 | if (curr_dl -> dl_hidden_link == HIDE_POINTER(link)) { | |
195 | curr_dl -> dl_hidden_obj = HIDE_POINTER(obj); | |
196 | # ifdef THREADS | |
197 | UNLOCK(); | |
198 | ENABLE_SIGNALS(); | |
199 | # endif | |
200 | return(1); | |
201 | } | |
202 | } | |
9110a741 BM |
203 | new_dl = (struct disappearing_link *) |
204 | GC_INTERNAL_MALLOC(sizeof(struct disappearing_link),NORMAL); | |
5a2586cf TT |
205 | if (0 == new_dl) { |
206 | # ifdef THREADS | |
207 | UNLOCK(); | |
208 | ENABLE_SIGNALS(); | |
209 | # endif | |
4c7726b1 | 210 | new_dl = GC_oom_fn(sizeof(struct disappearing_link)); |
5a2586cf TT |
211 | if (0 == new_dl) { |
212 | GC_finalization_failures++; | |
213 | return(0); | |
214 | } | |
215 | /* It's not likely we'll make it here, but ... */ | |
216 | # ifdef THREADS | |
217 | DISABLE_SIGNALS(); | |
218 | LOCK(); | |
219 | # endif | |
73ffefd0 | 220 | } |
5a2586cf TT |
221 | new_dl -> dl_hidden_obj = HIDE_POINTER(obj); |
222 | new_dl -> dl_hidden_link = HIDE_POINTER(link); | |
223 | dl_set_next(new_dl, dl_head[index]); | |
224 | dl_head[index] = new_dl; | |
225 | GC_dl_entries++; | |
73ffefd0 TT |
226 | # ifdef THREADS |
227 | UNLOCK(); | |
228 | ENABLE_SIGNALS(); | |
229 | # endif | |
230 | return(0); | |
231 | } | |
232 | ||
233 | # if defined(__STDC__) || defined(__cplusplus) | |
234 | int GC_unregister_disappearing_link(GC_PTR * link) | |
235 | # else | |
236 | int GC_unregister_disappearing_link(link) | |
237 | GC_PTR * link; | |
238 | # endif | |
239 | { | |
240 | struct disappearing_link *curr_dl, *prev_dl; | |
241 | int index; | |
242 | DCL_LOCK_STATE; | |
243 | ||
244 | DISABLE_SIGNALS(); | |
245 | LOCK(); | |
246 | index = HASH2(link, log_dl_table_size); | |
247 | if (((unsigned long)link & (ALIGNMENT-1))) goto out; | |
248 | prev_dl = 0; curr_dl = dl_head[index]; | |
249 | while (curr_dl != 0) { | |
250 | if (curr_dl -> dl_hidden_link == HIDE_POINTER(link)) { | |
251 | if (prev_dl == 0) { | |
252 | dl_head[index] = dl_next(curr_dl); | |
253 | } else { | |
254 | dl_set_next(prev_dl, dl_next(curr_dl)); | |
255 | } | |
256 | GC_dl_entries--; | |
257 | UNLOCK(); | |
258 | ENABLE_SIGNALS(); | |
9110a741 | 259 | # ifdef DBG_HDRS_ALL |
5a2586cf | 260 | dl_set_next(curr_dl, 0); |
9110a741 BM |
261 | # else |
262 | GC_free((GC_PTR)curr_dl); | |
263 | # endif | |
73ffefd0 TT |
264 | return(1); |
265 | } | |
266 | prev_dl = curr_dl; | |
267 | curr_dl = dl_next(curr_dl); | |
268 | } | |
269 | out: | |
270 | UNLOCK(); | |
271 | ENABLE_SIGNALS(); | |
272 | return(0); | |
273 | } | |
274 | ||
275 | /* Possible finalization_marker procedures. Note that mark stack */ | |
276 | /* overflow is handled by the caller, and is not a disaster. */ | |
20bbd3cd | 277 | GC_API void GC_normal_finalize_mark_proc(p) |
73ffefd0 TT |
278 | ptr_t p; |
279 | { | |
280 | hdr * hhdr = HDR(p); | |
281 | ||
282 | PUSH_OBJ((word *)p, hhdr, GC_mark_stack_top, | |
283 | &(GC_mark_stack[GC_mark_stack_size])); | |
284 | } | |
285 | ||
286 | /* This only pays very partial attention to the mark descriptor. */ | |
287 | /* It does the right thing for normal and atomic objects, and treats */ | |
288 | /* most others as normal. */ | |
20bbd3cd | 289 | GC_API void GC_ignore_self_finalize_mark_proc(p) |
73ffefd0 TT |
290 | ptr_t p; |
291 | { | |
292 | hdr * hhdr = HDR(p); | |
293 | word descr = hhdr -> hb_descr; | |
294 | ptr_t q, r; | |
295 | ptr_t scan_limit; | |
296 | ptr_t target_limit = p + WORDS_TO_BYTES(hhdr -> hb_sz) - 1; | |
297 | ||
9110a741 | 298 | if ((descr & GC_DS_TAGS) == GC_DS_LENGTH) { |
73ffefd0 TT |
299 | scan_limit = p + descr - sizeof(word); |
300 | } else { | |
301 | scan_limit = target_limit + 1 - sizeof(word); | |
302 | } | |
303 | for (q = p; q <= scan_limit; q += ALIGNMENT) { | |
304 | r = *(ptr_t *)q; | |
305 | if (r < p || r > target_limit) { | |
20bbd3cd | 306 | GC_PUSH_ONE_HEAP((word)r, q); |
73ffefd0 TT |
307 | } |
308 | } | |
309 | } | |
310 | ||
311 | /*ARGSUSED*/ | |
20bbd3cd | 312 | GC_API void GC_null_finalize_mark_proc(p) |
73ffefd0 TT |
313 | ptr_t p; |
314 | { | |
315 | } | |
316 | ||
317 | ||
318 | ||
319 | /* Register a finalization function. See gc.h for details. */ | |
320 | /* in the nonthreads case, we try to avoid disabling signals, */ | |
321 | /* since it can be expensive. Threads packages typically */ | |
322 | /* make it cheaper. */ | |
20bbd3cd TT |
323 | /* The last parameter is a procedure that determines */ |
324 | /* marking for finalization ordering. Any objects marked */ | |
325 | /* by that procedure will be guaranteed to not have been */ | |
326 | /* finalized when this finalizer is invoked. */ | |
327 | GC_API void GC_register_finalizer_inner(obj, fn, cd, ofn, ocd, mp) | |
73ffefd0 TT |
328 | GC_PTR obj; |
329 | GC_finalization_proc fn; | |
330 | GC_PTR cd; | |
331 | GC_finalization_proc * ofn; | |
332 | GC_PTR * ocd; | |
333 | finalization_mark_proc * mp; | |
334 | { | |
335 | ptr_t base; | |
336 | struct finalizable_object * curr_fo, * prev_fo; | |
337 | int index; | |
338 | struct finalizable_object *new_fo; | |
41029b88 | 339 | hdr *hhdr; |
73ffefd0 TT |
340 | DCL_LOCK_STATE; |
341 | ||
342 | # ifdef THREADS | |
343 | DISABLE_SIGNALS(); | |
344 | LOCK(); | |
345 | # endif | |
346 | if (log_fo_table_size == -1 | |
347 | || GC_fo_entries > ((word)1 << log_fo_table_size)) { | |
348 | # ifndef THREADS | |
349 | DISABLE_SIGNALS(); | |
350 | # endif | |
351 | GC_grow_table((struct hash_chain_entry ***)(&fo_head), | |
352 | &log_fo_table_size); | |
9110a741 BM |
353 | # ifdef CONDPRINT |
354 | if (GC_print_stats) { | |
73ffefd0 TT |
355 | GC_printf1("Grew fo table to %lu entries\n", |
356 | (unsigned long)(1 << log_fo_table_size)); | |
9110a741 | 357 | } |
73ffefd0 TT |
358 | # endif |
359 | # ifndef THREADS | |
360 | ENABLE_SIGNALS(); | |
361 | # endif | |
362 | } | |
363 | /* in the THREADS case signals are disabled and we hold allocation */ | |
364 | /* lock; otherwise neither is true. Proceed carefully. */ | |
365 | base = (ptr_t)obj; | |
366 | index = HASH2(base, log_fo_table_size); | |
367 | prev_fo = 0; curr_fo = fo_head[index]; | |
368 | while (curr_fo != 0) { | |
369 | if (curr_fo -> fo_hidden_base == HIDE_POINTER(base)) { | |
370 | /* Interruption by a signal in the middle of this */ | |
371 | /* should be safe. The client may see only *ocd */ | |
372 | /* updated, but we'll declare that to be his */ | |
373 | /* problem. */ | |
374 | if (ocd) *ocd = (GC_PTR) curr_fo -> fo_client_data; | |
375 | if (ofn) *ofn = curr_fo -> fo_fn; | |
376 | /* Delete the structure for base. */ | |
377 | if (prev_fo == 0) { | |
378 | fo_head[index] = fo_next(curr_fo); | |
379 | } else { | |
380 | fo_set_next(prev_fo, fo_next(curr_fo)); | |
381 | } | |
382 | if (fn == 0) { | |
383 | GC_fo_entries--; | |
384 | /* May not happen if we get a signal. But a high */ | |
385 | /* estimate will only make the table larger than */ | |
386 | /* necessary. */ | |
9110a741 | 387 | # if !defined(THREADS) && !defined(DBG_HDRS_ALL) |
73ffefd0 TT |
388 | GC_free((GC_PTR)curr_fo); |
389 | # endif | |
390 | } else { | |
391 | curr_fo -> fo_fn = fn; | |
392 | curr_fo -> fo_client_data = (ptr_t)cd; | |
393 | curr_fo -> fo_mark_proc = mp; | |
394 | /* Reinsert it. We deleted it first to maintain */ | |
395 | /* consistency in the event of a signal. */ | |
396 | if (prev_fo == 0) { | |
397 | fo_head[index] = curr_fo; | |
398 | } else { | |
399 | fo_set_next(prev_fo, curr_fo); | |
400 | } | |
401 | } | |
402 | # ifdef THREADS | |
403 | UNLOCK(); | |
404 | ENABLE_SIGNALS(); | |
405 | # endif | |
406 | return; | |
407 | } | |
408 | prev_fo = curr_fo; | |
409 | curr_fo = fo_next(curr_fo); | |
410 | } | |
411 | if (ofn) *ofn = 0; | |
412 | if (ocd) *ocd = 0; | |
413 | if (fn == 0) { | |
414 | # ifdef THREADS | |
415 | UNLOCK(); | |
416 | ENABLE_SIGNALS(); | |
417 | # endif | |
418 | return; | |
419 | } | |
41029b88 HB |
420 | GET_HDR(base, hhdr); |
421 | if (0 == hhdr) { | |
422 | /* We won't collect it, hence finalizer wouldn't be run. */ | |
41029b88 HB |
423 | # ifdef THREADS |
424 | UNLOCK(); | |
425 | ENABLE_SIGNALS(); | |
426 | # endif | |
427 | return; | |
428 | } | |
9110a741 BM |
429 | new_fo = (struct finalizable_object *) |
430 | GC_INTERNAL_MALLOC(sizeof(struct finalizable_object),NORMAL); | |
5a2586cf TT |
431 | if (0 == new_fo) { |
432 | # ifdef THREADS | |
433 | UNLOCK(); | |
434 | ENABLE_SIGNALS(); | |
435 | # endif | |
4c7726b1 | 436 | new_fo = GC_oom_fn(sizeof(struct finalizable_object)); |
5a2586cf TT |
437 | if (0 == new_fo) { |
438 | GC_finalization_failures++; | |
439 | return; | |
440 | } | |
441 | /* It's not likely we'll make it here, but ... */ | |
442 | # ifdef THREADS | |
443 | DISABLE_SIGNALS(); | |
444 | LOCK(); | |
445 | # endif | |
73ffefd0 | 446 | } |
5a2586cf TT |
447 | new_fo -> fo_hidden_base = (word)HIDE_POINTER(base); |
448 | new_fo -> fo_fn = fn; | |
449 | new_fo -> fo_client_data = (ptr_t)cd; | |
450 | new_fo -> fo_object_size = hhdr -> hb_sz; | |
451 | new_fo -> fo_mark_proc = mp; | |
452 | fo_set_next(new_fo, fo_head[index]); | |
453 | GC_fo_entries++; | |
454 | fo_head[index] = new_fo; | |
73ffefd0 TT |
455 | # ifdef THREADS |
456 | UNLOCK(); | |
457 | ENABLE_SIGNALS(); | |
458 | # endif | |
459 | } | |
460 | ||
461 | # if defined(__STDC__) | |
462 | void GC_register_finalizer(void * obj, | |
463 | GC_finalization_proc fn, void * cd, | |
464 | GC_finalization_proc *ofn, void ** ocd) | |
465 | # else | |
466 | void GC_register_finalizer(obj, fn, cd, ofn, ocd) | |
467 | GC_PTR obj; | |
468 | GC_finalization_proc fn; | |
469 | GC_PTR cd; | |
470 | GC_finalization_proc * ofn; | |
471 | GC_PTR * ocd; | |
472 | # endif | |
473 | { | |
474 | GC_register_finalizer_inner(obj, fn, cd, ofn, | |
475 | ocd, GC_normal_finalize_mark_proc); | |
476 | } | |
477 | ||
478 | # if defined(__STDC__) | |
479 | void GC_register_finalizer_ignore_self(void * obj, | |
480 | GC_finalization_proc fn, void * cd, | |
481 | GC_finalization_proc *ofn, void ** ocd) | |
482 | # else | |
483 | void GC_register_finalizer_ignore_self(obj, fn, cd, ofn, ocd) | |
484 | GC_PTR obj; | |
485 | GC_finalization_proc fn; | |
486 | GC_PTR cd; | |
487 | GC_finalization_proc * ofn; | |
488 | GC_PTR * ocd; | |
489 | # endif | |
490 | { | |
491 | GC_register_finalizer_inner(obj, fn, cd, ofn, | |
492 | ocd, GC_ignore_self_finalize_mark_proc); | |
493 | } | |
494 | ||
495 | # if defined(__STDC__) | |
496 | void GC_register_finalizer_no_order(void * obj, | |
497 | GC_finalization_proc fn, void * cd, | |
498 | GC_finalization_proc *ofn, void ** ocd) | |
499 | # else | |
500 | void GC_register_finalizer_no_order(obj, fn, cd, ofn, ocd) | |
501 | GC_PTR obj; | |
502 | GC_finalization_proc fn; | |
503 | GC_PTR cd; | |
504 | GC_finalization_proc * ofn; | |
505 | GC_PTR * ocd; | |
506 | # endif | |
507 | { | |
508 | GC_register_finalizer_inner(obj, fn, cd, ofn, | |
509 | ocd, GC_null_finalize_mark_proc); | |
510 | } | |
511 | ||
9110a741 BM |
512 | #ifndef NO_DEBUGGING |
513 | void GC_dump_finalization() | |
514 | { | |
515 | struct disappearing_link * curr_dl; | |
516 | struct finalizable_object * curr_fo; | |
517 | ptr_t real_ptr, real_link; | |
518 | int dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size); | |
519 | int fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size); | |
520 | int i; | |
521 | ||
522 | GC_printf0("Disappearing links:\n"); | |
523 | for (i = 0; i < dl_size; i++) { | |
524 | for (curr_dl = dl_head[i]; curr_dl != 0; curr_dl = dl_next(curr_dl)) { | |
525 | real_ptr = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_obj); | |
526 | real_link = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_link); | |
527 | GC_printf2("Object: 0x%lx, Link:0x%lx\n", real_ptr, real_link); | |
528 | } | |
529 | } | |
530 | GC_printf0("Finalizers:\n"); | |
531 | for (i = 0; i < fo_size; i++) { | |
532 | for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = fo_next(curr_fo)) { | |
533 | real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base); | |
534 | GC_printf1("Finalizable object: 0x%lx\n", real_ptr); | |
535 | } | |
536 | } | |
537 | } | |
538 | #endif | |
539 | ||
73ffefd0 TT |
540 | /* Called with world stopped. Cause disappearing links to disappear, */ |
541 | /* and invoke finalizers. */ | |
542 | void GC_finalize() | |
543 | { | |
544 | struct disappearing_link * curr_dl, * prev_dl, * next_dl; | |
545 | struct finalizable_object * curr_fo, * prev_fo, * next_fo; | |
546 | ptr_t real_ptr, real_link; | |
547 | register int i; | |
548 | int dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size); | |
549 | int fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size); | |
550 | ||
551 | /* Make disappearing links disappear */ | |
552 | for (i = 0; i < dl_size; i++) { | |
553 | curr_dl = dl_head[i]; | |
554 | prev_dl = 0; | |
555 | while (curr_dl != 0) { | |
556 | real_ptr = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_obj); | |
557 | real_link = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_link); | |
558 | if (!GC_is_marked(real_ptr)) { | |
559 | *(word *)real_link = 0; | |
560 | next_dl = dl_next(curr_dl); | |
561 | if (prev_dl == 0) { | |
562 | dl_head[i] = next_dl; | |
563 | } else { | |
564 | dl_set_next(prev_dl, next_dl); | |
565 | } | |
566 | GC_clear_mark_bit((ptr_t)curr_dl); | |
567 | GC_dl_entries--; | |
568 | curr_dl = next_dl; | |
569 | } else { | |
570 | prev_dl = curr_dl; | |
571 | curr_dl = dl_next(curr_dl); | |
572 | } | |
573 | } | |
574 | } | |
575 | /* Mark all objects reachable via chains of 1 or more pointers */ | |
576 | /* from finalizable objects. */ | |
9110a741 | 577 | GC_ASSERT(GC_mark_state == MS_NONE); |
73ffefd0 TT |
578 | for (i = 0; i < fo_size; i++) { |
579 | for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = fo_next(curr_fo)) { | |
580 | real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base); | |
581 | if (!GC_is_marked(real_ptr)) { | |
20bbd3cd | 582 | GC_MARKED_FOR_FINALIZATION(real_ptr); |
73ffefd0 TT |
583 | GC_MARK_FO(real_ptr, curr_fo -> fo_mark_proc); |
584 | if (GC_is_marked(real_ptr)) { | |
585 | WARN("Finalization cycle involving %lx\n", real_ptr); | |
586 | } | |
587 | } | |
588 | } | |
589 | } | |
590 | /* Enqueue for finalization all objects that are still */ | |
591 | /* unreachable. */ | |
592 | GC_words_finalized = 0; | |
593 | for (i = 0; i < fo_size; i++) { | |
594 | curr_fo = fo_head[i]; | |
595 | prev_fo = 0; | |
596 | while (curr_fo != 0) { | |
597 | real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base); | |
598 | if (!GC_is_marked(real_ptr)) { | |
20bbd3cd TT |
599 | if (!GC_java_finalization) { |
600 | GC_set_mark_bit(real_ptr); | |
601 | } | |
73ffefd0 TT |
602 | /* Delete from hash table */ |
603 | next_fo = fo_next(curr_fo); | |
604 | if (prev_fo == 0) { | |
605 | fo_head[i] = next_fo; | |
606 | } else { | |
607 | fo_set_next(prev_fo, next_fo); | |
608 | } | |
609 | GC_fo_entries--; | |
610 | /* Add to list of objects awaiting finalization. */ | |
611 | fo_set_next(curr_fo, GC_finalize_now); | |
612 | GC_finalize_now = curr_fo; | |
613 | /* unhide object pointer so any future collections will */ | |
614 | /* see it. */ | |
615 | curr_fo -> fo_hidden_base = | |
616 | (word) REVEAL_POINTER(curr_fo -> fo_hidden_base); | |
617 | GC_words_finalized += | |
618 | ALIGNED_WORDS(curr_fo -> fo_object_size) | |
619 | + ALIGNED_WORDS(sizeof(struct finalizable_object)); | |
5a2586cf | 620 | GC_ASSERT(GC_is_marked(GC_base((ptr_t)curr_fo))); |
73ffefd0 TT |
621 | curr_fo = next_fo; |
622 | } else { | |
623 | prev_fo = curr_fo; | |
624 | curr_fo = fo_next(curr_fo); | |
625 | } | |
626 | } | |
627 | } | |
628 | ||
20bbd3cd TT |
629 | if (GC_java_finalization) { |
630 | /* make sure we mark everything reachable from objects finalized | |
631 | using the no_order mark_proc */ | |
632 | for (curr_fo = GC_finalize_now; | |
633 | curr_fo != NULL; curr_fo = fo_next(curr_fo)) { | |
634 | real_ptr = (ptr_t)curr_fo -> fo_hidden_base; | |
635 | if (!GC_is_marked(real_ptr)) { | |
636 | if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) { | |
637 | GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc); | |
638 | } | |
639 | GC_set_mark_bit(real_ptr); | |
640 | } | |
641 | } | |
642 | } | |
73ffefd0 TT |
643 | |
644 | /* Remove dangling disappearing links. */ | |
645 | for (i = 0; i < dl_size; i++) { | |
646 | curr_dl = dl_head[i]; | |
647 | prev_dl = 0; | |
648 | while (curr_dl != 0) { | |
649 | real_link = GC_base((ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_link)); | |
650 | if (real_link != 0 && !GC_is_marked(real_link)) { | |
651 | next_dl = dl_next(curr_dl); | |
652 | if (prev_dl == 0) { | |
653 | dl_head[i] = next_dl; | |
654 | } else { | |
655 | dl_set_next(prev_dl, next_dl); | |
656 | } | |
657 | GC_clear_mark_bit((ptr_t)curr_dl); | |
658 | GC_dl_entries--; | |
659 | curr_dl = next_dl; | |
660 | } else { | |
661 | prev_dl = curr_dl; | |
662 | curr_dl = dl_next(curr_dl); | |
663 | } | |
664 | } | |
665 | } | |
666 | } | |
667 | ||
20bbd3cd | 668 | #ifndef JAVA_FINALIZATION_NOT_NEEDED |
73ffefd0 TT |
669 | |
670 | /* Enqueue all remaining finalizers to be run - Assumes lock is | |
671 | * held, and signals are disabled */ | |
672 | void GC_enqueue_all_finalizers() | |
673 | { | |
674 | struct finalizable_object * curr_fo, * prev_fo, * next_fo; | |
9444af72 | 675 | ptr_t real_ptr; |
73ffefd0 TT |
676 | register int i; |
677 | int fo_size; | |
678 | ||
679 | fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size); | |
680 | GC_words_finalized = 0; | |
681 | for (i = 0; i < fo_size; i++) { | |
682 | curr_fo = fo_head[i]; | |
683 | prev_fo = 0; | |
684 | while (curr_fo != 0) { | |
685 | real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base); | |
686 | GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc); | |
687 | GC_set_mark_bit(real_ptr); | |
688 | ||
689 | /* Delete from hash table */ | |
690 | next_fo = fo_next(curr_fo); | |
691 | if (prev_fo == 0) { | |
692 | fo_head[i] = next_fo; | |
693 | } else { | |
694 | fo_set_next(prev_fo, next_fo); | |
695 | } | |
696 | GC_fo_entries--; | |
697 | ||
698 | /* Add to list of objects awaiting finalization. */ | |
699 | fo_set_next(curr_fo, GC_finalize_now); | |
700 | GC_finalize_now = curr_fo; | |
701 | ||
702 | /* unhide object pointer so any future collections will */ | |
703 | /* see it. */ | |
704 | curr_fo -> fo_hidden_base = | |
705 | (word) REVEAL_POINTER(curr_fo -> fo_hidden_base); | |
706 | ||
707 | GC_words_finalized += | |
708 | ALIGNED_WORDS(curr_fo -> fo_object_size) | |
709 | + ALIGNED_WORDS(sizeof(struct finalizable_object)); | |
710 | curr_fo = next_fo; | |
711 | } | |
712 | } | |
713 | ||
714 | return; | |
715 | } | |
716 | ||
717 | /* Invoke all remaining finalizers that haven't yet been run. | |
718 | * This is needed for strict compliance with the Java standard, | |
719 | * which can make the runtime guarantee that all finalizers are run. | |
720 | * Unfortunately, the Java standard implies we have to keep running | |
721 | * finalizers until there are no more left, a potential infinite loop. | |
20bbd3cd TT |
722 | * YUCK. |
723 | * Note that this is even more dangerous than the usual Java | |
724 | * finalizers, in that objects reachable from static variables | |
725 | * may have been finalized when these finalizers are run. | |
726 | * Finalizers run at this point must be prepared to deal with a | |
727 | * mostly broken world. | |
728 | * This routine is externally callable, so is called without | |
729 | * the allocation lock. | |
73ffefd0 | 730 | */ |
20bbd3cd | 731 | GC_API void GC_finalize_all() |
73ffefd0 TT |
732 | { |
733 | DCL_LOCK_STATE; | |
734 | ||
735 | DISABLE_SIGNALS(); | |
736 | LOCK(); | |
737 | while (GC_fo_entries > 0) { | |
738 | GC_enqueue_all_finalizers(); | |
739 | UNLOCK(); | |
740 | ENABLE_SIGNALS(); | |
741 | GC_INVOKE_FINALIZERS(); | |
742 | DISABLE_SIGNALS(); | |
743 | LOCK(); | |
744 | } | |
745 | UNLOCK(); | |
746 | ENABLE_SIGNALS(); | |
747 | } | |
748 | #endif | |
749 | ||
93002327 BM |
750 | /* Returns true if it is worth calling GC_invoke_finalizers. (Useful if */ |
751 | /* finalizers can only be called from some kind of `safe state' and */ | |
752 | /* getting into that safe state is expensive.) */ | |
753 | int GC_should_invoke_finalizers GC_PROTO((void)) | |
754 | { | |
755 | return GC_finalize_now != 0; | |
756 | } | |
757 | ||
73ffefd0 TT |
758 | /* Invoke finalizers for all objects that are ready to be finalized. */ |
759 | /* Should be called without allocation lock. */ | |
760 | int GC_invoke_finalizers() | |
761 | { | |
762 | register struct finalizable_object * curr_fo; | |
763 | register int count = 0; | |
764 | DCL_LOCK_STATE; | |
765 | ||
766 | while (GC_finalize_now != 0) { | |
767 | # ifdef THREADS | |
768 | DISABLE_SIGNALS(); | |
769 | LOCK(); | |
770 | # endif | |
771 | curr_fo = GC_finalize_now; | |
772 | # ifdef THREADS | |
773 | if (curr_fo != 0) GC_finalize_now = fo_next(curr_fo); | |
774 | UNLOCK(); | |
775 | ENABLE_SIGNALS(); | |
776 | if (curr_fo == 0) break; | |
777 | # else | |
778 | GC_finalize_now = fo_next(curr_fo); | |
779 | # endif | |
780 | fo_set_next(curr_fo, 0); | |
781 | (*(curr_fo -> fo_fn))((ptr_t)(curr_fo -> fo_hidden_base), | |
782 | curr_fo -> fo_client_data); | |
783 | curr_fo -> fo_client_data = 0; | |
784 | ++count; | |
785 | # ifdef UNDEFINED | |
786 | /* This is probably a bad idea. It throws off accounting if */ | |
787 | /* nearly all objects are finalizable. O.w. it shouldn't */ | |
788 | /* matter. */ | |
789 | GC_free((GC_PTR)curr_fo); | |
790 | # endif | |
791 | } | |
792 | return count; | |
793 | } | |
794 | ||
9110a741 BM |
795 | void (* GC_finalizer_notifier)() = (void (*) GC_PROTO((void)))0; |
796 | ||
797 | static GC_word last_finalizer_notification = 0; | |
798 | ||
799 | void GC_notify_or_invoke_finalizers GC_PROTO((void)) | |
800 | { | |
801 | if (GC_finalize_now == 0) return; | |
802 | if (!GC_finalize_on_demand) { | |
803 | (void) GC_invoke_finalizers(); | |
804 | GC_ASSERT(GC_finalize_now == 0); | |
805 | return; | |
806 | } | |
807 | if (GC_finalizer_notifier != (void (*) GC_PROTO((void)))0 | |
808 | && last_finalizer_notification != GC_gc_no) { | |
809 | last_finalizer_notification = GC_gc_no; | |
810 | GC_finalizer_notifier(); | |
811 | } | |
812 | } | |
813 | ||
73ffefd0 TT |
814 | # ifdef __STDC__ |
815 | GC_PTR GC_call_with_alloc_lock(GC_fn_type fn, | |
816 | GC_PTR client_data) | |
817 | # else | |
818 | GC_PTR GC_call_with_alloc_lock(fn, client_data) | |
819 | GC_fn_type fn; | |
820 | GC_PTR client_data; | |
821 | # endif | |
822 | { | |
823 | GC_PTR result; | |
824 | DCL_LOCK_STATE; | |
825 | ||
826 | # ifdef THREADS | |
827 | DISABLE_SIGNALS(); | |
828 | LOCK(); | |
829 | SET_LOCK_HOLDER(); | |
830 | # endif | |
831 | result = (*fn)(client_data); | |
832 | # ifdef THREADS | |
9110a741 BM |
833 | # ifndef GC_ASSERTIONS |
834 | UNSET_LOCK_HOLDER(); | |
835 | # endif /* o.w. UNLOCK() does it implicitly */ | |
73ffefd0 TT |
836 | UNLOCK(); |
837 | ENABLE_SIGNALS(); | |
838 | # endif | |
839 | return(result); | |
840 | } | |
79f777fd | 841 |