misc.c raw
1 /*
2 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3 * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
4 * Copyright (c) 1999-2001 by Hewlett-Packard Company. All rights reserved.
5 * Copyright (c) 2008-2022 Ivan Maidanski
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 #include <limits.h>
20 #include <stdarg.h>
21
22 #if defined(SOLARIS) && defined(THREADS)
23 # include <sys/syscall.h>
24 #endif
25
26 #if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(SYMBIAN) \
27 || (defined(CONSOLE_LOG) && defined(MSWIN32))
28 # include <fcntl.h>
29 # include <sys/stat.h>
30 #endif
31
32 #if defined(CONSOLE_LOG) && defined(MSWIN32) && !defined(__GNUC__)
33 # include <io.h>
34 #endif
35
36 #ifdef NONSTOP
37 # include <floss.h>
38 #endif
39
40 #ifdef THREADS
41 # if defined(SN_TARGET_PSP2)
42 GC_INNER WapiMutex GC_allocate_ml_PSP2 = { 0, NULL };
43 # elif defined(GC_DEFN_ALLOCATE_ML) && !defined(USE_RWLOCK) \
44 || defined(SN_TARGET_PS3)
45 # include <pthread.h>
46 GC_INNER pthread_mutex_t GC_allocate_ml;
47 # else
48 /*
49 * For other platforms with threads, the allocator lock and, possibly,
50 * `GC_lock_holder` are defined in the thread support code.
51 */
52 # endif
53 #endif /* THREADS */
54
55 #ifdef DYNAMIC_LOADING
56 /*
57 * We need to register the main data segment. Returns `TRUE` unless
58 * this is done implicitly as part of dynamic library registration.
59 */
60 # define GC_REGISTER_MAIN_STATIC_DATA() GC_register_main_static_data()
61 #elif defined(GC_DONT_REGISTER_MAIN_STATIC_DATA)
62 # define GC_REGISTER_MAIN_STATIC_DATA() FALSE
63 #else
64 /*
65 * Do not unnecessarily call `GC_register_main_static_data()` in case
66 * `dyn_load.c` file is not linked in.
67 */
68 # define GC_REGISTER_MAIN_STATIC_DATA() TRUE
69 #endif
70
71 #ifdef NEED_CANCEL_DISABLE_COUNT
72 __thread unsigned char GC_cancel_disable_count = 0;
73 #endif
74
75 struct _GC_arrays GC_arrays /* `= { 0 }` */;
76
77 GC_INNER unsigned GC_n_mark_procs = GC_RESERVED_MARK_PROCS;
78
79 GC_INNER unsigned GC_n_kinds = GC_N_KINDS_INITIAL_VALUE;
80
81 ptr_t GC_stackbottom = 0;
82
83 #if defined(E2K) && defined(THREADS) || defined(IA64)
84 GC_INNER ptr_t GC_register_stackbottom = NULL;
85 #endif
86
87 int GC_dont_gc = FALSE;
88
89 int GC_dont_precollect = FALSE;
90
91 GC_bool GC_quiet = 0; /*< used also in `msvc_dbg.c` file */
92
93 #if !defined(NO_CLOCK) || !defined(SMALL_CONFIG)
94 GC_INNER int GC_print_stats = 0;
95 #endif
96
97 #ifdef MAKE_BACK_GRAPH
98 # ifdef GC_PRINT_BACK_HEIGHT
99 GC_INNER GC_bool GC_print_back_height = TRUE;
100 # else
101 GC_INNER GC_bool GC_print_back_height = FALSE;
102 # endif
103 #endif
104
105 #ifndef NO_DEBUGGING
106 # ifdef GC_DUMP_REGULARLY
107 GC_INNER GC_bool GC_dump_regularly = TRUE;
108 # else
109 GC_INNER GC_bool GC_dump_regularly = FALSE;
110 # endif
111 # ifndef NO_CLOCK
112 /* The time that the collector was initialized at. */
113 STATIC CLOCK_TYPE GC_init_time;
114 # endif
115 #endif /* !NO_DEBUGGING */
116
117 #ifdef KEEP_BACK_PTRS
118 GC_INNER long GC_backtraces = 0;
119 #endif
120
121 #ifdef FIND_LEAK
122 int GC_find_leak = 1;
123 #else
124 int GC_find_leak = 0;
125 #endif
126
127 #if !defined(NO_FIND_LEAK) && !defined(SHORT_DBG_HDRS)
128 # ifdef GC_FINDLEAK_DELAY_FREE
129 GC_INNER GC_bool GC_findleak_delay_free = TRUE;
130 # else
131 GC_INNER GC_bool GC_findleak_delay_free = FALSE;
132 # endif
133 #endif /* !NO_FIND_LEAK && !SHORT_DBG_HDRS */
134
135 #ifdef ALL_INTERIOR_POINTERS
136 int GC_all_interior_pointers = 1;
137 #else
138 int GC_all_interior_pointers = 0;
139 #endif
140
141 #ifdef FINALIZE_ON_DEMAND
142 int GC_finalize_on_demand = 1;
143 #else
144 int GC_finalize_on_demand = 0;
145 #endif
146
147 #ifdef JAVA_FINALIZATION
148 int GC_java_finalization = 1;
149 #else
150 int GC_java_finalization = 0;
151 #endif
152
153 /* All accesses to it should be synchronized to avoid data race. */
154 GC_finalizer_notifier_proc GC_finalizer_notifier
155 = (GC_finalizer_notifier_proc)0;
156
157 #ifdef GC_FORCE_UNMAP_ON_GCOLLECT
158 GC_INNER GC_bool GC_force_unmap_on_gcollect = TRUE;
159 #else
160 GC_INNER GC_bool GC_force_unmap_on_gcollect = FALSE;
161 #endif
162
163 #ifndef GC_LARGE_ALLOC_WARN_INTERVAL
164 # define GC_LARGE_ALLOC_WARN_INTERVAL 5
165 #endif
166
167 #ifndef NO_BLACK_LISTING
168 GC_INNER long GC_large_alloc_warn_interval = GC_LARGE_ALLOC_WARN_INTERVAL;
169 #endif
170
171 STATIC void *GC_CALLBACK
172 GC_default_oom_fn(size_t bytes_requested)
173 {
174 UNUSED_ARG(bytes_requested);
175 return NULL;
176 }
177
178 /* All accesses to it should be synchronized to avoid data race. */
179 GC_oom_func GC_oom_fn = GC_default_oom_fn;
180
181 #ifdef CAN_HANDLE_FORK
182 # ifdef HANDLE_FORK
183 GC_INNER int GC_handle_fork = 1;
184 # else
185 GC_INNER int GC_handle_fork = FALSE;
186 # endif
187
188 #elif !defined(HAVE_NO_FORK)
189 GC_API void GC_CALL
190 GC_atfork_prepare(void)
191 {
192 # ifdef THREADS
193 ABORT("fork() handling unsupported");
194 # endif
195 }
196
197 GC_API void GC_CALL
198 GC_atfork_parent(void)
199 {
200 /* Empty. */
201 }
202
203 GC_API void GC_CALL
204 GC_atfork_child(void)
205 {
206 /* Empty. */
207 }
208 #endif /* !CAN_HANDLE_FORK && !HAVE_NO_FORK */
209
210 GC_API void GC_CALL
211 GC_set_handle_fork(int value)
212 {
213 #ifdef CAN_HANDLE_FORK
214 if (!GC_is_initialized) {
215 /* Map all negative values except for -1 to a positive one. */
216 GC_handle_fork = value >= -1 ? value : 1;
217 }
218 #elif defined(THREADS) || (defined(DARWIN) && defined(MPROTECT_VDB))
219 if (!GC_is_initialized && value) {
220 # ifndef SMALL_CONFIG
221 /* Initialize `GC_manual_vdb` and `GC_stderr`. */
222 GC_init();
223 # ifndef THREADS
224 if (GC_manual_vdb)
225 return;
226 # endif
227 # endif
228 ABORT("fork() handling unsupported");
229 }
230 #else
231 /* No at-fork handler is needed in the single-threaded mode. */
232 UNUSED_ARG(value);
233 #endif
234 }
235
236 /*
237 * Set things up so that `GC_size_map[i] >= granules(i)`, but not too
238 * much bigger and so that `GC_size_map` contains relatively few
239 * distinct entries. This was originally stolen from Russ Atkinson's
240 * Cedar quantization algorithm (but we precompute it).
241 */
242 STATIC void
243 GC_init_size_map(void)
244 {
245 size_t i = 1;
246
247 /* Map size 0 to something bigger; this avoids problems at lower levels. */
248 GC_size_map[0] = 1;
249
250 for (; i <= GRANULES_TO_BYTES(GC_TINY_FREELISTS - 1) - EXTRA_BYTES; i++) {
251 GC_size_map[i] = ALLOC_REQUEST_GRANS(i);
252 #ifndef _MSC_VER
253 /* Seems to tickle bug in VC++ 2008 for x86_64. */
254 GC_ASSERT(GC_size_map[i] < GC_TINY_FREELISTS);
255 #endif
256 }
257 /* We leave the rest of the array to be filled in on demand. */
258 }
259
260 /*
261 * The following is a gross hack to deal with a problem that can occur
262 * on machines that are sloppy about stack frame sizes, notably SPARC.
263 * Bogus pointers may be written to the stack and not cleared for
264 * a LONG time, because they always fall into holes in stack frames
265 * that are not written. We partially address this by clearing
266 * sections of the stack whenever we get control.
267 */
268
269 #ifndef SMALL_CLEAR_SIZE
270 /* Clear this many words of the stack every time. */
271 # define SMALL_CLEAR_SIZE 256
272 #endif
273
274 #if defined(ALWAYS_SMALL_CLEAR_STACK) || defined(STACK_NOT_SCANNED)
275 GC_API void *GC_CALL
276 GC_clear_stack(void *arg)
277 {
278 # ifndef STACK_NOT_SCANNED
279 volatile ptr_t dummy[SMALL_CLEAR_SIZE];
280
281 BZERO(CAST_AWAY_VOLATILE_PVOID(dummy), sizeof(dummy));
282 # endif
283 return arg;
284 }
285 #else
286
287 # ifdef THREADS
288 /* Clear this much sometimes. */
289 # define BIG_CLEAR_SIZE 2048
290 # else
291 /* `GC_gc_no` value when we last did this. */
292 STATIC word GC_stack_last_cleared = 0;
293
294 STATIC word GC_bytes_allocd_at_reset = 0;
295
296 /*
297 * Coolest stack pointer value from which we have already cleared
298 * the stack.
299 */
300 STATIC ptr_t GC_min_sp = NULL;
301
302 /*
303 * The "hottest" stack pointer value we have seen recently.
304 * Degrades over time.
305 */
306 STATIC ptr_t GC_high_water = NULL;
307
308 # define DEGRADE_RATE 50
309 # endif
310
311 # if defined(__APPLE_CC__) && !GC_CLANG_PREREQ(6, 0)
312 # define CLEARSTACK_LIMIT_MODIFIER volatile /*< to workaround some bug */
313 # else
314 # define CLEARSTACK_LIMIT_MODIFIER /*< empty */
315 # endif
316
317 EXTERN_C_BEGIN
318 void *GC_clear_stack_inner(void *, CLEARSTACK_LIMIT_MODIFIER ptr_t);
319 EXTERN_C_END
320
321 # ifndef ASM_CLEAR_CODE
322 /*
323 * Clear the stack up to about `limit`. Return `arg`. This function is
324 * not `static` because it could also be erroneously defined in `.S` file,
325 * so this error would be caught by the linker.
326 */
327 void *
328 GC_clear_stack_inner(void *arg, CLEARSTACK_LIMIT_MODIFIER ptr_t limit)
329 {
330 # define CLEAR_SIZE 213 /*< granularity */
331 volatile ptr_t dummy[CLEAR_SIZE];
332
333 BZERO(CAST_AWAY_VOLATILE_PVOID(dummy), sizeof(dummy));
334 if (HOTTER_THAN((/* no volatile */ ptr_t)limit, GC_approx_sp())) {
335 (void)GC_clear_stack_inner(arg, limit);
336 }
337 /*
338 * Make sure the recursive call is not a tail call, and the `bzero` call
339 * is not recognized as dead code.
340 */
341 # if defined(CPPCHECK)
342 GC_noop1(ADDR(dummy[0]));
343 # else
344 GC_noop1(COVERT_DATAFLOW(ADDR(dummy)));
345 # endif
346 return arg;
347 }
348 # endif /* !ASM_CLEAR_CODE */
349
350 # ifdef THREADS
351 /* Used to occasionally clear a bigger chunk. */
352 /* TODO: Should be more random than it is... */
353 static unsigned
354 next_random_no(void)
355 {
356 # ifdef AO_HAVE_fetch_and_add1
357 static volatile AO_t random_no;
358
359 return (unsigned)AO_fetch_and_add1(&random_no) % 13;
360 # else
361 static unsigned random_no = 0;
362
363 return (random_no++) % 13;
364 # endif
365 }
366 # endif /* THREADS */
367
368 GC_API void *GC_CALL
369 GC_clear_stack(void *arg)
370 {
371 /* Note: this is hotter than the actual stack pointer. */
372 ptr_t sp = GC_approx_sp();
373 # ifdef THREADS
374 volatile ptr_t dummy[SMALL_CLEAR_SIZE];
375 # endif
376
377 /*
378 * Extra bytes we clear every time. This clears our own activation
379 * record, and should cause more frequent clearing near the cold end
380 * of the stack, a good thing.
381 */
382 # define SLOP 400
383
384 /*
385 * We make `GC_high_water` this much hotter than we really saw it,
386 * to cover for the GC noise above our current frame.
387 */
388 # define GC_SLOP 4000
389
390 /*
391 * We restart the clearing process after this many bytes of allocation.
392 * Otherwise very heavily recursive programs with sparse stacks may
393 * result in heaps that grow almost without bounds. As the heap gets
394 * larger, collection frequency decreases, thus clearing frequency
395 * would decrease, thus more junk remains accessible, thus the heap
396 * gets larger...
397 */
398 # define CLEAR_THRESHOLD 100000
399
400 # ifdef THREADS
401 if (next_random_no() == 0) {
402 ptr_t limit = sp;
403
404 MAKE_HOTTER(limit, BIG_CLEAR_SIZE * sizeof(ptr_t));
405 /*
406 * Make it sufficiently aligned for assembly implementations
407 * of `GC_clear_stack_inner`.
408 */
409 limit = PTR_ALIGN_DOWN(limit, 0x10);
410 return GC_clear_stack_inner(arg, limit);
411 }
412 BZERO(CAST_AWAY_VOLATILE_PVOID(dummy), sizeof(dummy));
413 # else
414 if (GC_gc_no != GC_stack_last_cleared) {
415 /* Start things over, so we clear the entire stack again. */
416 if (UNLIKELY(NULL == GC_high_water))
417 GC_high_water = (ptr_t)GC_stackbottom;
418 GC_min_sp = GC_high_water;
419 GC_stack_last_cleared = GC_gc_no;
420 GC_bytes_allocd_at_reset = GC_bytes_allocd;
421 }
422 /* Adjust `GC_high_water`. */
423 GC_ASSERT(GC_high_water != NULL);
424 MAKE_COOLER(GC_high_water, PTRS_TO_BYTES(DEGRADE_RATE) + GC_SLOP);
425 if (HOTTER_THAN(sp, GC_high_water))
426 GC_high_water = sp;
427 MAKE_HOTTER(GC_high_water, GC_SLOP);
428 {
429 ptr_t limit = GC_min_sp;
430
431 MAKE_HOTTER(limit, SLOP);
432 if (HOTTER_THAN(limit, sp)) {
433 limit = PTR_ALIGN_DOWN(limit, 0x10);
434 GC_min_sp = sp;
435 return GC_clear_stack_inner(arg, limit);
436 }
437 }
438 if (GC_bytes_allocd - GC_bytes_allocd_at_reset > CLEAR_THRESHOLD) {
439 /* Restart clearing process, but limit how much clearing we do. */
440 GC_min_sp = sp;
441 MAKE_HOTTER(GC_min_sp, CLEAR_THRESHOLD / 4);
442 if (HOTTER_THAN(GC_min_sp, GC_high_water))
443 GC_min_sp = GC_high_water;
444 GC_bytes_allocd_at_reset = GC_bytes_allocd;
445 }
446 # endif
447 return arg;
448 }
449
450 #endif /* !ALWAYS_SMALL_CLEAR_STACK && !STACK_NOT_SCANNED */
451
452 GC_API void *GC_CALL
453 GC_base(void *p)
454 {
455 ptr_t r = (ptr_t)p;
456 struct hblk *h;
457 bottom_index *bi;
458 hdr *hhdr;
459 ptr_t limit;
460 size_t sz;
461
462 if (UNLIKELY(!GC_is_initialized))
463 return NULL;
464 h = HBLKPTR(r);
465 GET_BI(r, bi);
466 hhdr = HDR_FROM_BI(bi, r);
467 if (NULL == hhdr)
468 return NULL;
469
470 /*
471 * If it is a pointer to the middle of a large object, then move it
472 * to the beginning.
473 */
474 if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
475 h = GC_find_starting_hblk(h, &hhdr);
476 r = (ptr_t)h;
477 }
478 if (HBLK_IS_FREE(hhdr))
479 return NULL;
480
481 /* Make sure `r` points to the beginning of the object. */
482 r = PTR_ALIGN_DOWN(r, sizeof(ptr_t));
483
484 sz = hhdr->hb_sz;
485 r -= HBLKDISPL(r) % sz;
486 limit = r + sz;
487 if ((ADDR_LT((ptr_t)(h + 1), limit) && sz <= HBLKSIZE)
488 || ADDR_GE((ptr_t)p, limit))
489 return NULL;
490
491 return r;
492 }
493
494 GC_API int GC_CALL
495 GC_is_heap_ptr(const void *p)
496 {
497 bottom_index *bi;
498
499 GC_ASSERT(GC_is_initialized);
500 GET_BI(p, bi);
501 return HDR_FROM_BI(bi, p) != 0;
502 }
503
504 GC_API size_t GC_CALL
505 GC_size(const void *p)
506 {
507 const hdr *hhdr;
508
509 /* Accept `NULL` for compatibility with `malloc_usable_size()`. */
510 if (UNLIKELY(NULL == p))
511 return 0;
512
513 hhdr = HDR(p);
514 return hhdr->hb_sz;
515 }
516
517 /*
518 * These getters remain unsynchronized for compatibility (since some clients
519 * could call some of them from a GC callback holding the allocator lock).
520 */
521
522 GC_API size_t GC_CALL
523 GC_get_heap_size(void)
524 {
525 /*
526 * Ignore the memory space returned to OS (i.e. count only the space
527 * owned by the garbage collector).
528 */
529 return (size_t)(GC_heapsize - GC_unmapped_bytes);
530 }
531
532 GC_API size_t GC_CALL
533 GC_get_obtained_from_os_bytes(void)
534 {
535 return (size_t)GC_our_mem_bytes;
536 }
537
538 GC_API size_t GC_CALL
539 GC_get_free_bytes(void)
540 {
541 /* Ignore the memory space returned to OS. */
542 return (size_t)(GC_large_free_bytes - GC_unmapped_bytes);
543 }
544
545 GC_API size_t GC_CALL
546 GC_get_unmapped_bytes(void)
547 {
548 return (size_t)GC_unmapped_bytes;
549 }
550
551 GC_API size_t GC_CALL
552 GC_get_bytes_since_gc(void)
553 {
554 return (size_t)GC_bytes_allocd;
555 }
556
557 GC_API size_t GC_CALL
558 GC_get_total_bytes(void)
559 {
560 return (size_t)(GC_bytes_allocd + GC_bytes_allocd_before_gc);
561 }
562
563 #ifndef GC_GET_HEAP_USAGE_NOT_NEEDED
564
565 GC_API size_t GC_CALL
566 GC_get_size_map_at(int i)
567 {
568 if ((unsigned)i > MAXOBJBYTES)
569 return GC_SIZE_MAX;
570 return GRANULES_TO_BYTES(GC_size_map[i]);
571 }
572
573 GC_API void GC_CALL
574 GC_get_heap_usage_safe(GC_word *pheap_size, GC_word *pfree_bytes,
575 GC_word *punmapped_bytes, GC_word *pbytes_since_gc,
576 GC_word *ptotal_bytes)
577 {
578 READER_LOCK();
579 if (pheap_size != NULL)
580 *pheap_size = GC_heapsize - GC_unmapped_bytes;
581 if (pfree_bytes != NULL)
582 *pfree_bytes = GC_large_free_bytes - GC_unmapped_bytes;
583 if (punmapped_bytes != NULL)
584 *punmapped_bytes = GC_unmapped_bytes;
585 if (pbytes_since_gc != NULL)
586 *pbytes_since_gc = GC_bytes_allocd;
587 if (ptotal_bytes != NULL)
588 *ptotal_bytes = GC_bytes_allocd + GC_bytes_allocd_before_gc;
589 READER_UNLOCK();
590 }
591
592 GC_INNER word GC_reclaimed_bytes_before_gc = 0;
593
594 /* Fill in GC statistics provided the destination is of enough size. */
595 static void
596 fill_prof_stats(struct GC_prof_stats_s *pstats)
597 {
598 pstats->heapsize_full = GC_heapsize;
599 pstats->free_bytes_full = GC_large_free_bytes;
600 pstats->unmapped_bytes = GC_unmapped_bytes;
601 pstats->bytes_allocd_since_gc = GC_bytes_allocd;
602 pstats->allocd_bytes_before_gc = GC_bytes_allocd_before_gc;
603 pstats->non_gc_bytes = GC_non_gc_bytes;
604 pstats->gc_no = GC_gc_no; /*< could be -1 */
605 # ifdef PARALLEL_MARK
606 pstats->markers_m1 = (word)((GC_signed_word)GC_markers_m1);
607 # else
608 /* A single marker. */
609 pstats->markers_m1 = 0;
610 # endif
611 pstats->bytes_reclaimed_since_gc
612 = GC_bytes_found > 0 ? (word)GC_bytes_found : 0;
613 pstats->reclaimed_bytes_before_gc = GC_reclaimed_bytes_before_gc;
614 pstats->expl_freed_bytes_since_gc = GC_bytes_freed; /*< since gc-7.7 */
615 pstats->obtained_from_os_bytes = GC_our_mem_bytes; /*< since gc-8.2 */
616 }
617
618 # include <string.h> /*< for `memset()` */
619
620 GC_API size_t GC_CALL
621 GC_get_prof_stats(struct GC_prof_stats_s *pstats, size_t stats_sz)
622 {
623 struct GC_prof_stats_s stats;
624
625 READER_LOCK();
626 fill_prof_stats(stats_sz >= sizeof(stats) ? pstats : &stats);
627 READER_UNLOCK();
628
629 if (stats_sz == sizeof(stats)) {
630 return sizeof(stats);
631 } else if (stats_sz > sizeof(stats)) {
632 /* Fill in the remaining part with -1. */
633 memset((char *)pstats + sizeof(stats), 0xff, stats_sz - sizeof(stats));
634 return sizeof(stats);
635 } else {
636 if (LIKELY(stats_sz > 0))
637 BCOPY(&stats, pstats, stats_sz);
638 return stats_sz;
639 }
640 }
641
642 # ifdef THREADS
643 GC_API size_t GC_CALL
644 GC_get_prof_stats_unsafe(struct GC_prof_stats_s *pstats, size_t stats_sz)
645 {
646 struct GC_prof_stats_s stats;
647
648 if (stats_sz >= sizeof(stats)) {
649 fill_prof_stats(pstats);
650 if (stats_sz > sizeof(stats))
651 memset((char *)pstats + sizeof(stats), 0xff, stats_sz - sizeof(stats));
652 return sizeof(stats);
653 } else {
654 if (LIKELY(stats_sz > 0)) {
655 fill_prof_stats(&stats);
656 BCOPY(&stats, pstats, stats_sz);
657 }
658 return stats_sz;
659 }
660 }
661 # endif /* THREADS */
662
663 #endif /* !GC_GET_HEAP_USAGE_NOT_NEEDED */
664
665 #if defined(THREADS) && !defined(SIGNAL_BASED_STOP_WORLD)
666 /* The collector does not use signals to suspend and restart threads. */
667
668 GC_API void GC_CALL
669 GC_set_suspend_signal(int sig)
670 {
671 UNUSED_ARG(sig);
672 }
673
674 GC_API void GC_CALL
675 GC_set_thr_restart_signal(int sig)
676 {
677 UNUSED_ARG(sig);
678 }
679
680 GC_API int GC_CALL
681 GC_get_suspend_signal(void)
682 {
683 return -1;
684 }
685
686 GC_API int GC_CALL
687 GC_get_thr_restart_signal(void)
688 {
689 return -1;
690 }
691 #endif /* THREADS && !SIGNAL_BASED_STOP_WORLD */
692
693 #if !defined(_MAX_PATH) && defined(ANY_MSWIN)
694 # define _MAX_PATH MAX_PATH
695 #endif
696
697 #ifdef GC_READ_ENV_FILE
698 /* This works for Win32/WinCE for now. Really useful only for WinCE. */
699
700 /*
701 * The content of the `.gc.env` file with CR and LF replaced to '\0'.
702 * `NULL` if the file is missing or empty. Otherwise, always ends with '\0'
703 * (designating the end of the file).
704 */
705 STATIC char *GC_envfile_content = NULL;
706
707 /* Length of `GC_envfile_content` (if non-`NULL`). */
708 STATIC unsigned GC_envfile_length = 0;
709
710 # ifndef GC_ENVFILE_MAXLEN
711 # define GC_ENVFILE_MAXLEN 0x4000
712 # endif
713
714 # define GC_ENV_FILE_EXT ".gc.env"
715
716 /* The routine initializes `GC_envfile_content` from the `.gc.env` file. */
717 STATIC void
718 GC_envfile_init(void)
719 {
720 # ifdef ANY_MSWIN
721 HANDLE hFile;
722 char *content;
723 unsigned ofs;
724 unsigned len;
725 DWORD nBytesRead;
726 TCHAR path[_MAX_PATH + 0x10]; /*< buffer for file path with extension */
727 size_t bytes_to_get;
728
729 GC_ASSERT(I_HOLD_LOCK());
730 len = (unsigned)GetModuleFileName(NULL /* `hModule` */, path, _MAX_PATH + 1);
731 /* If `GetModuleFileName()` failed, then len is 0. */
732 if (len > 4 && path[len - 4] == (TCHAR)'.') {
733 /* Strip the executable file extension. */
734 len -= 4;
735 }
736 BCOPY(TEXT(GC_ENV_FILE_EXT), &path[len], sizeof(TEXT(GC_ENV_FILE_EXT)));
737 hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
738 NULL /* `lpSecurityAttributes` */, OPEN_EXISTING,
739 FILE_ATTRIBUTE_NORMAL, NULL /* `hTemplateFile` */);
740 if (hFile == INVALID_HANDLE_VALUE) {
741 /* The file is absent or the operation failed. */
742 return;
743 }
744 len = (unsigned)GetFileSize(hFile, NULL);
745 if (len <= 1 || len >= GC_ENVFILE_MAXLEN) {
746 CloseHandle(hFile);
747 /* Invalid file length - ignoring the file content. */
748 return;
749 }
750 /*
751 * At this execution point, `GC_setpagesize()` and `GC_init_win32()`
752 * must already be called (for `GET_MEM()` to work correctly).
753 */
754 GC_ASSERT(GC_page_size != 0);
755 bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP((size_t)len + 1);
756 content = GC_os_get_mem(bytes_to_get);
757 if (content == NULL) {
758 CloseHandle(hFile);
759 /* An allocation failure. */
760 return;
761 }
762 ofs = 0;
763 nBytesRead = (DWORD)-1L;
764 /* Last `ReadFile()` call should clear `nBytesRead` on success. */
765 while (ReadFile(hFile, content + ofs, len - ofs + 1, &nBytesRead,
766 NULL /* `lpOverlapped` */)
767 && nBytesRead != 0) {
768 if ((ofs += nBytesRead) > len)
769 break;
770 }
771 CloseHandle(hFile);
772 if (ofs != len || nBytesRead != 0) {
773 /* TODO: Recycle content. */
774 /* Read operation has failed - ignoring the file content. */
775 return;
776 }
777 content[ofs] = '\0';
778 while (ofs-- > 0) {
779 if (content[ofs] == '\r' || content[ofs] == '\n')
780 content[ofs] = '\0';
781 }
782 GC_ASSERT(NULL == GC_envfile_content);
783 GC_envfile_length = len + 1;
784 GC_envfile_content = content;
785 # endif
786 }
787
788 GC_INNER char *
789 GC_envfile_getenv(const char *name)
790 {
791 char *p;
792 const char *end_of_content;
793 size_t namelen;
794
795 # ifndef NO_GETENV
796 /* Try the standard `getenv()` first. */
797 p = getenv(name);
798 if (p != NULL)
799 return *p != '\0' ? p : NULL;
800 # endif
801 p = GC_envfile_content;
802 if (NULL == p) {
803 /* The `.gc.env` file is absent (or empty). */
804 return NULL;
805 }
806 namelen = strlen(name);
807 if (0 == namelen) {
808 /* A sanity check. */
809 return NULL;
810 }
811 for (end_of_content = p + GC_envfile_length;
812 ADDR_LT((ptr_t)p, (ptr_t)end_of_content); p += strlen(p) + 1) {
813 if (strncmp(p, name, namelen) == 0 && *(p += namelen) == '=') {
814 /* The match is found; skip "=". */
815 p++;
816 return *p != '\0' ? p : NULL;
817 }
818 /* If not matching then skip to the next line. */
819 }
820 GC_ASSERT(p == end_of_content);
821 /* No match is found. */
822 return NULL;
823 }
824 #endif /* GC_READ_ENV_FILE */
825
826 GC_INNER GC_bool GC_is_initialized = FALSE;
827
828 GC_API int GC_CALL
829 GC_is_init_called(void)
830 {
831 return (int)GC_is_initialized;
832 }
833
834 #if defined(GC_WIN32_THREADS) \
835 && ((defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE))
836 GC_INNER CRITICAL_SECTION GC_write_cs;
837 #endif
838
839 #ifndef DONT_USE_ATEXIT
840 # if !defined(SMALL_CONFIG)
841 /*
842 * A dedicated variable to avoid a garbage collection on abort.
843 * `GC_find_leak` cannot be used for this purpose as otherwise
844 * TSan finds a data race (between `GC_default_on_abort` and, e.g.,
845 * `GC_finish_collection`).
846 */
847 static GC_bool skip_gc_atexit = FALSE;
848 # else
849 # define skip_gc_atexit FALSE
850 # endif
851
852 STATIC void
853 GC_exit_check(void)
854 {
855 if (GC_find_leak && !skip_gc_atexit) {
856 # ifdef THREADS
857 /*
858 * Check that the thread executing at-exit functions is the same as
859 * the one performed the GC initialization, otherwise the latter
860 * thread might already be dead but still registered and this, as
861 * a consequence, might cause a signal delivery fail when suspending
862 * the threads on platforms that do not guarantee `ESRCH` returned
863 * if the signal is not delivered. It should also prevent
864 * "Collecting from unknown thread" abort in `GC_push_all_stacks()`.
865 */
866 if (!GC_is_main_thread() || !GC_thread_is_registered())
867 return;
868 # endif
869 GC_gcollect();
870 }
871 }
872 #endif /* !DONT_USE_ATEXIT */
873
874 #if defined(UNIX_LIKE) && !defined(NO_DEBUGGING)
875 static void
876 looping_handler(int sig)
877 {
878 GC_err_printf("Caught signal %d: looping in handler\n", sig);
879 for (;;) {
880 /* Empty. */
881 }
882 }
883
884 static GC_bool installed_looping_handler = FALSE;
885
886 static void
887 maybe_install_looping_handler(void)
888 {
889 /*
890 * Install looping handler before the write fault handler,
891 * so we handle write faults correctly.
892 */
893 if (!installed_looping_handler && GETENV("GC_LOOP_ON_ABORT") != NULL) {
894 GC_set_and_save_fault_handler(looping_handler);
895 installed_looping_handler = TRUE;
896 }
897 }
898
899 #else /* !UNIX_LIKE */
900 # define maybe_install_looping_handler()
901 #endif
902
903 #define GC_DEFAULT_STDERR_FD 2
904 #ifdef KOS
905 # define GC_DEFAULT_STDOUT_FD GC_DEFAULT_STDERR_FD
906 #else
907 # define GC_DEFAULT_STDOUT_FD 1
908 #endif
909
910 #if !defined(OS2) && !defined(GC_ANDROID_LOG) && !defined(NN_PLATFORM_CTR) \
911 && !defined(NINTENDO_SWITCH) \
912 && (!defined(MSWIN32) || defined(CONSOLE_LOG)) && !defined(MSWINCE)
913 STATIC int GC_stdout = GC_DEFAULT_STDOUT_FD;
914 STATIC int GC_stderr = GC_DEFAULT_STDERR_FD;
915 STATIC int GC_log = GC_DEFAULT_STDERR_FD;
916
917 # ifndef MSWIN32
918 GC_API void GC_CALL
919 GC_set_log_fd(int fd)
920 {
921 GC_log = fd;
922 }
923 # endif
924 #endif
925
926 #ifdef MSGBOX_ON_ERROR
927 STATIC void
928 GC_win32_MessageBoxA(const char *msg, const char *caption, unsigned flags)
929 {
930 # ifndef DONT_USE_USER32_DLL
931 /* Use static binding to `user32.dll` file. */
932 (void)MessageBoxA(NULL, msg, caption, flags);
933 # else
934 /* This simplifies linking - resolve `MessageBoxA()` at run-time. */
935 HINSTANCE hU32 = LoadLibrary(TEXT("user32.dll"));
936 if (hU32) {
937 FARPROC pfn = GetProcAddress(hU32, "MessageBoxA");
938 if (pfn)
939 (void)(*(int(WINAPI *)(HWND, LPCSTR, LPCSTR, UINT))(GC_funcptr_uint)pfn)(
940 NULL /* `hWnd` */, msg, caption, flags);
941 (void)FreeLibrary(hU32);
942 }
943 # endif
944 }
945 #endif /* MSGBOX_ON_ERROR */
946
947 #if defined(THREADS) && defined(UNIX_LIKE) && !defined(NO_GETCONTEXT)
948 static void
949 callee_saves_pushed_dummy_fn(ptr_t data, void *context)
950 {
951 UNUSED_ARG(data);
952 UNUSED_ARG(context);
953 }
954 #endif
955
956 #ifdef MANUAL_VDB
957 static GC_bool manual_vdb_allowed = TRUE;
958 #else
959 static GC_bool manual_vdb_allowed = FALSE;
960 #endif
961
962 GC_API void GC_CALL
963 GC_set_manual_vdb_allowed(int value)
964 {
965 manual_vdb_allowed = (GC_bool)value;
966 }
967
968 GC_API int GC_CALL
969 GC_get_manual_vdb_allowed(void)
970 {
971 return (int)manual_vdb_allowed;
972 }
973
974 GC_API unsigned GC_CALL
975 GC_get_supported_vdbs(void)
976 {
977 #ifdef GC_DISABLE_INCREMENTAL
978 return GC_VDB_NONE;
979 #else
980 # if defined(CPPCHECK)
981 /* Workaround a warning about redundant `| 0`. */
982 volatile unsigned zero = 0;
983 # endif
984 return
985 # if defined(CPPCHECK)
986 zero
987 # else
988 0
989 # endif
990 # ifndef NO_MANUAL_VDB
991 | GC_VDB_MANUAL
992 # endif
993 # ifdef DEFAULT_VDB
994 | GC_VDB_DEFAULT
995 # endif
996 # ifdef MPROTECT_VDB
997 | GC_VDB_MPROTECT
998 # endif
999 # ifdef GWW_VDB
1000 | GC_VDB_GWW
1001 # endif
1002 # ifdef PROC_VDB
1003 | GC_VDB_PROC
1004 # endif
1005 # ifdef SOFT_VDB
1006 | GC_VDB_SOFT
1007 # endif
1008 ;
1009 #endif
1010 }
1011
1012 #ifndef GC_DISABLE_INCREMENTAL
1013 static void
1014 set_incremental_mode_on(void)
1015 {
1016 GC_ASSERT(I_HOLD_LOCK());
1017 # ifndef NO_MANUAL_VDB
1018 if (manual_vdb_allowed) {
1019 GC_manual_vdb = TRUE;
1020 GC_incremental = TRUE;
1021 } else
1022 # endif
1023 /* else */ {
1024 /*
1025 * For `GWW_VDB` on Win32, this needs to happen before any heap memory
1026 * is allocated.
1027 */
1028 GC_incremental = GC_dirty_init();
1029 }
1030 }
1031 #endif /* !GC_DISABLE_INCREMENTAL */
1032
1033 STATIC word
1034 GC_parse_mem_size_arg(const char *str)
1035 {
1036 word result;
1037 char *endptr;
1038 char ch;
1039
1040 if ('\0' == *str)
1041 return GC_WORD_MAX; /*< bad value */
1042 result = (word)STRTOULL(str, &endptr, 10);
1043 ch = *endptr;
1044 if (ch != '\0') {
1045 if (*(endptr + 1) != '\0')
1046 return GC_WORD_MAX;
1047 /* Allow "k", "M" or "G" suffix. */
1048 switch (ch) {
1049 case 'K':
1050 case 'k':
1051 result <<= 10;
1052 break;
1053 #if CPP_WORDSZ >= 32
1054 case 'M':
1055 case 'm':
1056 result <<= 20;
1057 break;
1058 case 'G':
1059 case 'g':
1060 result <<= 30;
1061 break;
1062 #endif
1063 default:
1064 result = GC_WORD_MAX;
1065 }
1066 }
1067 return result;
1068 }
1069
1070 #define GC_LOG_STD_NAME "gc.log"
1071
1072 GC_API void GC_CALL
1073 GC_init(void)
1074 {
1075 word initial_heap_sz;
1076 IF_CANCEL(int cancel_state;)
1077
1078 if (LIKELY(GC_is_initialized))
1079 return;
1080 #ifdef REDIRECT_MALLOC
1081 {
1082 static GC_bool init_started = FALSE;
1083 if (init_started)
1084 ABORT("Redirected malloc() called during GC init");
1085 init_started = TRUE;
1086 }
1087 #endif
1088
1089 #if defined(GC_INITIAL_HEAP_SIZE) && !defined(CPPCHECK)
1090 initial_heap_sz = GC_INITIAL_HEAP_SIZE;
1091 #else
1092 initial_heap_sz = MINHINCR * HBLKSIZE;
1093 #endif
1094
1095 DISABLE_CANCEL(cancel_state);
1096 /*
1097 * Note that although we are nominally called with the allocator lock
1098 * held, now it is only really acquired once a second thread is created.
1099 * And the initialization code needs to run before then. Thus we really
1100 * do not hold any locks, and can safely initialize them here.
1101 */
1102 #ifdef THREADS
1103 # ifndef GC_ALWAYS_MULTITHREADED
1104 GC_ASSERT(!GC_need_to_lock);
1105 # endif
1106 {
1107 # if !defined(GC_BUILTIN_ATOMIC) && defined(HP_PA) \
1108 && (defined(USE_SPIN_LOCK) || defined(NEED_FAULT_HANDLER_LOCK))
1109 AO_TS_t ts_init = AO_TS_INITIALIZER;
1110
1111 /* Arrays can only be initialized when declared. */
1112 # ifdef USE_SPIN_LOCK
1113 BCOPY(&ts_init, (/* no volatile */ void *)&GC_allocate_lock,
1114 sizeof(GC_allocate_lock));
1115 # endif
1116 # ifdef NEED_FAULT_HANDLER_LOCK
1117 BCOPY(&ts_init, (/* no volatile */ void *)&GC_fault_handler_lock,
1118 sizeof(GC_fault_handler_lock));
1119 # endif
1120 # else
1121 # ifdef USE_SPIN_LOCK
1122 GC_allocate_lock = AO_TS_INITIALIZER;
1123 # endif
1124 # ifdef NEED_FAULT_HANDLER_LOCK
1125 GC_fault_handler_lock = AO_TS_INITIALIZER;
1126 # endif
1127 # endif
1128 }
1129 # ifdef SN_TARGET_PS3
1130 {
1131 pthread_mutexattr_t mattr;
1132
1133 if (pthread_mutexattr_init(&mattr) != 0)
1134 ABORT("pthread_mutexattr_init failed");
1135 if (pthread_mutex_init(&GC_allocate_ml, &mattr) != 0)
1136 ABORT("pthread_mutex_init failed");
1137 (void)pthread_mutexattr_destroy(&mattr);
1138 }
1139 # endif
1140 #endif /* THREADS */
1141 #if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS)
1142 # ifndef SPIN_COUNT
1143 # define SPIN_COUNT 4000
1144 # endif
1145 # ifdef USE_RWLOCK
1146 /* TODO: Probably use `SRWLOCK_INIT` instead. */
1147 InitializeSRWLock(&GC_allocate_ml);
1148 # elif defined(MSWINRT_FLAVOR)
1149 InitializeCriticalSectionAndSpinCount(&GC_allocate_ml, SPIN_COUNT);
1150 # else
1151 {
1152 # ifndef MSWINCE
1153 FARPROC pfn = 0;
1154 HMODULE hK32 = GetModuleHandle(TEXT("kernel32.dll"));
1155 if (hK32)
1156 pfn = GetProcAddress(hK32, "InitializeCriticalSectionAndSpinCount");
1157 if (pfn) {
1158 (*(BOOL(WINAPI *)(LPCRITICAL_SECTION, DWORD))(GC_funcptr_uint)pfn)(
1159 &GC_allocate_ml, SPIN_COUNT);
1160 } else
1161 # endif /* !MSWINCE */
1162 /* else */ InitializeCriticalSection(&GC_allocate_ml);
1163 }
1164 # endif
1165 #endif /* GC_WIN32_THREADS && !GC_PTHREADS */
1166 #if defined(GC_WIN32_THREADS) \
1167 && ((defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE))
1168 InitializeCriticalSection(&GC_write_cs);
1169 #endif
1170 #if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED)
1171 /* Just to set `GC_lock_holder`. */
1172 LOCK();
1173 #endif
1174 #ifdef DYNAMIC_POINTER_MASK
1175 if (0 == GC_pointer_mask)
1176 GC_pointer_mask = GC_WORD_MAX;
1177 #endif
1178 GC_setpagesize();
1179 #ifdef MSWIN32
1180 GC_init_win32();
1181 #endif
1182 #ifdef GC_READ_ENV_FILE
1183 GC_envfile_init();
1184 #endif
1185 #if !defined(NO_CLOCK) || !defined(SMALL_CONFIG)
1186 # ifdef GC_PRINT_VERBOSE_STATS
1187 /*
1188 * This is useful for debugging and profiling on platforms with
1189 * missing `getenv()` (like WinCE).
1190 */
1191 GC_print_stats = VERBOSE;
1192 # else
1193 if (GETENV("GC_PRINT_VERBOSE_STATS") != NULL) {
1194 GC_print_stats = VERBOSE;
1195 } else if (GETENV("GC_PRINT_STATS") != NULL) {
1196 GC_print_stats = 1;
1197 }
1198 # endif
1199 #endif
1200 #if ((defined(UNIX_LIKE) && !defined(GC_ANDROID_LOG)) \
1201 || (defined(CONSOLE_LOG) && defined(MSWIN32)) || defined(CYGWIN32) \
1202 || defined(SYMBIAN)) \
1203 && !defined(SMALL_CONFIG)
1204 {
1205 const char *fname = TRUSTED_STRING(GETENV("GC_LOG_FILE"));
1206 # ifdef GC_LOG_TO_FILE_ALWAYS
1207 if (NULL == fname)
1208 fname = GC_LOG_STD_NAME;
1209 # else
1210 if (fname != NULL)
1211 # endif
1212 {
1213 # if defined(_MSC_VER)
1214 int log_d = _open(fname, O_CREAT | O_WRONLY | O_APPEND);
1215 # else
1216 int log_d = open(fname, O_CREAT | O_WRONLY | O_APPEND, 0644);
1217 # endif
1218 if (log_d < 0) {
1219 GC_err_printf("Failed to open %s as log file\n", fname);
1220 } else {
1221 const char *str;
1222 GC_log = log_d;
1223 str = GETENV("GC_ONLY_LOG_TO_FILE");
1224 # ifdef GC_ONLY_LOG_TO_FILE
1225 /*
1226 * The similar environment variable set to "0"
1227 * overrides the effect of the macro defined.
1228 */
1229 if (str != NULL && str[0] == '0' && str[1] == '\0')
1230 # else
1231 /*
1232 * Otherwise setting the environment variable to anything other
1233 * than "0" will prevent from redirecting `stdout` and `stderr`
1234 * to the collector log file.
1235 */
1236 if (str == NULL || (str[0] == '0' && str[1] == '\0'))
1237 # endif
1238 {
1239 GC_stdout = log_d;
1240 GC_stderr = log_d;
1241 }
1242 }
1243 }
1244 }
1245 #endif
1246 #if !defined(NO_DEBUGGING) && !defined(GC_DUMP_REGULARLY)
1247 if (GETENV("GC_DUMP_REGULARLY") != NULL) {
1248 GC_dump_regularly = TRUE;
1249 }
1250 #endif
1251 #ifdef KEEP_BACK_PTRS
1252 {
1253 const char *str = GETENV("GC_BACKTRACES");
1254
1255 if (str != NULL) {
1256 GC_backtraces = atol(str);
1257 if (str[0] == '\0')
1258 GC_backtraces = 1;
1259 }
1260 }
1261 #endif
1262 #ifndef NO_FIND_LEAK
1263 if (GETENV("GC_FIND_LEAK") != NULL) {
1264 GC_find_leak = 1;
1265 }
1266 # ifndef SHORT_DBG_HDRS
1267 if (GETENV("GC_FINDLEAK_DELAY_FREE") != NULL) {
1268 GC_findleak_delay_free = TRUE;
1269 }
1270 # endif
1271 #endif
1272 if (GETENV("GC_ALL_INTERIOR_POINTERS") != NULL) {
1273 GC_all_interior_pointers = 1;
1274 }
1275 if (GETENV("GC_DONT_GC") != NULL) {
1276 #if defined(LINT2) \
1277 && !(defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED))
1278 GC_disable();
1279 #else
1280 GC_dont_gc = 1;
1281 #endif
1282 }
1283 #if !defined(SMALL_CONFIG) && !defined(GC_PRINT_BACK_HEIGHT)
1284 if (GETENV("GC_PRINT_BACK_HEIGHT") != NULL) {
1285 # ifdef MAKE_BACK_GRAPH
1286 GC_print_back_height = TRUE;
1287 # else
1288 GC_err_printf("Back height is not available!\n");
1289 # endif
1290 }
1291 #endif
1292 {
1293 const char *str = GETENV("GC_TRACE");
1294
1295 if (str != NULL) {
1296 #ifndef ENABLE_TRACE
1297 WARN("Tracing not enabled: Ignoring GC_TRACE value\n", 0);
1298 #else
1299 ptr_t p = MAKE_CPTR(STRTOULL(str, NULL, 16));
1300
1301 if (ADDR(p) < 0x1000)
1302 WARN("Unlikely trace address: %p\n", p);
1303 GC_trace_ptr = p;
1304 #endif
1305 }
1306 }
1307 #ifdef GC_COLLECT_AT_MALLOC
1308 {
1309 const char *str = GETENV("GC_COLLECT_AT_MALLOC");
1310
1311 if (str != NULL) {
1312 size_t min_lb = (size_t)STRTOULL(str, NULL, 10);
1313
1314 if (min_lb > 0)
1315 GC_dbg_collect_at_malloc_min_lb = min_lb;
1316 }
1317 }
1318 #endif
1319 #if !defined(GC_DISABLE_INCREMENTAL) && !defined(NO_CLOCK)
1320 {
1321 const char *str = GETENV("GC_PAUSE_TIME_TARGET");
1322
1323 if (str != NULL) {
1324 long time_limit = atol(str);
1325
1326 if (time_limit > 0) {
1327 GC_time_limit = (unsigned long)time_limit;
1328 }
1329 }
1330 }
1331 #endif
1332 #ifndef SMALL_CONFIG
1333 {
1334 const char *str = GETENV("GC_FULL_FREQUENCY");
1335
1336 if (str != NULL) {
1337 int full_freq = atoi(str);
1338
1339 if (full_freq > 0)
1340 GC_full_freq = full_freq;
1341 }
1342 }
1343 #endif
1344 #ifndef NO_BLACK_LISTING
1345 {
1346 char const *str = GETENV("GC_LARGE_ALLOC_WARN_INTERVAL");
1347
1348 if (str != NULL) {
1349 long interval = atol(str);
1350
1351 if (interval <= 0) {
1352 WARN("GC_LARGE_ALLOC_WARN_INTERVAL environment variable has"
1353 " bad value - ignoring\n",
1354 0);
1355 } else {
1356 GC_large_alloc_warn_interval = interval;
1357 }
1358 }
1359 }
1360 #endif
1361 {
1362 const char *str = GETENV("GC_FREE_SPACE_DIVISOR");
1363
1364 if (str != NULL) {
1365 int space_divisor = atoi(str);
1366
1367 if (space_divisor > 0)
1368 GC_free_space_divisor = (unsigned)space_divisor;
1369 }
1370 }
1371 #ifdef USE_MUNMAP
1372 {
1373 const char *str = GETENV("GC_UNMAP_THRESHOLD");
1374
1375 if (str != NULL) {
1376 if (str[0] == '0' && str[1] == '\0') {
1377 /* "0" is used to disable unmapping. */
1378 GC_unmap_threshold = 0;
1379 } else {
1380 int unmap_threshold = atoi(str);
1381
1382 if (unmap_threshold > 0)
1383 GC_unmap_threshold = (unsigned)unmap_threshold;
1384 }
1385 }
1386 }
1387 {
1388 const char *str = GETENV("GC_FORCE_UNMAP_ON_GCOLLECT");
1389
1390 if (str != NULL) {
1391 if (str[0] == '0' && str[1] == '\0') {
1392 /* "0" is used to turn off the mode. */
1393 GC_force_unmap_on_gcollect = FALSE;
1394 } else {
1395 GC_force_unmap_on_gcollect = TRUE;
1396 }
1397 }
1398 }
1399 {
1400 const char *str = GETENV("GC_USE_ENTIRE_HEAP");
1401
1402 if (str != NULL) {
1403 if (str[0] == '0' && str[1] == '\0') {
1404 /* "0" is used to turn off the mode. */
1405 GC_use_entire_heap = FALSE;
1406 } else {
1407 GC_use_entire_heap = TRUE;
1408 }
1409 }
1410 }
1411 #endif
1412 #if !defined(NO_DEBUGGING) && !defined(NO_CLOCK)
1413 GET_TIME(GC_init_time);
1414 #endif
1415 maybe_install_looping_handler();
1416 #if ALIGNMENT > GC_DS_TAGS
1417 /* Adjust normal object descriptor for extra allocation. */
1418 if (EXTRA_BYTES != 0)
1419 GC_obj_kinds[NORMAL].ok_descriptor
1420 = ((~(word)ALIGNMENT) + 1) | GC_DS_LENGTH;
1421 #endif
1422 GC_exclude_static_roots_inner(beginGC_arrays, endGC_arrays);
1423 GC_exclude_static_roots_inner(beginGC_obj_kinds, endGC_obj_kinds);
1424 #ifdef SEPARATE_GLOBALS
1425 GC_exclude_static_roots_inner(beginGC_objfreelist, endGC_objfreelist);
1426 GC_exclude_static_roots_inner(beginGC_aobjfreelist, endGC_aobjfreelist);
1427 #endif
1428 #if defined(USE_PROC_FOR_LIBRARIES) && defined(LINUX) && defined(THREADS)
1429 /*
1430 * TODO: `USE_PROC_FOR_LIBRARIES` with LinuxThreads performs poorly!
1431 * If thread stacks are cached, they tend to be scanned in entirety
1432 * as part of the root set. This will grow them to maximum size, and
1433 * is generally not desirable.
1434 */
1435 #endif
1436 #if !defined(THREADS) || !(defined(SN_TARGET_PS3) || defined(SN_TARGET_PSP2))
1437 if (NULL == GC_stackbottom) {
1438 GC_stackbottom = GC_get_main_stack_base();
1439 # if (defined(LINUX) || defined(HPUX)) && defined(IA64)
1440 GC_register_stackbottom = GC_get_register_stack_base();
1441 # endif
1442 } else {
1443 # if (defined(LINUX) || defined(HPUX)) && defined(IA64)
1444 if (NULL == GC_register_stackbottom) {
1445 WARN("GC_register_stackbottom should be set with GC_stackbottom\n", 0);
1446 /*
1447 * The following may fail, since we may rely on alignment properties
1448 * that may not hold with `GC_stackbottom` value set by client.
1449 */
1450 GC_register_stackbottom = GC_get_register_stack_base();
1451 }
1452 # endif
1453 }
1454 #endif
1455 #if !defined(CPPCHECK)
1456 GC_STATIC_ASSERT(sizeof(size_t) <= sizeof(ptrdiff_t));
1457 # ifdef AO_HAVE_store
1458 /*
1459 * As of now, `hb_descr`, `mse_descr` and `hb_marks[i]` might be treated
1460 * as variables of `word` type but might be accessed atomically.
1461 */
1462 GC_STATIC_ASSERT(sizeof(AO_t) == sizeof(word));
1463 # endif
1464 GC_STATIC_ASSERT(sizeof(ptrdiff_t) == sizeof(word));
1465 GC_STATIC_ASSERT(sizeof(GC_signed_word) == sizeof(word));
1466 GC_STATIC_ASSERT(sizeof(word) * 8 == CPP_WORDSZ);
1467 GC_STATIC_ASSERT(sizeof(ptr_t) * 8 == CPP_PTRSZ);
1468 GC_STATIC_ASSERT(sizeof(ptr_t) == sizeof(GC_uintptr_t));
1469 GC_STATIC_ASSERT(sizeof(GC_oom_func) == sizeof(GC_funcptr_uint));
1470 # ifdef FUNCPTR_IS_DATAPTR
1471 GC_STATIC_ASSERT(sizeof(ptr_t) == sizeof(GC_funcptr_uint));
1472 # endif
1473 GC_STATIC_ASSERT((word)(-1) > (word)0); /*< `word` should be unsigned */
1474 /*
1475 * We no longer check for `(void *)-1 > NULL` since all pointers
1476 * are explicitly cast to `word` in every less/greater comparison.
1477 */
1478 GC_STATIC_ASSERT((GC_signed_word)(-1) < (GC_signed_word)0);
1479 #endif
1480 GC_STATIC_ASSERT(sizeof(struct hblk) == HBLKSIZE);
1481 #ifndef THREADS
1482 GC_ASSERT(!HOTTER_THAN(GC_stackbottom, GC_approx_sp()));
1483 #endif
1484 GC_init_headers();
1485 #ifdef SEARCH_FOR_DATA_START
1486 /*
1487 * For `MPROTECT_VDB`, the temporary fault handler should be installed
1488 * first, before the write fault one in `GC_dirty_init`.
1489 */
1490 if (GC_REGISTER_MAIN_STATIC_DATA())
1491 GC_init_linux_data_start();
1492 #endif
1493 #ifndef GC_DISABLE_INCREMENTAL
1494 if (GC_incremental || GETENV("GC_ENABLE_INCREMENTAL") != NULL) {
1495 set_incremental_mode_on();
1496 GC_ASSERT(0 == GC_bytes_allocd);
1497 }
1498 #endif
1499
1500 /*
1501 * Add the initial guess of root sets. Do this first, since `sbrk(0)`
1502 * might be used.
1503 */
1504 if (GC_REGISTER_MAIN_STATIC_DATA())
1505 GC_register_data_segments();
1506
1507 GC_bl_init();
1508 GC_mark_init();
1509 {
1510 const char *str = GETENV("GC_INITIAL_HEAP_SIZE");
1511
1512 if (str != NULL) {
1513 word value = GC_parse_mem_size_arg(str);
1514
1515 if (GC_WORD_MAX == value) {
1516 WARN("Bad initial heap size %s - ignoring\n", str);
1517 } else {
1518 initial_heap_sz = value;
1519 }
1520 }
1521 }
1522 {
1523 const char *str = GETENV("GC_MAXIMUM_HEAP_SIZE");
1524
1525 if (str != NULL) {
1526 word max_heap_sz = GC_parse_mem_size_arg(str);
1527
1528 if (max_heap_sz < initial_heap_sz || GC_WORD_MAX == max_heap_sz) {
1529 WARN("Bad maximum heap size %s - ignoring\n", str);
1530 } else {
1531 if (0 == GC_max_retries)
1532 GC_max_retries = 2;
1533 GC_set_max_heap_size(max_heap_sz);
1534 }
1535 }
1536 }
1537 if (initial_heap_sz != 0) {
1538 if (!GC_expand_hp_inner(divHBLKSZ(initial_heap_sz))) {
1539 GC_err_printf("Can't start up: not enough memory\n");
1540 EXIT();
1541 } else {
1542 GC_requested_heapsize += initial_heap_sz;
1543 }
1544 }
1545 if (GC_all_interior_pointers)
1546 GC_initialize_offsets();
1547 GC_register_displacement_inner(0);
1548 #ifdef REDIR_MALLOC_AND_LINUXTHREADS
1549 if (!GC_all_interior_pointers) {
1550 /* TLS ABI uses "pointer-sized" offsets for `dtv`. */
1551 GC_register_displacement_inner(sizeof(void *));
1552 }
1553 #endif
1554 GC_init_size_map();
1555 GC_is_initialized = TRUE;
1556 #ifdef THREADS
1557 # if defined(LINT2) \
1558 && !(defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED))
1559 LOCK();
1560 GC_thr_init();
1561 UNLOCK();
1562 # else
1563 GC_thr_init();
1564 # endif
1565 #endif
1566 COND_DUMP;
1567 /* Get black list set up and/or the incremental GC started. */
1568 if (!GC_dont_precollect || GC_incremental) {
1569 #if defined(DYNAMIC_LOADING) && defined(DARWIN)
1570 GC_ASSERT(0 == GC_bytes_allocd);
1571 #endif
1572 GC_gcollect_inner();
1573 }
1574 #if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED)
1575 UNLOCK();
1576 #endif
1577 #if defined(THREADS) && defined(UNIX_LIKE) && !defined(NO_GETCONTEXT)
1578 /* Ensure `getcontext_works` is set to avoid potential data race. */
1579 if (GC_dont_gc || GC_dont_precollect)
1580 GC_with_callee_saves_pushed(callee_saves_pushed_dummy_fn, NULL);
1581 #endif
1582 #ifndef DONT_USE_ATEXIT
1583 if (GC_find_leak) {
1584 /*
1585 * This is to give us at least one chance to detect leaks.
1586 * This may report some very benign leaks, but...
1587 */
1588 atexit(GC_exit_check);
1589 }
1590 #endif
1591 /*
1592 * The rest of this again assumes we do not really hold the allocator
1593 * lock.
1594 */
1595
1596 #ifdef THREADS
1597 /* Initialize thread-local allocation. */
1598 GC_init_parallel();
1599 #endif
1600
1601 #if defined(DYNAMIC_LOADING) && defined(DARWIN)
1602 /*
1603 * This must be called *without* the allocator lock held and before
1604 * any threads are created.
1605 */
1606 GC_init_dyld();
1607 #endif
1608 RESTORE_CANCEL(cancel_state);
1609 /*
1610 * It is not safe to allocate any object till completion of `GC_init`
1611 * (in particular by `GC_thr_init`), i.e. before `GC_init_dyld()` call
1612 * and initialization of the incremental mode (if any).
1613 */
1614 #if defined(GWW_VDB) && !defined(KEEP_BACK_PTRS)
1615 GC_ASSERT(GC_bytes_allocd + GC_bytes_allocd_before_gc == 0);
1616 #endif
1617 }
1618
1619 GC_API void GC_CALL
1620 GC_enable_incremental(void)
1621 {
1622 #if !defined(GC_DISABLE_INCREMENTAL) && !defined(KEEP_BACK_PTRS)
1623 /*
1624 * If we are keeping back pointers, the collector itself dirties all pages
1625 * on which objects have been marked, making the incremental collection
1626 * pointless.
1627 */
1628 if (!GC_find_leak_inner && NULL == GETENV("GC_DISABLE_INCREMENTAL")) {
1629 LOCK();
1630 if (!GC_incremental) {
1631 GC_setpagesize();
1632 /* TODO: Should we skip enabling incremental if win32s? */
1633
1634 /* Install the looping handler before write fault handler! */
1635 maybe_install_looping_handler();
1636 if (!GC_is_initialized) {
1637 /* Indicate the intention to turn it on. */
1638 GC_incremental = TRUE;
1639 UNLOCK();
1640 GC_init();
1641 LOCK();
1642 } else {
1643 set_incremental_mode_on();
1644 }
1645 /* Cannot easily do it if `GC_dont_gc`. */
1646 if (GC_incremental && !GC_dont_gc) {
1647 IF_CANCEL(int cancel_state;)
1648
1649 DISABLE_CANCEL(cancel_state);
1650 if (GC_bytes_allocd > 0) {
1651 /* There may be unmarked reachable objects. */
1652 GC_gcollect_inner();
1653 } else {
1654 /*
1655 * We are OK in assuming everything is clean since nothing can
1656 * point to an unmarked object.
1657 */
1658 # ifdef CHECKSUMS
1659 GC_read_dirty(FALSE);
1660 # else
1661 GC_read_dirty(TRUE);
1662 # endif
1663 }
1664 RESTORE_CANCEL(cancel_state);
1665 }
1666 }
1667 UNLOCK();
1668 return;
1669 }
1670 #endif
1671 GC_init();
1672 }
1673
1674 GC_API void GC_CALL
1675 GC_start_mark_threads(void)
1676 {
1677 #ifdef PARALLEL_MARK
1678 IF_CANCEL(int cancel_state;)
1679
1680 DISABLE_CANCEL(cancel_state);
1681 LOCK();
1682 GC_start_mark_threads_inner();
1683 UNLOCK();
1684 RESTORE_CANCEL(cancel_state);
1685 #else
1686 /* No action since parallel markers are disabled (or no POSIX `fork`). */
1687 GC_ASSERT(I_DONT_HOLD_LOCK());
1688 #endif
1689 }
1690
1691 GC_API void GC_CALL
1692 GC_deinit(void)
1693 {
1694 if (GC_is_initialized) {
1695 /* Prevent duplicate resource close. */
1696 GC_is_initialized = FALSE;
1697 GC_bytes_allocd = 0;
1698 GC_bytes_allocd_before_gc = 0;
1699 #if defined(GC_WIN32_THREADS) && (defined(MSWIN32) || defined(MSWINCE))
1700 # if !defined(CONSOLE_LOG) || defined(MSWINCE)
1701 DeleteCriticalSection(&GC_write_cs);
1702 # endif
1703 # if !defined(GC_PTHREADS) && !defined(USE_RWLOCK)
1704 DeleteCriticalSection(&GC_allocate_ml);
1705 # endif
1706 #endif
1707 }
1708 }
1709
1710 #if (defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE)
1711
1712 STATIC HANDLE GC_log = 0;
1713
1714 # ifdef THREADS
1715 # if defined(PARALLEL_MARK) && !defined(GC_ALWAYS_MULTITHREADED)
1716 # define IF_NEED_TO_LOCK(x) \
1717 if (GC_parallel || GC_need_to_lock) \
1718 x
1719 # else
1720 # define IF_NEED_TO_LOCK(x) \
1721 if (GC_need_to_lock) \
1722 x
1723 # endif
1724 # else
1725 # define IF_NEED_TO_LOCK(x)
1726 # endif /* !THREADS */
1727
1728 # ifdef MSWINRT_FLAVOR
1729 # include <windows.storage.h>
1730
1731 /*
1732 * This API function is defined in platform `roapi.h` file, but we cannot
1733 * include it here since it does not compile in C.
1734 */
1735 DECLSPEC_IMPORT HRESULT WINAPI
1736 RoGetActivationFactory(HSTRING activatableClassId, REFIID iid, void **factory);
1737
1738 static GC_bool
1739 getWinRTLogPath(wchar_t *buf, size_t bufLen)
1740 {
1741 static const GUID kIID_IApplicationDataStatics
1742 = { 0x5612147B, 0xE843, 0x45E3, 0x94, 0xD8, 0x06,
1743 0x16, 0x9E, 0x3C, 0x8E, 0x17 };
1744 static const GUID kIID_IStorageItem
1745 = { 0x4207A996, 0xCA2F, 0x42F7, 0xBD, 0xE8, 0x8B,
1746 0x10, 0x45, 0x7A, 0x7F, 0x30 };
1747 GC_bool result = FALSE;
1748 HSTRING_HEADER appDataClassNameHeader;
1749 HSTRING appDataClassName;
1750 __x_ABI_CWindows_CStorage_CIApplicationDataStatics *appDataStatics = 0;
1751
1752 GC_ASSERT(bufLen > 0);
1753 if (SUCCEEDED(WindowsCreateStringReference(
1754 RuntimeClass_Windows_Storage_ApplicationData,
1755 (sizeof(RuntimeClass_Windows_Storage_ApplicationData) - 1)
1756 / sizeof(wchar_t),
1757 &appDataClassNameHeader, &appDataClassName))
1758 && SUCCEEDED(RoGetActivationFactory(
1759 appDataClassName, &kIID_IApplicationDataStatics, &appDataStatics))) {
1760 __x_ABI_CWindows_CStorage_CIApplicationData *appData = NULL;
1761 __x_ABI_CWindows_CStorage_CIStorageFolder *tempFolder = NULL;
1762 __x_ABI_CWindows_CStorage_CIStorageItem *tempFolderItem = NULL;
1763 HSTRING tempPath = NULL;
1764
1765 if (SUCCEEDED(
1766 appDataStatics->lpVtbl->get_Current(appDataStatics, &appData))
1767 && SUCCEEDED(
1768 appData->lpVtbl->get_TemporaryFolder(appData, &tempFolder))
1769 && SUCCEEDED(tempFolder->lpVtbl->QueryInterface(
1770 tempFolder, &kIID_IStorageItem, &tempFolderItem))
1771 && SUCCEEDED(
1772 tempFolderItem->lpVtbl->get_Path(tempFolderItem, &tempPath))) {
1773 UINT32 tempPathLen;
1774 const wchar_t *tempPathBuf
1775 = WindowsGetStringRawBuffer(tempPath, &tempPathLen);
1776
1777 buf[0] = '\0';
1778 if (wcsncat_s(buf, bufLen, tempPathBuf, tempPathLen) == 0
1779 && wcscat_s(buf, bufLen, L"\\") == 0
1780 && wcscat_s(buf, bufLen, TEXT(GC_LOG_STD_NAME)) == 0)
1781 result = TRUE;
1782 WindowsDeleteString(tempPath);
1783 }
1784
1785 if (tempFolderItem != NULL)
1786 tempFolderItem->lpVtbl->Release(tempFolderItem);
1787 if (tempFolder != NULL)
1788 tempFolder->lpVtbl->Release(tempFolder);
1789 if (appData != NULL)
1790 appData->lpVtbl->Release(appData);
1791 appDataStatics->lpVtbl->Release(appDataStatics);
1792 }
1793 return result;
1794 }
1795 # endif /* MSWINRT_FLAVOR */
1796
1797 STATIC HANDLE
1798 GC_CreateLogFile(void)
1799 {
1800 HANDLE hFile;
1801 # ifdef MSWINRT_FLAVOR
1802 TCHAR pathBuf[_MAX_PATH + 0x10]; /*< buffer for file path plus extension */
1803
1804 hFile = INVALID_HANDLE_VALUE;
1805 if (getWinRTLogPath(pathBuf, _MAX_PATH + 1)) {
1806 CREATEFILE2_EXTENDED_PARAMETERS extParams;
1807
1808 BZERO(&extParams, sizeof(extParams));
1809 extParams.dwSize = sizeof(extParams);
1810 extParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
1811 extParams.dwFileFlags
1812 = GC_print_stats == VERBOSE ? 0 : FILE_FLAG_WRITE_THROUGH;
1813 hFile = CreateFile2(pathBuf, GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS,
1814 &extParams);
1815 }
1816
1817 # else
1818 TCHAR *logPath;
1819 # if defined(NO_GETENV_WIN32) && defined(CPPCHECK)
1820 # define appendToFile FALSE
1821 # else
1822 BOOL appendToFile = FALSE;
1823 # endif
1824 # if !defined(NO_GETENV_WIN32) || !defined(OLD_WIN32_LOG_FILE)
1825 TCHAR pathBuf[_MAX_PATH + 0x10]; /*< buffer for file path plus extension */
1826
1827 logPath = pathBuf;
1828 # endif
1829
1830 /* Use `GetEnvironmentVariable` instead of `GETENV` for Unicode support. */
1831 # ifndef NO_GETENV_WIN32
1832 if (GetEnvironmentVariable(TEXT("GC_LOG_FILE"), pathBuf, _MAX_PATH + 1) - 1U
1833 < (DWORD)_MAX_PATH) {
1834 appendToFile = TRUE;
1835 } else
1836 # endif
1837 /* else */ {
1838 /* Environment var not found or its value too long. */
1839 # ifdef OLD_WIN32_LOG_FILE
1840 logPath = TEXT(GC_LOG_STD_NAME);
1841 # else
1842 int len
1843 = (int)GetModuleFileName(NULL /* `hModule` */, pathBuf, _MAX_PATH + 1);
1844 /* If `GetModuleFileName()` has failed, then len is 0. */
1845 if (len > 4 && pathBuf[len - 4] == (TCHAR)'.') {
1846 /* Strip the executable file extension. */
1847 len -= 4;
1848 }
1849 BCOPY(TEXT(".") TEXT(GC_LOG_STD_NAME), &pathBuf[len],
1850 sizeof(TEXT(".") TEXT(GC_LOG_STD_NAME)));
1851 # endif
1852 }
1853
1854 hFile = CreateFile(logPath, GENERIC_WRITE, FILE_SHARE_READ,
1855 NULL /* `lpSecurityAttributes` */,
1856 appendToFile ? OPEN_ALWAYS : CREATE_ALWAYS,
1857 GC_print_stats == VERBOSE
1858 ? FILE_ATTRIBUTE_NORMAL
1859 :
1860 /* immediately flush writes unless very verbose */
1861 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
1862 NULL /* `hTemplateFile` */);
1863
1864 # ifndef NO_GETENV_WIN32
1865 if (appendToFile && hFile != INVALID_HANDLE_VALUE) {
1866 LONG posHigh = 0;
1867 /* Seek to the file end (ignoring any error). */
1868 (void)SetFilePointer(hFile, 0, &posHigh, FILE_END);
1869 }
1870 # endif
1871 # undef appendToFile
1872 # endif
1873 return hFile;
1874 }
1875
1876 STATIC int
1877 GC_write(const char *buf, size_t len)
1878 {
1879 BOOL res;
1880 DWORD written;
1881 # if defined(THREADS) && defined(GC_ASSERTIONS)
1882 /* This is to prevent infinite recursion at abort. */
1883 static GC_bool inside_write = FALSE;
1884 if (inside_write)
1885 return -1;
1886 # endif
1887
1888 if (0 == len)
1889 return 0;
1890 IF_NEED_TO_LOCK(EnterCriticalSection(&GC_write_cs));
1891 # if defined(THREADS) && defined(GC_ASSERTIONS)
1892 if (GC_write_disabled) {
1893 inside_write = TRUE;
1894 ABORT("Assertion failure: GC_write called with write_disabled");
1895 }
1896 # endif
1897 if (0 == GC_log) {
1898 GC_log = GC_CreateLogFile();
1899 }
1900 if (GC_log == INVALID_HANDLE_VALUE) {
1901 IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs));
1902 # ifdef NO_DEBUGGING
1903 /*
1904 * Ignore open log failure (e.g., it might be caused by read-only folder
1905 * of the client application).
1906 */
1907 return 0;
1908 # else
1909 return -1;
1910 # endif
1911 }
1912 res = WriteFile(GC_log, buf, (DWORD)len, &written, NULL);
1913 # if defined(_MSC_VER) && defined(_DEBUG) && !defined(NO_CRT) \
1914 && !defined(NO_CRTDBGREPORT)
1915 # ifdef MSWINCE
1916 /* There is no `CrtDbgReport()` in WinCE. */
1917 {
1918 WCHAR wbuf[1024];
1919
1920 /* Always use Unicode variant of `OutputDebugString()`. */
1921 wbuf[MultiByteToWideChar(CP_ACP, 0 /* `dwFlags` */, buf, len, wbuf,
1922 sizeof(wbuf) / sizeof(wbuf[0]) - 1)]
1923 = 0;
1924 OutputDebugStringW(wbuf);
1925 }
1926 # else
1927 _CrtDbgReport(_CRT_WARN, NULL, 0, NULL, "%.*s", len, buf);
1928 # endif
1929 # endif
1930 IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs));
1931 return res ? (int)written : -1;
1932 }
1933
1934 /* TODO: This is pretty ugly... */
1935 # define WRITE(f, buf, len) GC_write(buf, len)
1936
1937 #elif defined(OS2)
1938 STATIC FILE *GC_stdout = NULL;
1939 STATIC FILE *GC_stderr = NULL;
1940 STATIC FILE *GC_log = NULL;
1941
1942 /* Initialize `GC_log` (and the friends) passed to `GC_write()`. */
1943 STATIC void
1944 GC_set_files(void)
1945 {
1946 if (GC_stdout == NULL) {
1947 GC_stdout = stdout;
1948 }
1949 if (GC_stderr == NULL) {
1950 GC_stderr = stderr;
1951 }
1952 if (GC_log == NULL) {
1953 GC_log = stderr;
1954 }
1955 }
1956
1957 GC_INLINE int
1958 GC_write(FILE *f, const char *buf, size_t len)
1959 {
1960 int res = fwrite(buf, 1, len, f);
1961 fflush(f);
1962 return res;
1963 }
1964
1965 # define WRITE(f, buf, len) (GC_set_files(), GC_write(f, buf, len))
1966
1967 #elif defined(GC_ANDROID_LOG)
1968
1969 # include <android/log.h>
1970
1971 # ifndef GC_ANDROID_LOG_TAG
1972 # define GC_ANDROID_LOG_TAG "BDWGC"
1973 # endif
1974
1975 # define GC_stdout ANDROID_LOG_DEBUG
1976 # define GC_stderr ANDROID_LOG_ERROR
1977 # define GC_log GC_stdout
1978
1979 # define WRITE(level, buf, unused_len) \
1980 __android_log_write(level, GC_ANDROID_LOG_TAG, buf)
1981
1982 #elif defined(NN_PLATFORM_CTR)
1983 int n3ds_log_write(const char *text, int length);
1984 # define WRITE(level, buf, len) n3ds_log_write(buf, len)
1985
1986 #elif defined(NINTENDO_SWITCH)
1987 int switch_log_write(const char *text, int length);
1988 # define WRITE(level, buf, len) switch_log_write(buf, len)
1989
1990 #else
1991
1992 # if !defined(ECOS) && !defined(NOSYS) && !defined(PLATFORM_WRITE) \
1993 && !defined(SN_TARGET_PSP2)
1994 # include <errno.h>
1995 # endif
1996
1997 STATIC int
1998 GC_write(int fd, const char *buf, size_t len)
1999 {
2000 # if defined(ECOS) || defined(PLATFORM_WRITE) || defined(SN_TARGET_PSP2) \
2001 || defined(NOSYS)
2002 UNUSED_ARG(fd);
2003 # ifdef ECOS
2004 /* FIXME: This seems to be defined nowhere at present. */
2005 /* `_Jv_diag_write(buf, len);` */
2006 # else
2007 /* No writing. */
2008 # endif
2009 UNUSED_ARG(buf);
2010 return (int)len;
2011 # else
2012 size_t bytes_written = 0;
2013 IF_CANCEL(int cancel_state;)
2014
2015 DISABLE_CANCEL(cancel_state);
2016 while (bytes_written < len) {
2017 int result;
2018
2019 # if defined(SOLARIS) && defined(THREADS)
2020 result = syscall(SYS_write, fd, buf + bytes_written, len - bytes_written);
2021 # elif defined(_MSC_VER)
2022 result = _write(fd, buf + bytes_written, (unsigned)(len - bytes_written));
2023 # else
2024 result = (int)write(fd, buf + bytes_written, len - bytes_written);
2025 # endif
2026 if (result < 0) {
2027 if (EAGAIN == errno) {
2028 /* Resource is temporarily unavailable. */
2029 continue;
2030 }
2031 RESTORE_CANCEL(cancel_state);
2032 return -1;
2033 }
2034 # ifdef LINT2
2035 if ((unsigned)result > len - bytes_written)
2036 ABORT("write() result cannot be bigger than requested length");
2037 # endif
2038 bytes_written += (unsigned)result;
2039 }
2040 RESTORE_CANCEL(cancel_state);
2041 return (int)bytes_written;
2042 # endif
2043 }
2044
2045 # define WRITE(f, buf, len) GC_write(f, buf, len)
2046 #endif /* !MSWINCE && !OS2 && !GC_ANDROID_LOG */
2047
2048 #ifndef GC_DISABLE_SNPRINTF
2049 # define BUFSZ 1024
2050
2051 # if defined(DJGPP) || defined(__STRICT_ANSI__)
2052 /* `vsnprintf` is missing in DJGPP (v2.0.3). */
2053 # define GC_VSNPRINTF(buf, bufsz, format, args) vsprintf(buf, format, args)
2054 # elif defined(_MSC_VER)
2055 # ifdef MSWINCE
2056 /* `_vsnprintf` is deprecated in WinCE. */
2057 # define GC_VSNPRINTF StringCchVPrintfA
2058 # else
2059 # define GC_VSNPRINTF _vsnprintf
2060 # endif
2061 # else
2062 # define GC_VSNPRINTF vsnprintf
2063 # endif
2064
2065 /*
2066 * A variant of `printf` that is unlikely to call `malloc`, and is thus
2067 * safer to call from the collector in case `malloc` has been bound to
2068 * `GC_malloc`. Floating-point arguments and formats should be avoided,
2069 * since the conversion is more likely to allocate memory.
2070 * Assumes that no more than `BUFSZ - 1` characters are written at once.
2071 */
2072 # define GC_PRINTF_FILLBUF(buf, format) \
2073 do { \
2074 va_list args; \
2075 va_start(args, format); \
2076 (buf)[sizeof(buf) - 1] = 0x15; /*< guard */ \
2077 (void)GC_VSNPRINTF(buf, sizeof(buf) - 1, format, args); \
2078 va_end(args); \
2079 if ((buf)[sizeof(buf) - 1] != 0x15) \
2080 ABORT("GC_printf clobbered stack"); \
2081 } while (0)
2082
2083 # define DECL_BUF_AND_PRINTF_TO(buf, format) \
2084 char buf[BUFSZ + 1]; \
2085 GC_PRINTF_FILLBUF(buf, format)
2086 #else
2087 /*
2088 * At most, when `vsnprintf()` is unavailable, we could only print the
2089 * format string as is, not handling the format specifiers (if any), thus
2090 * skipping the rest of the `printf` arguments.
2091 */
2092 # define DECL_BUF_AND_PRINTF_TO(buf, format) const char *buf = (format)
2093 #endif /* GC_DISABLE_SNPRINTF */
2094
2095 void
2096 GC_printf(const char *format, ...)
2097 {
2098 if (!GC_quiet) {
2099 DECL_BUF_AND_PRINTF_TO(buf, format);
2100 #ifdef NACL
2101 (void)WRITE(GC_stdout, buf, strlen(buf));
2102 /* Ignore errors silently. */
2103 #else
2104 if (WRITE(GC_stdout, buf, strlen(buf)) < 0
2105 # if defined(CYGWIN32) || (defined(CONSOLE_LOG) && defined(MSWIN32))
2106 && GC_stdout != GC_DEFAULT_STDOUT_FD
2107 # endif
2108 ) {
2109 ABORT("write to stdout failed");
2110 }
2111 #endif
2112 }
2113 }
2114
2115 void
2116 GC_err_printf(const char *format, ...)
2117 {
2118 DECL_BUF_AND_PRINTF_TO(buf, format);
2119 GC_err_puts(buf);
2120 }
2121
2122 void
2123 GC_log_printf(const char *format, ...)
2124 {
2125 DECL_BUF_AND_PRINTF_TO(buf, format);
2126 #ifdef NACL
2127 (void)WRITE(GC_log, buf, strlen(buf));
2128 #else
2129 if (WRITE(GC_log, buf, strlen(buf)) < 0
2130 # if defined(CYGWIN32) || (defined(CONSOLE_LOG) && defined(MSWIN32))
2131 && GC_log != GC_DEFAULT_STDERR_FD
2132 # endif
2133 ) {
2134 ABORT("write to GC log failed");
2135 }
2136 #endif
2137 }
2138
2139 #ifndef GC_ANDROID_LOG
2140
2141 # define GC_warn_printf GC_err_printf
2142
2143 #else
2144
2145 GC_INNER void
2146 GC_info_log_printf(const char *format, ...)
2147 {
2148 DECL_BUF_AND_PRINTF_TO(buf, format);
2149 (void)WRITE(ANDROID_LOG_INFO, buf, 0 /* unused */);
2150 }
2151
2152 GC_INNER void
2153 GC_verbose_log_printf(const char *format, ...)
2154 {
2155 DECL_BUF_AND_PRINTF_TO(buf, format);
2156 /* Note: write errors are ignored. */
2157 (void)WRITE(ANDROID_LOG_VERBOSE, buf, 0);
2158 }
2159
2160 STATIC void
2161 GC_warn_printf(const char *format, ...)
2162 {
2163 DECL_BUF_AND_PRINTF_TO(buf, format);
2164 (void)WRITE(ANDROID_LOG_WARN, buf, 0);
2165 }
2166
2167 #endif /* GC_ANDROID_LOG */
2168
2169 void
2170 GC_err_puts(const char *s)
2171 {
2172 /* Note: write errors are ignored. */
2173 (void)WRITE(GC_stderr, s, strlen(s));
2174 }
2175
2176 STATIC void GC_CALLBACK
2177 GC_default_warn_proc(const char *msg, GC_uintptr_t arg)
2178 {
2179 /* TODO: Add assertion on argument to comply with `msg` (format). */
2180 GC_warn_printf(msg, arg);
2181 }
2182
2183 GC_INNER GC_warn_proc GC_current_warn_proc = GC_default_warn_proc;
2184
2185 GC_API void GC_CALLBACK
2186 GC_ignore_warn_proc(const char *msg, GC_uintptr_t arg)
2187 {
2188 if (GC_print_stats) {
2189 /* Do not ignore warnings if stats printing is on. */
2190 GC_default_warn_proc(msg, arg);
2191 }
2192 }
2193
2194 GC_API void GC_CALL
2195 GC_set_warn_proc(GC_warn_proc p)
2196 {
2197 GC_ASSERT(NONNULL_ARG_NOT_NULL(p));
2198 LOCK();
2199 GC_current_warn_proc = p;
2200 UNLOCK();
2201 }
2202
2203 GC_API GC_warn_proc GC_CALL
2204 GC_get_warn_proc(void)
2205 {
2206 GC_warn_proc result;
2207
2208 READER_LOCK();
2209 result = GC_current_warn_proc;
2210 READER_UNLOCK();
2211 return result;
2212 }
2213
2214 /*
2215 * Print (or display) a message before abnormal exit (including abort).
2216 * Invoked from `ABORT(msg)` macro (where `msg` is non-`NULL`) and from
2217 * `EXIT()` macro (`msg` is `NULL` in that case).
2218 */
2219 STATIC void GC_CALLBACK
2220 GC_default_on_abort(const char *msg)
2221 {
2222 #if !defined(SMALL_CONFIG)
2223 # ifndef DONT_USE_ATEXIT
2224 /* Disable at-exit garbage collection. */
2225 skip_gc_atexit = TRUE;
2226 # endif
2227
2228 if (msg != NULL) {
2229 # ifdef MSGBOX_ON_ERROR
2230 GC_win32_MessageBoxA(msg, "Fatal error in GC", MB_ICONERROR | MB_OK);
2231 /* Also duplicate `msg` to the collector log file. */
2232 # endif
2233
2234 # ifndef GC_ANDROID_LOG
2235 /*
2236 * Avoid calling `GC_err_printf()` here, as `GC_on_abort()` could
2237 * be called from it. Note 1: this is not an atomic output.
2238 * Note 2: possible write errors are ignored.
2239 */
2240 # if defined(GC_WIN32_THREADS) && defined(GC_ASSERTIONS) \
2241 && ((defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE))
2242 if (!GC_write_disabled)
2243 # endif
2244 {
2245 if (WRITE(GC_stderr, msg, strlen(msg)) >= 0)
2246 (void)WRITE(GC_stderr, "\n", 1);
2247 }
2248 # else
2249 __android_log_assert("*" /* `cond` */, GC_ANDROID_LOG_TAG, "%s\n", msg);
2250 # endif
2251 # if defined(HAIKU) && !defined(DONT_CALL_DEBUGGER)
2252 /*
2253 * This will cause the crash reason to appear in any debug reports
2254 * generated (by the default system application crash dialog).
2255 */
2256 debugger(msg);
2257 # endif
2258 }
2259
2260 # if !defined(NO_DEBUGGING) && !defined(GC_ANDROID_LOG)
2261 if (GETENV("GC_LOOP_ON_ABORT") != NULL) {
2262 /*
2263 * In many cases it is easier to debug a running process.
2264 * It is arguably nicer to sleep, but that makes it harder to look
2265 * at the thread if the debugger does not know much about threads.
2266 */
2267 for (;;) {
2268 /* Empty. */
2269 }
2270 }
2271 # endif
2272 #else
2273 UNUSED_ARG(msg);
2274 #endif
2275 }
2276
2277 #ifndef SMALL_CONFIG
2278 GC_abort_func GC_on_abort = GC_default_on_abort;
2279 #endif
2280
2281 GC_API void GC_CALL
2282 GC_set_abort_func(GC_abort_func fn)
2283 {
2284 GC_ASSERT(NONNULL_ARG_NOT_NULL(fn));
2285 LOCK();
2286 #ifndef SMALL_CONFIG
2287 GC_on_abort = fn;
2288 #else
2289 UNUSED_ARG(fn);
2290 #endif
2291 UNLOCK();
2292 }
2293
2294 GC_API GC_abort_func GC_CALL
2295 GC_get_abort_func(void)
2296 {
2297 GC_abort_func fn;
2298
2299 READER_LOCK();
2300 #ifndef SMALL_CONFIG
2301 fn = GC_on_abort;
2302 GC_ASSERT(fn != 0);
2303 #else
2304 fn = GC_default_on_abort;
2305 #endif
2306 READER_UNLOCK();
2307 return fn;
2308 }
2309
2310 #if defined(NEED_SNPRINTF_SLDS) /* && GC_DISABLE_SNPRINTF */
2311 GC_INNER void
2312 GC_snprintf_s_ld_s(char *buf, size_t buf_sz, const char *prefix, long lv,
2313 const char *suffix)
2314 {
2315 size_t len = strlen(prefix);
2316
2317 GC_ASSERT(buf_sz > 0);
2318 /* Copy the prefix. */
2319 if (UNLIKELY(len >= buf_sz))
2320 len = buf_sz - 1;
2321 BCOPY(prefix, buf, len);
2322 buf += len;
2323 buf_sz -= len;
2324
2325 /* Handle sign of the number. */
2326 if (lv >= 0) {
2327 lv = -lv;
2328 } else if (LIKELY(buf_sz > 1)) {
2329 *(buf++) = '-';
2330 buf_sz--;
2331 }
2332
2333 /* Convert the decimal number to string. (A trivial implementation.) */
2334 {
2335 char num_buf[20];
2336 size_t pos = sizeof(num_buf);
2337
2338 do {
2339 long r = lv / 10;
2340
2341 if (UNLIKELY(0 == pos))
2342 break; /*< overflow */
2343 num_buf[--pos] = (char)(r * 10 - lv + '0');
2344 lv = r;
2345 } while (lv < 0);
2346 len = sizeof(num_buf) - pos;
2347 if (UNLIKELY(len >= buf_sz))
2348 len = buf_sz - 1;
2349 BCOPY(&num_buf[pos], buf, len);
2350 }
2351 buf += len;
2352 buf_sz -= len;
2353
2354 /* Copy the suffix (if any). */
2355 len = strlen(suffix);
2356 if (len > 0) {
2357 if (UNLIKELY(len >= buf_sz))
2358 len = buf_sz - 1;
2359 BCOPY(suffix, buf, len);
2360 buf += len;
2361 }
2362 *buf = '\0';
2363 }
2364 #endif /* NEED_SNPRINTF_SLDS */
2365
2366 GC_API void GC_CALL
2367 GC_enable(void)
2368 {
2369 LOCK();
2370 /* Ensure no counter underflow. */
2371 GC_ASSERT(GC_dont_gc != 0);
2372 GC_dont_gc--;
2373 if (!GC_dont_gc && GC_heapsize > GC_heapsize_on_gc_disable)
2374 WARN("Heap grown by %" WARN_PRIuPTR " KiB while GC was disabled\n",
2375 (GC_heapsize - GC_heapsize_on_gc_disable) >> 10);
2376 UNLOCK();
2377 }
2378
2379 GC_API void GC_CALL
2380 GC_disable(void)
2381 {
2382 LOCK();
2383 if (!GC_dont_gc)
2384 GC_heapsize_on_gc_disable = GC_heapsize;
2385 GC_dont_gc++;
2386 UNLOCK();
2387 }
2388
2389 GC_API int GC_CALL
2390 GC_is_disabled(void)
2391 {
2392 return GC_dont_gc != 0;
2393 }
2394
2395 /* Helper procedures for new kind creation. */
2396
2397 GC_API void **GC_CALL
2398 GC_new_free_list_inner(void)
2399 {
2400 void *result;
2401
2402 GC_ASSERT(I_HOLD_LOCK());
2403 result = GC_INTERNAL_MALLOC((MAXOBJGRANULES + 1) * sizeof(ptr_t), PTRFREE);
2404 if (NULL == result)
2405 ABORT("Failed to allocate free list for new kind");
2406 BZERO(result, (MAXOBJGRANULES + 1) * sizeof(ptr_t));
2407 return (void **)result;
2408 }
2409
2410 GC_API void **GC_CALL
2411 GC_new_free_list(void)
2412 {
2413 void **result;
2414
2415 LOCK();
2416 result = GC_new_free_list_inner();
2417 UNLOCK();
2418 return result;
2419 }
2420
2421 GC_API unsigned GC_CALL
2422 GC_new_kind_inner(void **fl, GC_word descr, int adjust, int clear)
2423 {
2424 unsigned result = GC_n_kinds;
2425
2426 GC_ASSERT(NONNULL_ARG_NOT_NULL(fl));
2427 GC_ASSERT(!adjust || 1 == adjust);
2428 /*
2429 * If an object is not needed to be cleared (when moved to the free list),
2430 * then its descriptor should be zero to denote a pointer-free object
2431 * (and, as a consequence, the size of the object should not be added to
2432 * the descriptor template).
2433 */
2434 GC_ASSERT(1 == clear || (0 == descr && !adjust && !clear));
2435 if (result < MAXOBJKINDS) {
2436 GC_ASSERT(result > 0);
2437 GC_n_kinds++;
2438 GC_obj_kinds[result].ok_freelist = fl;
2439 GC_obj_kinds[result].ok_reclaim_list = 0;
2440 GC_obj_kinds[result].ok_descriptor = descr;
2441 GC_obj_kinds[result].ok_relocate_descr = (GC_bool)adjust;
2442 GC_obj_kinds[result].ok_init = (GC_bool)clear;
2443 #ifdef ENABLE_DISCLAIM
2444 GC_obj_kinds[result].ok_mark_unconditionally = FALSE;
2445 GC_obj_kinds[result].ok_disclaim_proc = 0;
2446 #endif
2447 } else {
2448 ABORT("Too many kinds");
2449 }
2450 return result;
2451 }
2452
2453 GC_API unsigned GC_CALL
2454 GC_new_kind(void **fl, GC_word descr, int adjust, int clear)
2455 {
2456 unsigned result;
2457
2458 LOCK();
2459 result = GC_new_kind_inner(fl, descr, adjust, clear);
2460 UNLOCK();
2461 return result;
2462 }
2463
2464 GC_API unsigned GC_CALL
2465 GC_new_proc_inner(GC_mark_proc proc)
2466 {
2467 unsigned result = GC_n_mark_procs;
2468
2469 if (result < GC_MAX_MARK_PROCS) {
2470 GC_n_mark_procs++;
2471 GC_mark_procs[result] = proc;
2472 } else {
2473 ABORT("Too many mark procedures");
2474 }
2475 return result;
2476 }
2477
2478 GC_API unsigned GC_CALL
2479 GC_new_proc(GC_mark_proc proc)
2480 {
2481 unsigned result;
2482
2483 LOCK();
2484 result = GC_new_proc_inner(proc);
2485 UNLOCK();
2486 return result;
2487 }
2488
2489 GC_API void *GC_CALL
2490 GC_call_with_alloc_lock(GC_fn_type fn, void *client_data)
2491 {
2492 void *result;
2493
2494 LOCK();
2495 result = fn(client_data);
2496 UNLOCK();
2497 return result;
2498 }
2499
2500 #ifdef THREADS
2501 GC_API void GC_CALL
2502 GC_alloc_lock(void)
2503 {
2504 LOCK();
2505 }
2506
2507 GC_API void GC_CALL
2508 GC_alloc_unlock(void)
2509 {
2510 UNLOCK();
2511 }
2512
2513 GC_API void *GC_CALL
2514 GC_call_with_reader_lock(GC_fn_type fn, void *client_data, int release)
2515 {
2516 void *result;
2517
2518 READER_LOCK();
2519 result = fn(client_data);
2520 # ifdef HAS_REAL_READER_LOCK
2521 if (release) {
2522 READER_UNLOCK_RELEASE();
2523 # ifdef LINT2
2524 GC_noop1((unsigned)release);
2525 # endif
2526 return result;
2527 }
2528 # else
2529 UNUSED_ARG(release);
2530 # endif
2531 READER_UNLOCK();
2532 return result;
2533 }
2534 #endif /* THREADS */
2535
2536 GC_ATTR_NOINLINE
2537 GC_API void *GC_CALL
2538 GC_call_with_stack_base(GC_stack_base_func fn, void *arg)
2539 {
2540 struct GC_stack_base base;
2541 void *result;
2542
2543 STORE_APPROX_SP_TO(*(volatile ptr_t *)&base.mem_base);
2544 #ifdef IA64
2545 base.reg_base = GC_save_regs_in_stack();
2546 /*
2547 * TODO: Unnecessarily flushes register stack, but that probably
2548 * does not hurt.
2549 */
2550 #elif defined(E2K)
2551 {
2552 unsigned long long sz_ull;
2553
2554 GET_PROCEDURE_STACK_SIZE_INNER(&sz_ull);
2555 base.reg_base = NUMERIC_TO_VPTR(sz_ull);
2556 }
2557 #endif
2558 result = (*(GC_stack_base_func volatile *)&fn)(&base, arg);
2559 /*
2560 * Strongly discourage the compiler from treating the above as
2561 * a tail call.
2562 */
2563 GC_noop1(COVERT_DATAFLOW(ADDR(&base)));
2564 return result;
2565 }
2566
2567 #ifndef THREADS
2568
2569 GC_INNER ptr_t GC_blocked_sp = NULL;
2570
2571 # ifdef IA64
2572 STATIC ptr_t GC_blocked_register_sp = NULL;
2573 # endif
2574
2575 GC_INNER struct GC_traced_stack_sect_s *GC_traced_stack_sect = NULL;
2576
2577 /* This is nearly the same as in `pthread_support.c` file. */
2578 GC_ATTR_NOINLINE
2579 GC_API void *GC_CALL
2580 GC_call_with_gc_active(GC_fn_type fn, void *client_data)
2581 {
2582 struct GC_traced_stack_sect_s stacksect;
2583 GC_ASSERT(GC_is_initialized);
2584
2585 /*
2586 * Adjust our stack bottom pointer (this could happen if
2587 * `GC_get_main_stack_base()` is unimplemented or broken for
2588 * the platform). Note: `stacksect` variable is reused here.
2589 */
2590 STORE_APPROX_SP_TO(*(volatile ptr_t *)&stacksect.saved_stack_ptr);
2591 if (HOTTER_THAN(GC_stackbottom, stacksect.saved_stack_ptr))
2592 GC_stackbottom = stacksect.saved_stack_ptr;
2593
2594 if (GC_blocked_sp == NULL) {
2595 /* We are not inside `GC_do_blocking()` - do nothing more. */
2596 client_data = (*(GC_fn_type volatile *)&fn)(client_data);
2597 /* Prevent treating the above as a tail call. */
2598 GC_noop1(COVERT_DATAFLOW(ADDR(&stacksect)));
2599 return client_data; /*< result */
2600 }
2601
2602 /* Setup new "stack section". */
2603 stacksect.saved_stack_ptr = GC_blocked_sp;
2604 # ifdef IA64
2605 /* This is the same as in `GC_call_with_stack_base()`. */
2606 stacksect.backing_store_end = GC_save_regs_in_stack();
2607 /* Unnecessarily flushes register stack, but that probably does not hurt. */
2608 stacksect.saved_backing_store_ptr = GC_blocked_register_sp;
2609 # endif
2610 stacksect.prev = GC_traced_stack_sect;
2611 GC_blocked_sp = NULL;
2612 GC_traced_stack_sect = &stacksect;
2613
2614 client_data = (*(GC_fn_type volatile *)&fn)(client_data);
2615 GC_ASSERT(GC_blocked_sp == NULL);
2616 GC_ASSERT(GC_traced_stack_sect == &stacksect);
2617
2618 # if defined(CPPCHECK)
2619 GC_noop1_ptr(GC_traced_stack_sect);
2620 GC_noop1_ptr(GC_blocked_sp);
2621 # endif
2622 /* Restore original "stack section". */
2623 GC_traced_stack_sect = stacksect.prev;
2624 # ifdef IA64
2625 GC_blocked_register_sp = stacksect.saved_backing_store_ptr;
2626 # endif
2627 GC_blocked_sp = stacksect.saved_stack_ptr;
2628
2629 return client_data; /*< result */
2630 }
2631
2632 /* This is nearly the same as in `pthread_support.c` file. */
2633 STATIC void
2634 GC_do_blocking_inner(ptr_t data, void *context)
2635 {
2636 UNUSED_ARG(context);
2637 GC_ASSERT(GC_is_initialized);
2638 GC_ASSERT(GC_blocked_sp == NULL);
2639 # ifdef SPARC
2640 GC_blocked_sp = GC_save_regs_in_stack();
2641 # else
2642 GC_blocked_sp = GC_approx_sp();
2643 # ifdef IA64
2644 GC_blocked_register_sp = GC_save_regs_in_stack();
2645 # endif
2646 # endif
2647
2648 ((struct blocking_data *)data)->client_data /*< result */
2649 = ((struct blocking_data *)data)
2650 ->fn(((struct blocking_data *)data)->client_data);
2651
2652 GC_ASSERT(GC_blocked_sp != NULL);
2653 # if defined(CPPCHECK)
2654 GC_noop1_ptr(GC_blocked_sp);
2655 # endif
2656 GC_blocked_sp = NULL;
2657 }
2658
2659 GC_API void GC_CALL
2660 GC_set_stackbottom(void *gc_thread_handle, const struct GC_stack_base *sb)
2661 {
2662 GC_ASSERT(sb->mem_base != NULL);
2663 GC_ASSERT(NULL == gc_thread_handle || &GC_stackbottom == gc_thread_handle);
2664 GC_ASSERT(NULL == GC_blocked_sp
2665 && NULL == GC_traced_stack_sect); /*< for now */
2666 UNUSED_ARG(gc_thread_handle);
2667
2668 GC_stackbottom = (char *)sb->mem_base;
2669 # ifdef IA64
2670 GC_register_stackbottom = (ptr_t)sb->reg_base;
2671 # endif
2672 }
2673
2674 GC_API void *GC_CALL
2675 GC_get_my_stackbottom(struct GC_stack_base *sb)
2676 {
2677 GC_ASSERT(GC_is_initialized);
2678 sb->mem_base = GC_stackbottom;
2679 # ifdef IA64
2680 sb->reg_base = GC_register_stackbottom;
2681 # elif defined(E2K)
2682 sb->reg_base = NULL;
2683 # endif
2684 return &GC_stackbottom; /*< `gc_thread_handle` */
2685 }
2686
2687 #endif /* !THREADS */
2688
2689 GC_API void *GC_CALL
2690 GC_do_blocking(GC_fn_type fn, void *client_data)
2691 {
2692 struct blocking_data my_data;
2693
2694 my_data.fn = fn;
2695 my_data.client_data = client_data;
2696 GC_with_callee_saves_pushed(GC_do_blocking_inner, (ptr_t)(&my_data));
2697 return my_data.client_data; /*< result */
2698 }
2699
2700 #if !defined(NO_DEBUGGING)
2701 GC_API void GC_CALL
2702 GC_dump(void)
2703 {
2704 READER_LOCK();
2705 GC_dump_named(NULL);
2706 READER_UNLOCK();
2707 }
2708
2709 GC_API void GC_CALL
2710 GC_dump_named(const char *name)
2711 {
2712 # ifndef NO_CLOCK
2713 CLOCK_TYPE current_time;
2714
2715 GET_TIME(current_time);
2716 # endif
2717 if (name != NULL) {
2718 GC_printf("\n***GC Dump %s\n", name);
2719 } else {
2720 GC_printf("\n***GC Dump collection #%lu\n", (unsigned long)GC_gc_no);
2721 }
2722 # ifndef NO_CLOCK
2723 /* Note that the time is wrapped in ~49 days if `sizeof(long) == 4`. */
2724 GC_printf("Time since GC init: %lu ms\n",
2725 MS_TIME_DIFF(current_time, GC_init_time));
2726 # endif
2727
2728 GC_printf("\n***Static roots:\n");
2729 GC_print_static_roots();
2730 GC_printf("\n***Heap sections:\n");
2731 GC_print_heap_sects();
2732 GC_printf("\n***Free blocks:\n");
2733 GC_print_hblkfreelist();
2734 GC_printf("\n***Blocks in use:\n");
2735 GC_print_block_list();
2736 # ifndef GC_NO_FINALIZATION
2737 GC_dump_finalization();
2738 # endif
2739 }
2740 #endif /* !NO_DEBUGGING */
2741
2742 GC_API GC_word GC_CALL
2743 GC_get_memory_use(void)
2744 {
2745 word bytes;
2746
2747 READER_LOCK();
2748 GC_ASSERT(GC_heapsize >= GC_large_free_bytes);
2749 bytes = GC_heapsize - GC_large_free_bytes;
2750 READER_UNLOCK();
2751 return bytes;
2752 }
2753
2754 /* Getter functions for the public read-only variables. */
2755
2756 GC_API GC_word GC_CALL
2757 GC_get_gc_no(void)
2758 {
2759 return GC_gc_no;
2760 }
2761
2762 #ifndef PARALLEL_MARK
2763 GC_API void GC_CALL
2764 GC_set_markers_count(unsigned markers)
2765 {
2766 UNUSED_ARG(markers);
2767 }
2768 #endif
2769
2770 GC_API int GC_CALL
2771 GC_get_parallel(void)
2772 {
2773 #ifdef THREADS
2774 return GC_parallel;
2775 #else
2776 return 0;
2777 #endif
2778 }
2779
2780 /*
2781 * Setter and getter functions for the public R/W function variables.
2782 * These functions are synchronized (like `GC_set_warn_proc()` and
2783 * `GC_get_warn_proc()`).
2784 */
2785
2786 GC_API void GC_CALL
2787 GC_set_oom_fn(GC_oom_func fn)
2788 {
2789 GC_ASSERT(NONNULL_ARG_NOT_NULL(fn));
2790 LOCK();
2791 GC_oom_fn = fn;
2792 UNLOCK();
2793 }
2794
2795 GC_API GC_oom_func GC_CALL
2796 GC_get_oom_fn(void)
2797 {
2798 GC_oom_func fn;
2799
2800 READER_LOCK();
2801 fn = GC_oom_fn;
2802 READER_UNLOCK();
2803 return fn;
2804 }
2805
2806 GC_API void GC_CALL
2807 GC_set_on_heap_resize(GC_on_heap_resize_proc fn)
2808 {
2809 /* `fn` may be 0 (means no event notifier). */
2810 LOCK();
2811 GC_on_heap_resize = fn;
2812 UNLOCK();
2813 }
2814
2815 GC_API GC_on_heap_resize_proc GC_CALL
2816 GC_get_on_heap_resize(void)
2817 {
2818 GC_on_heap_resize_proc fn;
2819
2820 READER_LOCK();
2821 fn = GC_on_heap_resize;
2822 READER_UNLOCK();
2823 return fn;
2824 }
2825
2826 GC_API void GC_CALL
2827 GC_set_finalizer_notifier(GC_finalizer_notifier_proc fn)
2828 {
2829 /* `fn` may be 0 (means no finalizer notifier). */
2830 LOCK();
2831 GC_finalizer_notifier = fn;
2832 UNLOCK();
2833 }
2834
2835 GC_API GC_finalizer_notifier_proc GC_CALL
2836 GC_get_finalizer_notifier(void)
2837 {
2838 GC_finalizer_notifier_proc fn;
2839
2840 READER_LOCK();
2841 fn = GC_finalizer_notifier;
2842 READER_UNLOCK();
2843 return fn;
2844 }
2845
2846 /*
2847 * Setter and getter functions for the public numeric R/W variables.
2848 * It is safe to call these functions even before `GC_INIT()`.
2849 * These functions are unsynchronized and, if called after `GC_INIT()`,
2850 * should be typically invoked inside the context of
2851 * `GC_call_with_alloc_lock()` (or `GC_call_with_reader_lock()` in case
2852 * of the getters) to prevent data race (unless it is guaranteed the
2853 * collector is not multi-threaded at that execution point).
2854 */
2855
2856 GC_API void GC_CALL
2857 GC_set_find_leak(int value)
2858 {
2859 /* `value` is of boolean type. */
2860 #ifdef NO_FIND_LEAK
2861 if (value)
2862 ABORT("Find-leak mode is unsupported");
2863 #else
2864 GC_find_leak = value;
2865 #endif
2866 }
2867
2868 GC_API int GC_CALL
2869 GC_get_find_leak(void)
2870 {
2871 return GC_find_leak_inner;
2872 }
2873
2874 GC_API void GC_CALL
2875 GC_set_all_interior_pointers(int value)
2876 {
2877 GC_all_interior_pointers = value ? 1 : 0;
2878 if (GC_is_initialized) {
2879 /*
2880 * It is not recommended to change `GC_all_interior_pointers` value
2881 * after the collector is initialized but it seems it could work
2882 * correctly even after switching the mode.
2883 */
2884 LOCK();
2885 /* Note: this resets manual offsets as well. */
2886 GC_initialize_offsets();
2887 #ifndef NO_BLACK_LISTING
2888 if (!GC_all_interior_pointers)
2889 GC_bl_init_no_interiors();
2890 #endif
2891 UNLOCK();
2892 }
2893 }
2894
2895 GC_API int GC_CALL
2896 GC_get_all_interior_pointers(void)
2897 {
2898 return GC_all_interior_pointers;
2899 }
2900
2901 GC_API void GC_CALL
2902 GC_set_finalize_on_demand(int value)
2903 {
2904 /* Note: -1 was used to retrieve old value in gc-7.2. */
2905 GC_ASSERT(value != -1);
2906 /* `value` is of boolean type. */
2907 GC_finalize_on_demand = value;
2908 }
2909
2910 GC_API int GC_CALL
2911 GC_get_finalize_on_demand(void)
2912 {
2913 return GC_finalize_on_demand;
2914 }
2915
2916 GC_API void GC_CALL
2917 GC_set_java_finalization(int value)
2918 {
2919 /* Note: -1 was used to retrieve old value in gc-7.2. */
2920 GC_ASSERT(value != -1);
2921 /* `value` is of boolean type. */
2922 GC_java_finalization = value;
2923 }
2924
2925 GC_API int GC_CALL
2926 GC_get_java_finalization(void)
2927 {
2928 return GC_java_finalization;
2929 }
2930
2931 GC_API void GC_CALL
2932 GC_set_dont_expand(int value)
2933 {
2934 /* Note: -1 was used to retrieve old value in gc-7.2. */
2935 GC_ASSERT(value != -1);
2936 /* `value` is of boolean type. */
2937 GC_dont_expand = value;
2938 }
2939
2940 GC_API int GC_CALL
2941 GC_get_dont_expand(void)
2942 {
2943 return GC_dont_expand;
2944 }
2945
2946 GC_API void GC_CALL
2947 GC_set_no_dls(int value)
2948 {
2949 /* Note: -1 was used to retrieve old value in gc-7.2. */
2950 GC_ASSERT(value != -1);
2951 /* `value` is of boolean type. */
2952 GC_no_dls = value;
2953 }
2954
2955 GC_API int GC_CALL
2956 GC_get_no_dls(void)
2957 {
2958 return GC_no_dls;
2959 }
2960
2961 GC_API void GC_CALL
2962 GC_set_non_gc_bytes(GC_word value)
2963 {
2964 GC_non_gc_bytes = value;
2965 }
2966
2967 GC_API GC_word GC_CALL
2968 GC_get_non_gc_bytes(void)
2969 {
2970 return GC_non_gc_bytes;
2971 }
2972
2973 GC_API void GC_CALL
2974 GC_set_free_space_divisor(GC_word value)
2975 {
2976 GC_ASSERT(value > 0);
2977 GC_free_space_divisor = value;
2978 }
2979
2980 GC_API GC_word GC_CALL
2981 GC_get_free_space_divisor(void)
2982 {
2983 return GC_free_space_divisor;
2984 }
2985
2986 GC_API void GC_CALL
2987 GC_set_max_retries(GC_word value)
2988 {
2989 /* Note: -1 was used to retrieve old value in gc-7.2. */
2990 GC_ASSERT((GC_signed_word)value != -1);
2991 GC_max_retries = value;
2992 }
2993
2994 GC_API GC_word GC_CALL
2995 GC_get_max_retries(void)
2996 {
2997 return GC_max_retries;
2998 }
2999
3000 GC_API void GC_CALL
3001 GC_set_dont_precollect(int value)
3002 {
3003 /* Note: -1 was used to retrieve old value in gc-7.2. */
3004 GC_ASSERT(value != -1);
3005 /* `value` is of boolean type. */
3006 GC_dont_precollect = value;
3007 }
3008
3009 GC_API int GC_CALL
3010 GC_get_dont_precollect(void)
3011 {
3012 return GC_dont_precollect;
3013 }
3014
3015 GC_API void GC_CALL
3016 GC_set_full_freq(int value)
3017 {
3018 GC_ASSERT(value >= 0);
3019 GC_full_freq = value;
3020 }
3021
3022 GC_API int GC_CALL
3023 GC_get_full_freq(void)
3024 {
3025 return GC_full_freq;
3026 }
3027
3028 GC_API void GC_CALL
3029 GC_set_time_limit(unsigned long value)
3030 {
3031 /* Note: -1 was used to retrieve old value in gc-7.2. */
3032 GC_ASSERT((long)value != -1L);
3033 GC_time_limit = value;
3034 }
3035
3036 GC_API unsigned long GC_CALL
3037 GC_get_time_limit(void)
3038 {
3039 return GC_time_limit;
3040 }
3041
3042 GC_API void GC_CALL
3043 GC_set_force_unmap_on_gcollect(int value)
3044 {
3045 GC_force_unmap_on_gcollect = (GC_bool)value;
3046 }
3047
3048 GC_API int GC_CALL
3049 GC_get_force_unmap_on_gcollect(void)
3050 {
3051 return (int)GC_force_unmap_on_gcollect;
3052 }
3053
3054 GC_API GC_OOM_ABORT_THROW_ATTRIBUTE void GC_CALL
3055 GC_abort_on_oom(void)
3056 {
3057 GC_err_printf("Insufficient memory for the allocation\n");
3058 EXIT();
3059 }
3060
3061 GC_API size_t GC_CALL
3062 GC_get_hblk_size(void)
3063 {
3064 return (size_t)HBLKSIZE;
3065 }
3066