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 *
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
16 #include "private/gc_pmark.h"
17
18 #include <stdio.h>
19 #include <limits.h>
20 #include <stdarg.h>
21
22 #ifndef MSWINCE
23 # include <signal.h>
24 #endif
25
26 #ifdef GC_SOLARIS_THREADS
27 # include <sys/syscall.h>
28 #endif
29 #if defined(MSWIN32) || defined(MSWINCE)
30 # ifndef WIN32_LEAN_AND_MEAN
31 # define WIN32_LEAN_AND_MEAN 1
32 # endif
33 # define NOSERVICE
34 # include <windows.h>
35 #endif
36
37 #if defined(UNIX_LIKE) || defined(CYGWIN32)
38 # include <fcntl.h>
39 # include <sys/types.h>
40 # include <sys/stat.h>
41 #endif
42
43 #ifdef NONSTOP
44 # include <floss.h>
45 #endif
46
47 #ifdef THREADS
48 # ifdef PCR
49 # include "il/PCR_IL.h"
50 GC_INNER PCR_Th_ML GC_allocate_ml;
51 # elif defined(SN_TARGET_PS3)
52 # include <pthread.h>
53 GC_INNER pthread_mutex_t GC_allocate_ml;
54 # endif
55 /* For other platforms with threads, the lock and possibly */
56 /* GC_lock_holder variables are defined in the thread support code. */
57 #endif /* THREADS */
58
59 #ifdef DYNAMIC_LOADING
60 /* We need to register the main data segment. Returns TRUE unless */
61 /* this is done implicitly as part of dynamic library registration. */
62 GC_INNER GC_bool GC_register_main_static_data(void);
63 # define GC_REGISTER_MAIN_STATIC_DATA() GC_register_main_static_data()
64 #else
65 /* Don't unnecessarily call GC_register_main_static_data() in case */
66 /* dyn_load.c isn't linked in. */
67 # define GC_REGISTER_MAIN_STATIC_DATA() TRUE
68 #endif
69
70 #ifdef NEED_CANCEL_DISABLE_COUNT
71 __thread unsigned char GC_cancel_disable_count = 0;
72 #endif
73
74 GC_FAR struct _GC_arrays GC_arrays /* = { 0 } */;
75
76 GC_INNER GC_bool GC_debugging_started = FALSE;
77 /* defined here so we don't have to load debug_malloc.o */
78
79 ptr_t GC_stackbottom = 0;
80
81 #ifdef IA64
82 ptr_t GC_register_stackbottom = 0;
83 #endif
84
85 GC_bool GC_dont_gc = 0;
86
87 GC_bool GC_dont_precollect = 0;
88
89 GC_bool GC_quiet = 0; /* used also in pcr_interface.c */
90
91 #ifndef SMALL_CONFIG
92 GC_bool GC_print_stats = 0;
93 #endif
94
95 #ifdef GC_PRINT_BACK_HEIGHT
96 GC_INNER GC_bool GC_print_back_height = TRUE;
97 #else
98 GC_INNER GC_bool GC_print_back_height = FALSE;
99 #endif
100
101 #ifndef NO_DEBUGGING
102 GC_INNER GC_bool GC_dump_regularly = FALSE;
103 /* Generate regular debugging dumps. */
104 #endif
105
106 #ifdef KEEP_BACK_PTRS
107 GC_INNER long GC_backtraces = 0;
108 /* Number of random backtraces to generate for each GC. */
109 #endif
110
111 #ifdef FIND_LEAK
112 int GC_find_leak = 1;
113 #else
114 int GC_find_leak = 0;
115 #endif
116
117 #ifndef SHORT_DBG_HDRS
118 # ifdef GC_FINDLEAK_DELAY_FREE
119 GC_INNER GC_bool GC_findleak_delay_free = TRUE;
120 # else
121 GC_INNER GC_bool GC_findleak_delay_free = FALSE;
122 # endif
123 #endif /* !SHORT_DBG_HDRS */
124
125 #ifdef ALL_INTERIOR_POINTERS
126 int GC_all_interior_pointers = 1;
127 #else
128 int GC_all_interior_pointers = 0;
129 #endif
130
131 #ifdef GC_FORCE_UNMAP_ON_GCOLLECT
132 /* Has no effect unless USE_MUNMAP. */
133 /* Has no effect on implicitly-initiated garbage collections. */
134 GC_INNER GC_bool GC_force_unmap_on_gcollect = TRUE;
135 #else
136 GC_INNER GC_bool GC_force_unmap_on_gcollect = FALSE;
137 #endif
138
139 #ifndef GC_LARGE_ALLOC_WARN_INTERVAL
140 # define GC_LARGE_ALLOC_WARN_INTERVAL 5
141 #endif
142 GC_INNER long GC_large_alloc_warn_interval = GC_LARGE_ALLOC_WARN_INTERVAL;
143 /* Interval between unsuppressed warnings. */
144
145 /*ARGSUSED*/
GC_default_oom_fn(size_t bytes_requested)146 STATIC void * GC_CALLBACK GC_default_oom_fn(size_t bytes_requested)
147 {
148 return(0);
149 }
150
151 /* All accesses to it should be synchronized to avoid data races. */
152 GC_oom_func GC_oom_fn = GC_default_oom_fn;
153
154 /* Set things up so that GC_size_map[i] >= granules(i), */
155 /* but not too much bigger */
156 /* and so that size_map contains relatively few distinct entries */
157 /* This was originally stolen from Russ Atkinson's Cedar */
158 /* quantization algorithm (but we precompute it). */
GC_init_size_map(void)159 STATIC void GC_init_size_map(void)
160 {
161 int i;
162
163 /* Map size 0 to something bigger. */
164 /* This avoids problems at lower levels. */
165 GC_size_map[0] = 1;
166 for (i = 1; i <= GRANULES_TO_BYTES(TINY_FREELISTS-1) - EXTRA_BYTES; i++) {
167 GC_size_map[i] = ROUNDED_UP_GRANULES(i);
168 # ifndef _MSC_VER
169 GC_ASSERT(GC_size_map[i] < TINY_FREELISTS);
170 /* Seems to tickle bug in VC++ 2008 for AMD64 */
171 # endif
172 }
173 /* We leave the rest of the array to be filled in on demand. */
174 }
175
176 /* Fill in additional entries in GC_size_map, including the ith one */
177 /* We assume the ith entry is currently 0. */
178 /* Note that a filled in section of the array ending at n always */
179 /* has length at least n/4. */
GC_extend_size_map(size_t i)180 GC_INNER void GC_extend_size_map(size_t i)
181 {
182 size_t orig_granule_sz = ROUNDED_UP_GRANULES(i);
183 size_t granule_sz = orig_granule_sz;
184 size_t byte_sz = GRANULES_TO_BYTES(granule_sz);
185 /* The size we try to preserve. */
186 /* Close to i, unless this would */
187 /* introduce too many distinct sizes. */
188 size_t smaller_than_i = byte_sz - (byte_sz >> 3);
189 size_t much_smaller_than_i = byte_sz - (byte_sz >> 2);
190 size_t low_limit; /* The lowest indexed entry we */
191 /* initialize. */
192 size_t j;
193
194 if (GC_size_map[smaller_than_i] == 0) {
195 low_limit = much_smaller_than_i;
196 while (GC_size_map[low_limit] != 0) low_limit++;
197 } else {
198 low_limit = smaller_than_i + 1;
199 while (GC_size_map[low_limit] != 0) low_limit++;
200 granule_sz = ROUNDED_UP_GRANULES(low_limit);
201 granule_sz += granule_sz >> 3;
202 if (granule_sz < orig_granule_sz) granule_sz = orig_granule_sz;
203 }
204 /* For these larger sizes, we use an even number of granules. */
205 /* This makes it easier to, for example, construct a 16byte-aligned */
206 /* allocator even if GRANULE_BYTES is 8. */
207 granule_sz += 1;
208 granule_sz &= ~1;
209 if (granule_sz > MAXOBJGRANULES) {
210 granule_sz = MAXOBJGRANULES;
211 }
212 /* If we can fit the same number of larger objects in a block, */
213 /* do so. */
214 {
215 size_t number_of_objs = HBLK_GRANULES/granule_sz;
216 granule_sz = HBLK_GRANULES/number_of_objs;
217 granule_sz &= ~1;
218 }
219 byte_sz = GRANULES_TO_BYTES(granule_sz);
220 /* We may need one extra byte; */
221 /* don't always fill in GC_size_map[byte_sz] */
222 byte_sz -= EXTRA_BYTES;
223
224 for (j = low_limit; j <= byte_sz; j++) GC_size_map[j] = granule_sz;
225 }
226
227
228 /*
229 * The following is a gross hack to deal with a problem that can occur
230 * on machines that are sloppy about stack frame sizes, notably SPARC.
231 * Bogus pointers may be written to the stack and not cleared for
232 * a LONG time, because they always fall into holes in stack frames
233 * that are not written. We partially address this by clearing
234 * sections of the stack whenever we get control.
235 */
236 # ifdef THREADS
237 # define BIG_CLEAR_SIZE 2048 /* Clear this much now and then. */
238 # define SMALL_CLEAR_SIZE 256 /* Clear this much every time. */
239 # else
240 STATIC word GC_stack_last_cleared = 0; /* GC_no when we last did this */
241 STATIC ptr_t GC_min_sp = NULL;
242 /* Coolest stack pointer value from which */
243 /* we've already cleared the stack. */
244 STATIC ptr_t GC_high_water = NULL;
245 /* "hottest" stack pointer value we have seen */
246 /* recently. Degrades over time. */
247 STATIC word GC_bytes_allocd_at_reset = 0;
248 # define DEGRADE_RATE 50
249 # endif
250
251 # define CLEAR_SIZE 213 /* Granularity for GC_clear_stack_inner */
252
253 #if defined(ASM_CLEAR_CODE)
254 void *GC_clear_stack_inner(void *, ptr_t);
255 #else
256 /* Clear the stack up to about limit. Return arg. This function is */
257 /* not static because it could also be errorneously defined in .S */
258 /* file, so this error would be caught by the linker. */
259 /*ARGSUSED*/
GC_clear_stack_inner(void * arg,ptr_t limit)260 void * GC_clear_stack_inner(void *arg, ptr_t limit)
261 {
262 word dummy[CLEAR_SIZE];
263
264 BZERO(dummy, CLEAR_SIZE*sizeof(word));
265 if ((ptr_t)(dummy) COOLER_THAN limit) {
266 (void) GC_clear_stack_inner(arg, limit);
267 }
268 /* Make sure the recursive call is not a tail call, and the bzero */
269 /* call is not recognized as dead code. */
270 GC_noop1((word)dummy);
271 return(arg);
272 }
273 #endif
274
275 /* Clear some of the inaccessible part of the stack. Returns its */
276 /* argument, so it can be used in a tail call position, hence clearing */
277 /* another frame. */
GC_clear_stack(void * arg)278 GC_INNER void * GC_clear_stack(void *arg)
279 {
280 ptr_t sp = GC_approx_sp(); /* Hotter than actual sp */
281 # ifdef THREADS
282 word dummy[SMALL_CLEAR_SIZE];
283 static unsigned random_no = 0;
284 /* Should be more random than it is ... */
285 /* Used to occasionally clear a bigger */
286 /* chunk. */
287 # endif
288 ptr_t limit;
289
290 # define SLOP 400
291 /* Extra bytes we clear every time. This clears our own */
292 /* activation record, and should cause more frequent */
293 /* clearing near the cold end of the stack, a good thing. */
294 # define GC_SLOP 4000
295 /* We make GC_high_water this much hotter than we really saw */
296 /* saw it, to cover for GC noise etc. above our current frame. */
297 # define CLEAR_THRESHOLD 100000
298 /* We restart the clearing process after this many bytes of */
299 /* allocation. Otherwise very heavily recursive programs */
300 /* with sparse stacks may result in heaps that grow almost */
301 /* without bounds. As the heap gets larger, collection */
302 /* frequency decreases, thus clearing frequency would decrease, */
303 /* thus more junk remains accessible, thus the heap gets */
304 /* larger ... */
305 # ifdef THREADS
306 if (++random_no % 13 == 0) {
307 limit = sp;
308 MAKE_HOTTER(limit, BIG_CLEAR_SIZE*sizeof(word));
309 limit = (ptr_t)((word)limit & ~0xf);
310 /* Make it sufficiently aligned for assembly */
311 /* implementations of GC_clear_stack_inner. */
312 return GC_clear_stack_inner(arg, limit);
313 } else {
314 BZERO(dummy, SMALL_CLEAR_SIZE*sizeof(word));
315 return arg;
316 }
317 # else
318 if (GC_gc_no > GC_stack_last_cleared) {
319 /* Start things over, so we clear the entire stack again */
320 if (GC_stack_last_cleared == 0) GC_high_water = (ptr_t)GC_stackbottom;
321 GC_min_sp = GC_high_water;
322 GC_stack_last_cleared = GC_gc_no;
323 GC_bytes_allocd_at_reset = GC_bytes_allocd;
324 }
325 /* Adjust GC_high_water */
326 MAKE_COOLER(GC_high_water, WORDS_TO_BYTES(DEGRADE_RATE) + GC_SLOP);
327 if (sp HOTTER_THAN GC_high_water) {
328 GC_high_water = sp;
329 }
330 MAKE_HOTTER(GC_high_water, GC_SLOP);
331 limit = GC_min_sp;
332 MAKE_HOTTER(limit, SLOP);
333 if (sp COOLER_THAN limit) {
334 limit = (ptr_t)((word)limit & ~0xf);
335 /* Make it sufficiently aligned for assembly */
336 /* implementations of GC_clear_stack_inner. */
337 GC_min_sp = sp;
338 return(GC_clear_stack_inner(arg, limit));
339 } else if (GC_bytes_allocd - GC_bytes_allocd_at_reset > CLEAR_THRESHOLD) {
340 /* Restart clearing process, but limit how much clearing we do. */
341 GC_min_sp = sp;
342 MAKE_HOTTER(GC_min_sp, CLEAR_THRESHOLD/4);
343 if (GC_min_sp HOTTER_THAN GC_high_water) GC_min_sp = GC_high_water;
344 GC_bytes_allocd_at_reset = GC_bytes_allocd;
345 }
346 return(arg);
347 # endif
348 }
349
350
351 /* Return a pointer to the base address of p, given a pointer to a */
352 /* an address within an object. Return 0 o.w. */
GC_base(void * p)353 GC_API void * GC_CALL GC_base(void * p)
354 {
355 ptr_t r;
356 struct hblk *h;
357 bottom_index *bi;
358 hdr *candidate_hdr;
359 ptr_t limit;
360
361 r = p;
362 if (!GC_is_initialized) return 0;
363 h = HBLKPTR(r);
364 GET_BI(r, bi);
365 candidate_hdr = HDR_FROM_BI(bi, r);
366 if (candidate_hdr == 0) return(0);
367 /* If it's a pointer to the middle of a large object, move it */
368 /* to the beginning. */
369 while (IS_FORWARDING_ADDR_OR_NIL(candidate_hdr)) {
370 h = FORWARDED_ADDR(h,candidate_hdr);
371 r = (ptr_t)h;
372 candidate_hdr = HDR(h);
373 }
374 if (HBLK_IS_FREE(candidate_hdr)) return(0);
375 /* Make sure r points to the beginning of the object */
376 r = (ptr_t)((word)r & ~(WORDS_TO_BYTES(1) - 1));
377 {
378 size_t offset = HBLKDISPL(r);
379 signed_word sz = candidate_hdr -> hb_sz;
380 size_t obj_displ = offset % sz;
381
382 r -= obj_displ;
383 limit = r + sz;
384 if (limit > (ptr_t)(h + 1) && sz <= HBLKSIZE) {
385 return(0);
386 }
387 if ((ptr_t)p >= limit) return(0);
388 }
389 return((void *)r);
390 }
391
392
393 /* Return the size of an object, given a pointer to its base. */
394 /* (For small objects this also happens to work from interior pointers, */
395 /* but that shouldn't be relied upon.) */
GC_size(const void * p)396 GC_API size_t GC_CALL GC_size(const void * p)
397 {
398 hdr * hhdr = HDR(p);
399
400 return hhdr -> hb_sz;
401 }
402
GC_get_heap_size(void)403 GC_API size_t GC_CALL GC_get_heap_size(void)
404 {
405 size_t value;
406 DCL_LOCK_STATE;
407 LOCK();
408 /* ignore the memory space returned to OS (i.e. count only the */
409 /* space owned by the garbage collector) */
410 value = (size_t)(GC_heapsize - GC_unmapped_bytes);
411 UNLOCK();
412 return value;
413 }
414
GC_get_free_bytes(void)415 GC_API size_t GC_CALL GC_get_free_bytes(void)
416 {
417 size_t value;
418 DCL_LOCK_STATE;
419 LOCK();
420 /* ignore the memory space returned to OS */
421 value = (size_t)(GC_large_free_bytes - GC_unmapped_bytes);
422 UNLOCK();
423 return value;
424 }
425
426 /* The _inner versions assume the caller holds the allocation lock. */
427 /* Declared in gc_mark.h (where other public "inner" functions reside). */
GC_get_heap_size_inner(void)428 GC_API size_t GC_CALL GC_get_heap_size_inner(void)
429 {
430 return (size_t)(GC_heapsize - GC_unmapped_bytes);
431 }
432
GC_get_free_bytes_inner(void)433 GC_API size_t GC_CALL GC_get_free_bytes_inner(void)
434 {
435 return (size_t)(GC_large_free_bytes - GC_unmapped_bytes);
436 }
437
GC_get_unmapped_bytes(void)438 GC_API size_t GC_CALL GC_get_unmapped_bytes(void)
439 {
440 # ifdef USE_MUNMAP
441 size_t value;
442 DCL_LOCK_STATE;
443 LOCK();
444 value = (size_t)GC_unmapped_bytes;
445 UNLOCK();
446 return value;
447 # else
448 return 0;
449 # endif
450 }
451
GC_get_bytes_since_gc(void)452 GC_API size_t GC_CALL GC_get_bytes_since_gc(void)
453 {
454 size_t value;
455 DCL_LOCK_STATE;
456 LOCK();
457 value = GC_bytes_allocd;
458 UNLOCK();
459 return value;
460 }
461
GC_get_total_bytes(void)462 GC_API size_t GC_CALL GC_get_total_bytes(void)
463 {
464 size_t value;
465 DCL_LOCK_STATE;
466 LOCK();
467 value = GC_bytes_allocd+GC_bytes_allocd_before_gc;
468 UNLOCK();
469 return value;
470 }
471
472 #ifdef THREADS
GC_get_suspend_signal(void)473 GC_API int GC_CALL GC_get_suspend_signal(void)
474 {
475 # ifdef SIG_SUSPEND
476 return SIG_SUSPEND;
477 # else
478 return -1;
479 # endif
480 }
481 #endif /* THREADS */
482
483 #ifdef GC_READ_ENV_FILE
484 /* This works for Win32/WinCE for now. Really useful only for WinCE. */
485 STATIC char *GC_envfile_content = NULL;
486 /* The content of the GC "env" file with CR and */
487 /* LF replaced to '\0'. NULL if the file is */
488 /* missing or empty. Otherwise, always ends */
489 /* with '\0'. */
490 STATIC unsigned GC_envfile_length = 0;
491 /* Length of GC_envfile_content (if non-NULL). */
492
493 # ifndef GC_ENVFILE_MAXLEN
494 # define GC_ENVFILE_MAXLEN 0x4000
495 # endif
496
497 /* The routine initializes GC_envfile_content from the GC "env" file. */
GC_envfile_init(void)498 STATIC void GC_envfile_init(void)
499 {
500 # if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
501 HANDLE hFile;
502 char *content;
503 unsigned ofs;
504 unsigned len;
505 DWORD nBytesRead;
506 TCHAR path[_MAX_PATH + 0x10]; /* buffer for path + ext */
507 len = (unsigned)GetModuleFileName(NULL /* hModule */, path,
508 _MAX_PATH + 1);
509 /* If GetModuleFileName() has failed then len is 0. */
510 if (len > 4 && path[len - 4] == (TCHAR)'.') {
511 len -= 4; /* strip executable file extension */
512 }
513 memcpy(&path[len], TEXT(".gc.env"), sizeof(TEXT(".gc.env")));
514 hFile = CreateFile(path, GENERIC_READ,
515 FILE_SHARE_READ | FILE_SHARE_WRITE,
516 NULL /* lpSecurityAttributes */, OPEN_EXISTING,
517 FILE_ATTRIBUTE_NORMAL, NULL /* hTemplateFile */);
518 if (hFile == INVALID_HANDLE_VALUE)
519 return; /* the file is absent or the operation is failed */
520 len = (unsigned)GetFileSize(hFile, NULL);
521 if (len <= 1 || len >= GC_ENVFILE_MAXLEN) {
522 CloseHandle(hFile);
523 return; /* invalid file length - ignoring the file content */
524 }
525 /* At this execution point, GC_setpagesize() and GC_init_win32() */
526 /* must already be called (for GET_MEM() to work correctly). */
527 content = (char *)GET_MEM(len + 1);
528 if (content == NULL) {
529 CloseHandle(hFile);
530 return; /* allocation failure */
531 }
532 ofs = 0;
533 nBytesRead = (DWORD)-1L;
534 /* Last ReadFile() call should clear nBytesRead on success. */
535 while (ReadFile(hFile, content + ofs, len - ofs + 1, &nBytesRead,
536 NULL /* lpOverlapped */) && nBytesRead != 0) {
537 if ((ofs += nBytesRead) > len)
538 break;
539 }
540 CloseHandle(hFile);
541 if (ofs != len || nBytesRead != 0)
542 return; /* read operation is failed - ignoring the file content */
543 content[ofs] = '\0';
544 while (ofs-- > 0) {
545 if (content[ofs] == '\r' || content[ofs] == '\n')
546 content[ofs] = '\0';
547 }
548 GC_envfile_length = len + 1;
549 GC_envfile_content = content;
550 # endif
551 }
552
553 /* This routine scans GC_envfile_content for the specified */
554 /* environment variable (and returns its value if found). */
GC_envfile_getenv(const char * name)555 GC_INNER char * GC_envfile_getenv(const char *name)
556 {
557 char *p;
558 char *end_of_content;
559 unsigned namelen;
560 # ifndef NO_GETENV
561 p = getenv(name); /* try the standard getenv() first */
562 if (p != NULL)
563 return *p != '\0' ? p : NULL;
564 # endif
565 p = GC_envfile_content;
566 if (p == NULL)
567 return NULL; /* "env" file is absent (or empty) */
568 namelen = strlen(name);
569 if (namelen == 0) /* a sanity check */
570 return NULL;
571 for (end_of_content = p + GC_envfile_length;
572 p != end_of_content; p += strlen(p) + 1) {
573 if (strncmp(p, name, namelen) == 0 && *(p += namelen) == '=') {
574 p++; /* the match is found; skip '=' */
575 return *p != '\0' ? p : NULL;
576 }
577 /* If not matching then skip to the next line. */
578 }
579 return NULL; /* no match found */
580 }
581 #endif /* GC_READ_ENV_FILE */
582
583 GC_INNER GC_bool GC_is_initialized = FALSE;
584
585 #if (defined(MSWIN32) || defined(MSWINCE)) && defined(THREADS)
586 GC_INNER CRITICAL_SECTION GC_write_cs;
587 #endif
588
589 #ifdef MSWIN32
590 GC_INNER void GC_init_win32(void);
591 #endif
592
593 GC_INNER void GC_setpagesize(void);
594
GC_exit_check(void)595 STATIC void GC_exit_check(void)
596 {
597 GC_gcollect();
598 }
599
600 #ifdef SEARCH_FOR_DATA_START
601 GC_INNER void GC_init_linux_data_start(void);
602 #endif
603
604 #ifdef UNIX_LIKE
605
606 GC_INNER void GC_set_and_save_fault_handler(void (*handler)(int));
607
looping_handler(int sig)608 static void looping_handler(int sig)
609 {
610 GC_err_printf("Caught signal %d: looping in handler\n", sig);
611 for (;;) {}
612 }
613
614 static GC_bool installed_looping_handler = FALSE;
615
maybe_install_looping_handler(void)616 static void maybe_install_looping_handler(void)
617 {
618 /* Install looping handler before the write fault handler, so we */
619 /* handle write faults correctly. */
620 if (!installed_looping_handler && 0 != GETENV("GC_LOOP_ON_ABORT")) {
621 GC_set_and_save_fault_handler(looping_handler);
622 installed_looping_handler = TRUE;
623 }
624 }
625
626 #else /* !UNIX_LIKE */
627 # define maybe_install_looping_handler()
628 #endif
629
630 #if defined(DYNAMIC_LOADING) && defined(DARWIN)
631 GC_INNER void GC_init_dyld(void);
632 #endif
633
634 #if defined(NETBSD) && defined(__ELF__)
635 GC_INNER void GC_init_netbsd_elf(void);
636 #endif
637
638 #if !defined(OS2) && !defined(MACOS) && !defined(MSWIN32) && !defined(MSWINCE)
639 STATIC int GC_stdout = 1;
640 STATIC int GC_stderr = 2;
641 STATIC int GC_log = 2; /* stderr */
642 #endif
643
GC_parse_mem_size_arg(const char * str)644 STATIC word GC_parse_mem_size_arg(const char *str)
645 {
646 char *endptr;
647 word result = 0; /* bad value */
648 char ch;
649
650 if (*str != '\0') {
651 result = (word)STRTOULL(str, &endptr, 10);
652 ch = *endptr;
653 if (ch != '\0') {
654 if (*(endptr + 1) != '\0')
655 return 0;
656 /* Allow k, M or G suffix. */
657 switch (ch) {
658 case 'K':
659 case 'k':
660 result <<= 10;
661 break;
662 case 'M':
663 case 'm':
664 result <<= 20;
665 break;
666 case 'G':
667 case 'g':
668 result <<= 30;
669 break;
670 default:
671 result = 0;
672 }
673 }
674 }
675 return result;
676 }
677
678 GC_INNER void GC_initialize_offsets(void); /* defined in obj_map.c */
679 GC_INNER void GC_bl_init(void); /* defined in blacklst.c */
680
GC_init(void)681 GC_API void GC_CALL GC_init(void)
682 {
683 /* LOCK(); -- no longer does anything this early. */
684 # if !defined(THREADS) && defined(GC_ASSERTIONS)
685 word dummy;
686 # endif
687 word initial_heap_sz;
688 IF_CANCEL(int cancel_state;)
689
690 if (GC_is_initialized) return;
691
692 # ifdef GC_INITIAL_HEAP_SIZE
693 initial_heap_sz = divHBLKSZ(GC_INITIAL_HEAP_SIZE);
694 # else
695 initial_heap_sz = (word)MINHINCR;
696 # endif
697 DISABLE_CANCEL(cancel_state);
698 /* Note that although we are nominally called with the */
699 /* allocation lock held, the allocation lock is now */
700 /* only really acquired once a second thread is forked.*/
701 /* And the initialization code needs to run before */
702 /* then. Thus we really don't hold any locks, and can */
703 /* in fact safely initialize them here. */
704 # ifdef THREADS
705 GC_ASSERT(!GC_need_to_lock);
706 # ifdef SN_TARGET_PS3
707 {
708 pthread_mutexattr_t mattr;
709 pthread_mutexattr_init(&mattr);
710 pthread_mutex_init(&GC_allocate_ml, &mattr);
711 pthread_mutexattr_destroy(&mattr);
712 }
713 # endif
714 # endif /* THREADS */
715 # if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS)
716 {
717 # ifndef MSWINCE
718 BOOL (WINAPI *pfn) (LPCRITICAL_SECTION, DWORD) = NULL;
719 HMODULE hK32 = GetModuleHandle(TEXT("kernel32.dll"));
720 if (hK32)
721 pfn = (BOOL (WINAPI *) (LPCRITICAL_SECTION, DWORD))
722 GetProcAddress (hK32,
723 "InitializeCriticalSectionAndSpinCount");
724 if (pfn)
725 pfn(&GC_allocate_ml, 4000);
726 else
727 # endif /* !MSWINCE */
728 /* else */ InitializeCriticalSection (&GC_allocate_ml);
729 }
730 # endif /* GC_WIN32_THREADS */
731 # if (defined(MSWIN32) || defined(MSWINCE)) && defined(THREADS)
732 InitializeCriticalSection(&GC_write_cs);
733 # endif
734 GC_setpagesize();
735 # ifdef MSWIN32
736 GC_init_win32();
737 # endif
738 # ifdef GC_READ_ENV_FILE
739 GC_envfile_init();
740 # endif
741 # ifndef SMALL_CONFIG
742 # ifdef GC_PRINT_VERBOSE_STATS
743 /* This is useful for debugging and profiling on platforms with */
744 /* missing getenv() (like WinCE). */
745 GC_print_stats = VERBOSE;
746 # else
747 if (0 != GETENV("GC_PRINT_VERBOSE_STATS")) {
748 GC_print_stats = VERBOSE;
749 } else if (0 != GETENV("GC_PRINT_STATS")) {
750 GC_print_stats = 1;
751 }
752 # endif
753 # if defined(UNIX_LIKE) || defined(CYGWIN32)
754 {
755 char * file_name = GETENV("GC_LOG_FILE");
756 if (0 != file_name) {
757 int log_d = open(file_name, O_CREAT|O_WRONLY|O_APPEND, 0666);
758 if (log_d < 0) {
759 GC_err_printf("Failed to open %s as log file\n", file_name);
760 } else {
761 char *str;
762 GC_log = log_d;
763 str = GETENV("GC_ONLY_LOG_TO_FILE");
764 # ifdef GC_ONLY_LOG_TO_FILE
765 /* The similar environment variable set to "0" */
766 /* overrides the effect of the macro defined. */
767 if (str != NULL && *str == '0' && *(str + 1) == '\0')
768 # else
769 /* Otherwise setting the environment variable */
770 /* to anything other than "0" will prevent from */
771 /* redirecting stdout/err to the log file. */
772 if (str == NULL || (*str == '0' && *(str + 1) == '\0'))
773 # endif
774 {
775 GC_stdout = log_d;
776 GC_stderr = log_d;
777 }
778 }
779 }
780 }
781 # endif
782 # endif /* !SMALL_CONFIG */
783 # ifndef NO_DEBUGGING
784 if (0 != GETENV("GC_DUMP_REGULARLY")) {
785 GC_dump_regularly = TRUE;
786 }
787 # endif
788 # ifdef KEEP_BACK_PTRS
789 {
790 char * backtraces_string = GETENV("GC_BACKTRACES");
791 if (0 != backtraces_string) {
792 GC_backtraces = atol(backtraces_string);
793 if (backtraces_string[0] == '\0') GC_backtraces = 1;
794 }
795 }
796 # endif
797 if (0 != GETENV("GC_FIND_LEAK")) {
798 GC_find_leak = 1;
799 }
800 # ifndef SHORT_DBG_HDRS
801 if (0 != GETENV("GC_FINDLEAK_DELAY_FREE")) {
802 GC_findleak_delay_free = TRUE;
803 }
804 # endif
805 if (0 != GETENV("GC_ALL_INTERIOR_POINTERS")) {
806 GC_all_interior_pointers = 1;
807 }
808 if (0 != GETENV("GC_DONT_GC")) {
809 GC_dont_gc = 1;
810 }
811 if (0 != GETENV("GC_PRINT_BACK_HEIGHT")) {
812 GC_print_back_height = TRUE;
813 }
814 if (0 != GETENV("GC_NO_BLACKLIST_WARNING")) {
815 GC_large_alloc_warn_interval = LONG_MAX;
816 }
817 {
818 char * addr_string = GETENV("GC_TRACE");
819 if (0 != addr_string) {
820 # ifndef ENABLE_TRACE
821 WARN("Tracing not enabled: Ignoring GC_TRACE value\n", 0);
822 # else
823 word addr = (word)STRTOULL(addr_string, NULL, 16);
824 if (addr < 0x1000)
825 WARN("Unlikely trace address: %p\n", addr);
826 GC_trace_addr = (ptr_t)addr;
827 # endif
828 }
829 }
830 # ifndef GC_DISABLE_INCREMENTAL
831 {
832 char * time_limit_string = GETENV("GC_PAUSE_TIME_TARGET");
833 if (0 != time_limit_string) {
834 long time_limit = atol(time_limit_string);
835 if (time_limit < 5) {
836 WARN("GC_PAUSE_TIME_TARGET environment variable value too small "
837 "or bad syntax: Ignoring\n", 0);
838 } else {
839 GC_time_limit = time_limit;
840 }
841 }
842 }
843 # endif
844 # ifndef SMALL_CONFIG
845 {
846 char * full_freq_string = GETENV("GC_FULL_FREQUENCY");
847 if (full_freq_string != NULL) {
848 int full_freq = atoi(full_freq_string);
849 if (full_freq > 0)
850 GC_full_freq = full_freq;
851 }
852 }
853 # endif
854 {
855 char * interval_string = GETENV("GC_LARGE_ALLOC_WARN_INTERVAL");
856 if (0 != interval_string) {
857 long interval = atol(interval_string);
858 if (interval <= 0) {
859 WARN("GC_LARGE_ALLOC_WARN_INTERVAL environment variable has "
860 "bad value: Ignoring\n", 0);
861 } else {
862 GC_large_alloc_warn_interval = interval;
863 }
864 }
865 }
866 {
867 char * space_divisor_string = GETENV("GC_FREE_SPACE_DIVISOR");
868 if (space_divisor_string != NULL) {
869 int space_divisor = atoi(space_divisor_string);
870 if (space_divisor > 0)
871 GC_free_space_divisor = (GC_word)space_divisor;
872 }
873 }
874 # ifdef USE_MUNMAP
875 {
876 char * string = GETENV("GC_UNMAP_THRESHOLD");
877 if (string != NULL) {
878 if (*string == '0' && *(string + 1) == '\0') {
879 /* "0" is used to disable unmapping. */
880 GC_unmap_threshold = 0;
881 } else {
882 int unmap_threshold = atoi(string);
883 if (unmap_threshold > 0)
884 GC_unmap_threshold = unmap_threshold;
885 }
886 }
887 }
888 {
889 char * string = GETENV("GC_FORCE_UNMAP_ON_GCOLLECT");
890 if (string != NULL) {
891 if (*string == '0' && *(string + 1) == '\0') {
892 /* "0" is used to turn off the mode. */
893 GC_force_unmap_on_gcollect = FALSE;
894 } else {
895 GC_force_unmap_on_gcollect = TRUE;
896 }
897 }
898 }
899 {
900 char * string = GETENV("GC_USE_ENTIRE_HEAP");
901 if (string != NULL) {
902 if (*string == '0' && *(string + 1) == '\0') {
903 /* "0" is used to turn off the mode. */
904 GC_use_entire_heap = FALSE;
905 } else {
906 GC_use_entire_heap = TRUE;
907 }
908 }
909 }
910 # endif
911 maybe_install_looping_handler();
912 /* Adjust normal object descriptor for extra allocation. */
913 if (ALIGNMENT > GC_DS_TAGS && EXTRA_BYTES != 0) {
914 GC_obj_kinds[NORMAL].ok_descriptor = ((word)(-ALIGNMENT) | GC_DS_LENGTH);
915 }
916 GC_exclude_static_roots_inner(beginGC_arrays, endGC_arrays);
917 GC_exclude_static_roots_inner(beginGC_obj_kinds, endGC_obj_kinds);
918 # ifdef SEPARATE_GLOBALS
919 GC_exclude_static_roots_inner(beginGC_objfreelist, endGC_objfreelist);
920 GC_exclude_static_roots_inner(beginGC_aobjfreelist, endGC_aobjfreelist);
921 # endif
922 # if defined(USE_PROC_FOR_LIBRARIES) && defined(GC_LINUX_THREADS)
923 WARN("USE_PROC_FOR_LIBRARIES + GC_LINUX_THREADS performs poorly.\n", 0);
924 /* If thread stacks are cached, they tend to be scanned in */
925 /* entirety as part of the root set. This wil grow them to */
926 /* maximum size, and is generally not desirable. */
927 # endif
928 # if defined(SEARCH_FOR_DATA_START)
929 GC_init_linux_data_start();
930 # endif
931 # if defined(NETBSD) && defined(__ELF__)
932 GC_init_netbsd_elf();
933 # endif
934 # if !defined(THREADS) || defined(GC_PTHREADS) || defined(GC_WIN32_THREADS) \
935 || defined(GC_SOLARIS_THREADS)
936 if (GC_stackbottom == 0) {
937 GC_stackbottom = GC_get_main_stack_base();
938 # if (defined(LINUX) || defined(HPUX)) && defined(IA64)
939 GC_register_stackbottom = GC_get_register_stack_base();
940 # endif
941 } else {
942 # if (defined(LINUX) || defined(HPUX)) && defined(IA64)
943 if (GC_register_stackbottom == 0) {
944 WARN("GC_register_stackbottom should be set with GC_stackbottom\n", 0);
945 /* The following may fail, since we may rely on */
946 /* alignment properties that may not hold with a user set */
947 /* GC_stackbottom. */
948 GC_register_stackbottom = GC_get_register_stack_base();
949 }
950 # endif
951 }
952 # endif
953 GC_STATIC_ASSERT(sizeof (ptr_t) == sizeof(word));
954 GC_STATIC_ASSERT(sizeof (signed_word) == sizeof(word));
955 GC_STATIC_ASSERT(sizeof (struct hblk) == HBLKSIZE);
956 # ifndef THREADS
957 # ifdef STACK_GROWS_DOWN
958 GC_ASSERT((word)(&dummy) <= (word)GC_stackbottom);
959 # else
960 GC_ASSERT((word)(&dummy) >= (word)GC_stackbottom);
961 # endif
962 # endif
963 # if !defined(_AUX_SOURCE) || defined(__GNUC__)
964 GC_STATIC_ASSERT((word)(-1) > (word)0);
965 /* word should be unsigned */
966 # endif
967 # if !defined(__BORLANDC__) && !defined(__CC_ARM) /* Workaround */
968 GC_STATIC_ASSERT((ptr_t)(word)(-1) > (ptr_t)0);
969 /* Ptr_t comparisons should behave as unsigned comparisons. */
970 # endif
971 GC_STATIC_ASSERT((signed_word)(-1) < (signed_word)0);
972 # ifndef GC_DISABLE_INCREMENTAL
973 if (GC_incremental || 0 != GETENV("GC_ENABLE_INCREMENTAL")) {
974 /* For GWW_VDB on Win32, this needs to happen before any */
975 /* heap memory is allocated. */
976 GC_dirty_init();
977 GC_ASSERT(GC_bytes_allocd == 0)
978 GC_incremental = TRUE;
979 }
980 # endif
981
982 /* Add initial guess of root sets. Do this first, since sbrk(0) */
983 /* might be used. */
984 if (GC_REGISTER_MAIN_STATIC_DATA()) GC_register_data_segments();
985 GC_init_headers();
986 GC_bl_init();
987 GC_mark_init();
988 {
989 char * sz_str = GETENV("GC_INITIAL_HEAP_SIZE");
990 if (sz_str != NULL) {
991 initial_heap_sz = GC_parse_mem_size_arg(sz_str);
992 if (initial_heap_sz <= MINHINCR * HBLKSIZE) {
993 WARN("Bad initial heap size %s - ignoring it.\n", sz_str);
994 }
995 initial_heap_sz = divHBLKSZ(initial_heap_sz);
996 }
997 }
998 {
999 char * sz_str = GETENV("GC_MAXIMUM_HEAP_SIZE");
1000 if (sz_str != NULL) {
1001 word max_heap_sz = GC_parse_mem_size_arg(sz_str);
1002 if (max_heap_sz < initial_heap_sz * HBLKSIZE) {
1003 WARN("Bad maximum heap size %s - ignoring it.\n", sz_str);
1004 }
1005 if (0 == GC_max_retries) GC_max_retries = 2;
1006 GC_set_max_heap_size(max_heap_sz);
1007 }
1008 }
1009 if (!GC_expand_hp_inner(initial_heap_sz)) {
1010 GC_err_printf("Can't start up: not enough memory\n");
1011 EXIT();
1012 }
1013 if (GC_all_interior_pointers)
1014 GC_initialize_offsets();
1015 GC_register_displacement_inner(0L);
1016 # if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC)
1017 if (!GC_all_interior_pointers) {
1018 /* TLS ABI uses pointer-sized offsets for dtv. */
1019 GC_register_displacement_inner(sizeof(void *));
1020 }
1021 # endif
1022 GC_init_size_map();
1023 # ifdef PCR
1024 if (PCR_IL_Lock(PCR_Bool_false, PCR_allSigsBlocked, PCR_waitForever)
1025 != PCR_ERes_okay) {
1026 ABORT("Can't lock load state");
1027 } else if (PCR_IL_Unlock() != PCR_ERes_okay) {
1028 ABORT("Can't unlock load state");
1029 }
1030 PCR_IL_Unlock();
1031 GC_pcr_install();
1032 # endif
1033 GC_is_initialized = TRUE;
1034 # if defined(GC_PTHREADS) || defined(GC_WIN32_THREADS)
1035 GC_thr_init();
1036 # endif
1037 COND_DUMP;
1038 /* Get black list set up and/or incremental GC started */
1039 if (!GC_dont_precollect || GC_incremental) GC_gcollect_inner();
1040 # ifdef STUBBORN_ALLOC
1041 GC_stubborn_init();
1042 # endif
1043 /* Convince lint that some things are used */
1044 # ifdef LINT
1045 {
1046 extern char * const GC_copyright[];
1047 GC_noop(GC_copyright, GC_find_header, GC_push_one,
1048 GC_call_with_alloc_lock, GC_dont_expand,
1049 # ifndef NO_DEBUGGING
1050 GC_dump,
1051 # endif
1052 GC_register_finalizer_no_order);
1053 }
1054 # endif
1055
1056 if (GC_find_leak) {
1057 /* This is to give us at least one chance to detect leaks. */
1058 /* This may report some very benign leaks, but ... */
1059 atexit(GC_exit_check);
1060 }
1061
1062 /* The rest of this again assumes we don't really hold */
1063 /* the allocation lock. */
1064 # if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
1065 /* Make sure marker threads are started and thread local */
1066 /* allocation is initialized, in case we didn't get */
1067 /* called from GC_init_parallel. */
1068 GC_init_parallel();
1069 # endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */
1070
1071 # if defined(DYNAMIC_LOADING) && defined(DARWIN)
1072 /* This must be called WITHOUT the allocation lock held */
1073 /* and before any threads are created. */
1074 GC_init_dyld();
1075 # endif
1076 RESTORE_CANCEL(cancel_state);
1077 }
1078
GC_enable_incremental(void)1079 GC_API void GC_CALL GC_enable_incremental(void)
1080 {
1081 # if !defined(GC_DISABLE_INCREMENTAL) && !defined(KEEP_BACK_PTRS)
1082 DCL_LOCK_STATE;
1083 /* If we are keeping back pointers, the GC itself dirties all */
1084 /* pages on which objects have been marked, making */
1085 /* incremental GC pointless. */
1086 if (!GC_find_leak && 0 == GETENV("GC_DISABLE_INCREMENTAL")) {
1087 LOCK();
1088 if (!GC_incremental) {
1089 GC_setpagesize();
1090 /* if (GC_no_win32_dlls) goto out; Should be win32S test? */
1091 maybe_install_looping_handler(); /* Before write fault handler! */
1092 GC_incremental = TRUE;
1093 if (!GC_is_initialized) {
1094 GC_init();
1095 } else {
1096 GC_dirty_init();
1097 }
1098 if (GC_dirty_maintained && !GC_dont_gc) {
1099 /* Can't easily do it if GC_dont_gc. */
1100 if (GC_bytes_allocd > 0) {
1101 /* There may be unmarked reachable objects. */
1102 GC_gcollect_inner();
1103 }
1104 /* else we're OK in assuming everything's */
1105 /* clean since nothing can point to an */
1106 /* unmarked object. */
1107 GC_read_dirty();
1108 }
1109 }
1110 UNLOCK();
1111 return;
1112 }
1113 # endif
1114 GC_init();
1115 }
1116
1117 #if defined(MSWIN32) || defined(MSWINCE)
1118
1119 # if defined(_MSC_VER) && defined(_DEBUG) && !defined(MSWINCE)
1120 # include <crtdbg.h>
1121 # endif
1122
1123 STATIC HANDLE GC_log = 0;
1124
GC_deinit(void)1125 void GC_deinit(void)
1126 {
1127 # ifdef THREADS
1128 if (GC_is_initialized) {
1129 DeleteCriticalSection(&GC_write_cs);
1130 }
1131 # endif
1132 }
1133
1134 # ifdef THREADS
1135 # ifdef PARALLEL_MARK
1136 # define IF_NEED_TO_LOCK(x) if (GC_parallel || GC_need_to_lock) x
1137 # else
1138 # define IF_NEED_TO_LOCK(x) if (GC_need_to_lock) x
1139 # endif
1140 # else
1141 # define IF_NEED_TO_LOCK(x)
1142 # endif /* !THREADS */
1143
1144 # ifndef _MAX_PATH
1145 # define _MAX_PATH MAX_PATH
1146 # endif
1147
GC_CreateLogFile(void)1148 STATIC HANDLE GC_CreateLogFile(void)
1149 {
1150 # if !defined(NO_GETENV_WIN32) || !defined(OLD_WIN32_LOG_FILE)
1151 TCHAR logPath[_MAX_PATH + 0x10]; /* buffer for path + ext */
1152 # endif
1153 /* Use GetEnvironmentVariable instead of GETENV() for unicode support. */
1154 # ifndef NO_GETENV_WIN32
1155 if (GetEnvironmentVariable(TEXT("GC_LOG_FILE"), logPath,
1156 _MAX_PATH + 1) - 1U >= (DWORD)_MAX_PATH)
1157 # endif
1158 {
1159 /* Env var not found or its value too long. */
1160 # ifdef OLD_WIN32_LOG_FILE
1161 return CreateFile(TEXT("gc.log"), GENERIC_WRITE, FILE_SHARE_READ,
1162 NULL /* lpSecurityAttributes */, CREATE_ALWAYS,
1163 FILE_FLAG_WRITE_THROUGH, NULL /* hTemplateFile */);
1164 # else
1165 int len = (int)GetModuleFileName(NULL /* hModule */, logPath,
1166 _MAX_PATH + 1);
1167 /* If GetModuleFileName() has failed then len is 0. */
1168 if (len > 4 && logPath[len - 4] == (TCHAR)'.') {
1169 len -= 4; /* strip executable file extension */
1170 }
1171 /* strcat/wcscat() are deprecated on WinCE, so use memcpy() */
1172 memcpy(&logPath[len], TEXT(".gc.log"), sizeof(TEXT(".gc.log")));
1173 # endif
1174 }
1175 # if !defined(NO_GETENV_WIN32) || !defined(OLD_WIN32_LOG_FILE)
1176 return CreateFile(logPath, GENERIC_WRITE, FILE_SHARE_READ,
1177 NULL /* lpSecurityAttributes */, CREATE_ALWAYS,
1178 GC_print_stats == VERBOSE ? FILE_ATTRIBUTE_NORMAL :
1179 /* immediately flush writes unless very verbose */
1180 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
1181 NULL /* hTemplateFile */);
1182 # endif
1183 }
1184
GC_write(const char * buf,size_t len)1185 STATIC int GC_write(const char *buf, size_t len)
1186 {
1187 BOOL tmp;
1188 DWORD written;
1189 if (len == 0)
1190 return 0;
1191 IF_NEED_TO_LOCK(EnterCriticalSection(&GC_write_cs));
1192 # ifdef THREADS
1193 GC_ASSERT(!GC_write_disabled);
1194 # endif
1195 if (GC_log == INVALID_HANDLE_VALUE) {
1196 IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs));
1197 return -1;
1198 } else if (GC_log == 0) {
1199 GC_log = GC_CreateLogFile();
1200 /* Ignore open log failure if the collector is built with */
1201 /* print_stats always set on. */
1202 # ifndef GC_PRINT_VERBOSE_STATS
1203 if (GC_log == INVALID_HANDLE_VALUE)
1204 ABORT("Open of log file failed");
1205 # endif
1206 }
1207 tmp = WriteFile(GC_log, buf, (DWORD)len, &written, NULL);
1208 if (!tmp)
1209 DebugBreak();
1210 # if defined(_MSC_VER) && defined(_DEBUG)
1211 # ifdef MSWINCE
1212 /* There is no CrtDbgReport() in WinCE */
1213 {
1214 WCHAR wbuf[1024];
1215 /* Always use Unicode variant of OutputDebugString() */
1216 wbuf[MultiByteToWideChar(CP_ACP, 0 /* dwFlags */,
1217 buf, len, wbuf,
1218 sizeof(wbuf) / sizeof(wbuf[0]) - 1)] = 0;
1219 OutputDebugStringW(wbuf);
1220 }
1221 # else
1222 _CrtDbgReport(_CRT_WARN, NULL, 0, NULL, "%.*s", len, buf);
1223 # endif
1224 # endif
1225 IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs));
1226 return tmp ? (int)written : -1;
1227 }
1228
1229 /* FIXME: This is pretty ugly ... */
1230 # define WRITE(f, buf, len) GC_write(buf, len)
1231
1232 #elif defined(OS2) || defined(MACOS)
1233 STATIC FILE * GC_stdout = NULL;
1234 STATIC FILE * GC_stderr = NULL;
1235 STATIC FILE * GC_log = NULL;
1236
1237 /* Initialize GC_log (and the friends) passed to GC_write(). */
GC_set_files(void)1238 STATIC void GC_set_files(void)
1239 {
1240 if (GC_stdout == NULL) {
1241 GC_stdout = stdout;
1242 }
1243 if (GC_stderr == NULL) {
1244 GC_stderr = stderr;
1245 }
1246 if (GC_log == NULL) {
1247 GC_log = stderr;
1248 }
1249 }
1250
GC_write(FILE * f,const char * buf,size_t len)1251 GC_INLINE int GC_write(FILE *f, const char *buf, size_t len)
1252 {
1253 int res = fwrite(buf, 1, len, f);
1254 fflush(f);
1255 return res;
1256 }
1257
1258 # define WRITE(f, buf, len) (GC_set_files(), GC_write(f, buf, len))
1259
1260 #else
1261 # if !defined(AMIGA) && !defined(__CC_ARM)
1262 # include <unistd.h>
1263 # endif
1264
GC_write(int fd,const char * buf,size_t len)1265 STATIC int GC_write(int fd, const char *buf, size_t len)
1266 {
1267 # if defined(ECOS) || defined(NOSYS)
1268 # ifdef ECOS
1269 /* FIXME: This seems to be defined nowhere at present. */
1270 /* _Jv_diag_write(buf, len); */
1271 # else
1272 /* No writing. */
1273 # endif
1274 return len;
1275 # else
1276 int bytes_written = 0;
1277 int result;
1278 IF_CANCEL(int cancel_state;)
1279
1280 DISABLE_CANCEL(cancel_state);
1281 while (bytes_written < len) {
1282 # ifdef GC_SOLARIS_THREADS
1283 result = syscall(SYS_write, fd, buf + bytes_written,
1284 len - bytes_written);
1285 # else
1286 result = write(fd, buf + bytes_written, len - bytes_written);
1287 # endif
1288 if (-1 == result) {
1289 RESTORE_CANCEL(cancel_state);
1290 return(result);
1291 }
1292 bytes_written += result;
1293 }
1294 RESTORE_CANCEL(cancel_state);
1295 return(bytes_written);
1296 # endif
1297 }
1298
1299 # define WRITE(f, buf, len) GC_write(f, buf, len)
1300 #endif /* !MSWIN32 && !OS2 && !MACOS */
1301
1302 #define BUFSZ 1024
1303 #ifdef _MSC_VER
1304 # ifdef MSWINCE
1305 /* _vsnprintf is deprecated in WinCE */
1306 # define vsnprintf StringCchVPrintfA
1307 # else
1308 # define vsnprintf _vsnprintf
1309 # endif
1310 #endif
1311 /* A version of printf that is unlikely to call malloc, and is thus safer */
1312 /* to call from the collector in case malloc has been bound to GC_malloc. */
1313 /* Floating point arguments and formats should be avoided, since fp */
1314 /* conversion is more likely to allocate. */
1315 /* Assumes that no more than BUFSZ-1 characters are written at once. */
GC_printf(const char * format,...)1316 void GC_printf(const char *format, ...)
1317 {
1318 va_list args;
1319 char buf[BUFSZ+1];
1320
1321 if (GC_quiet) return;
1322 va_start(args, format);
1323 buf[BUFSZ] = 0x15;
1324 (void) vsnprintf(buf, BUFSZ, format, args);
1325 va_end(args);
1326 if (buf[BUFSZ] != 0x15) ABORT("GC_printf clobbered stack");
1327 if (WRITE(GC_stdout, buf, strlen(buf)) < 0)
1328 ABORT("write to stdout failed");
1329 }
1330
GC_err_printf(const char * format,...)1331 void GC_err_printf(const char *format, ...)
1332 {
1333 va_list args;
1334 char buf[BUFSZ+1];
1335
1336 va_start(args, format);
1337 buf[BUFSZ] = 0x15;
1338 (void) vsnprintf(buf, BUFSZ, format, args);
1339 va_end(args);
1340 if (buf[BUFSZ] != 0x15) ABORT("GC_printf clobbered stack");
1341 if (WRITE(GC_stderr, buf, strlen(buf)) < 0)
1342 ABORT("write to stderr failed");
1343 }
1344
GC_log_printf(const char * format,...)1345 void GC_log_printf(const char *format, ...)
1346 {
1347 va_list args;
1348 char buf[BUFSZ+1];
1349
1350 va_start(args, format);
1351 buf[BUFSZ] = 0x15;
1352 (void) vsnprintf(buf, BUFSZ, format, args);
1353 va_end(args);
1354 if (buf[BUFSZ] != 0x15) ABORT("GC_printf clobbered stack");
1355 if (WRITE(GC_log, buf, strlen(buf)) < 0)
1356 ABORT("write to log failed");
1357 }
1358
1359 /* This is equivalent to GC_err_printf("%s",s). */
GC_err_puts(const char * s)1360 void GC_err_puts(const char *s)
1361 {
1362 if (WRITE(GC_stderr, s, strlen(s)) < 0) ABORT("write to stderr failed");
1363 }
1364
GC_default_warn_proc(char * msg,GC_word arg)1365 STATIC void GC_CALLBACK GC_default_warn_proc(char *msg, GC_word arg)
1366 {
1367 GC_err_printf(msg, arg);
1368 }
1369
1370 GC_INNER GC_warn_proc GC_current_warn_proc = GC_default_warn_proc;
1371
1372 /* This is recommended for production code (release). */
GC_ignore_warn_proc(char * msg,GC_word arg)1373 GC_API void GC_CALLBACK GC_ignore_warn_proc(char *msg, GC_word arg)
1374 {
1375 if (GC_print_stats) {
1376 /* Don't ignore warnings if stats printing is on. */
1377 GC_default_warn_proc(msg, arg);
1378 }
1379 }
1380
GC_set_warn_proc(GC_warn_proc p)1381 GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc p)
1382 {
1383 DCL_LOCK_STATE;
1384 GC_ASSERT(p != 0);
1385 # ifdef GC_WIN32_THREADS
1386 # ifdef CYGWIN32
1387 /* Need explicit GC_INIT call */
1388 GC_ASSERT(GC_is_initialized);
1389 # else
1390 if (!GC_is_initialized) GC_init();
1391 # endif
1392 # endif
1393 LOCK();
1394 GC_current_warn_proc = p;
1395 UNLOCK();
1396 }
1397
GC_get_warn_proc(void)1398 GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void)
1399 {
1400 GC_warn_proc result;
1401 DCL_LOCK_STATE;
1402 LOCK();
1403 result = GC_current_warn_proc;
1404 UNLOCK();
1405 return(result);
1406 }
1407
1408 #if !defined(PCR) && !defined(SMALL_CONFIG)
1409 /* Abort the program with a message. msg must not be NULL. */
GC_abort(const char * msg)1410 void GC_abort(const char *msg)
1411 {
1412 # if defined(MSWIN32)
1413 # ifndef DONT_USE_USER32_DLL
1414 /* Use static binding to "user32.dll". */
1415 (void)MessageBoxA(NULL, msg, "Fatal error in GC", MB_ICONERROR|MB_OK);
1416 # else
1417 /* This simplifies linking - resolve "MessageBoxA" at run-time. */
1418 HINSTANCE hU32 = LoadLibrary(TEXT("user32.dll"));
1419 if (hU32) {
1420 FARPROC pfn = GetProcAddress(hU32, "MessageBoxA");
1421 if (pfn)
1422 (void)(*(int (WINAPI *)(HWND, LPCSTR, LPCSTR, UINT))pfn)(
1423 NULL /* hWnd */, msg, "Fatal error in GC",
1424 MB_ICONERROR | MB_OK);
1425 (void)FreeLibrary(hU32);
1426 }
1427 # endif
1428 /* Also duplicate msg to GC log file. */
1429 # endif
1430 /* Avoid calling GC_err_printf() here, as GC_abort() could be */
1431 /* called from it. Note 1: this is not an atomic output. */
1432 /* Note 2: possible write errors are ignored. */
1433 if (WRITE(GC_stderr, (void *)msg, strlen(msg)) >= 0)
1434 (void)WRITE(GC_stderr, (void *)("\n"), 1);
1435
1436 if (GETENV("GC_LOOP_ON_ABORT") != NULL) {
1437 /* In many cases it's easier to debug a running process. */
1438 /* It's arguably nicer to sleep, but that makes it harder */
1439 /* to look at the thread if the debugger doesn't know much */
1440 /* about threads. */
1441 for(;;) {}
1442 }
1443 # ifndef LINT2
1444 if (!msg) return; /* to suppress compiler warnings in ABORT callers. */
1445 # endif
1446 # if defined(MSWIN32) && (defined(NO_DEBUGGING) || defined(LINT2))
1447 /* A more user-friendly abort after showing fatal message. */
1448 _exit(-1); /* exit on error without running "at-exit" callbacks */
1449 # elif defined(MSWINCE) && defined(NO_DEBUGGING)
1450 ExitProcess(-1);
1451 # elif defined(MSWIN32) || defined(MSWINCE)
1452 DebugBreak();
1453 /* Note that on a WinCE box, this could be silently */
1454 /* ignored (i.e., the program is not aborted). */
1455 # else
1456 (void) abort();
1457 # endif
1458 }
1459 #endif /* !SMALL_CONFIG */
1460
GC_enable(void)1461 GC_API void GC_CALL GC_enable(void)
1462 {
1463 DCL_LOCK_STATE;
1464 LOCK();
1465 GC_dont_gc--;
1466 UNLOCK();
1467 }
1468
GC_disable(void)1469 GC_API void GC_CALL GC_disable(void)
1470 {
1471 DCL_LOCK_STATE;
1472 LOCK();
1473 GC_dont_gc++;
1474 UNLOCK();
1475 }
1476
1477 /* Helper procedures for new kind creation. */
GC_new_free_list_inner(void)1478 GC_API void ** GC_CALL GC_new_free_list_inner(void)
1479 {
1480 void *result = GC_INTERNAL_MALLOC((MAXOBJGRANULES+1)*sizeof(ptr_t),
1481 PTRFREE);
1482 if (result == 0) ABORT("Failed to allocate freelist for new kind");
1483 BZERO(result, (MAXOBJGRANULES+1)*sizeof(ptr_t));
1484 return result;
1485 }
1486
GC_new_free_list(void)1487 GC_API void ** GC_CALL GC_new_free_list(void)
1488 {
1489 void *result;
1490 DCL_LOCK_STATE;
1491 LOCK();
1492 result = GC_new_free_list_inner();
1493 UNLOCK();
1494 return result;
1495 }
1496
GC_new_kind_inner(void ** fl,GC_word descr,int adjust,int clear)1497 GC_API unsigned GC_CALL GC_new_kind_inner(void **fl, GC_word descr,
1498 int adjust, int clear)
1499 {
1500 unsigned result = GC_n_kinds++;
1501
1502 if (GC_n_kinds > MAXOBJKINDS) ABORT("Too many kinds");
1503 GC_obj_kinds[result].ok_freelist = fl;
1504 GC_obj_kinds[result].ok_reclaim_list = 0;
1505 GC_obj_kinds[result].ok_descriptor = descr;
1506 GC_obj_kinds[result].ok_relocate_descr = adjust;
1507 GC_obj_kinds[result].ok_init = clear;
1508 return result;
1509 }
1510
GC_new_kind(void ** fl,GC_word descr,int adjust,int clear)1511 GC_API unsigned GC_CALL GC_new_kind(void **fl, GC_word descr, int adjust,
1512 int clear)
1513 {
1514 unsigned result;
1515 DCL_LOCK_STATE;
1516 LOCK();
1517 result = GC_new_kind_inner(fl, descr, adjust, clear);
1518 UNLOCK();
1519 return result;
1520 }
1521
GC_new_proc_inner(GC_mark_proc proc)1522 GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc proc)
1523 {
1524 unsigned result = GC_n_mark_procs++;
1525
1526 if (GC_n_mark_procs > MAX_MARK_PROCS) ABORT("Too many mark procedures");
1527 GC_mark_procs[result] = proc;
1528 return result;
1529 }
1530
GC_new_proc(GC_mark_proc proc)1531 GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc proc)
1532 {
1533 unsigned result;
1534 DCL_LOCK_STATE;
1535 LOCK();
1536 result = GC_new_proc_inner(proc);
1537 UNLOCK();
1538 return result;
1539 }
1540
GC_call_with_stack_base(GC_stack_base_func fn,void * arg)1541 GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func fn, void *arg)
1542 {
1543 int dummy;
1544 struct GC_stack_base base;
1545
1546 base.mem_base = (void *)&dummy;
1547 # ifdef IA64
1548 base.reg_base = (void *)GC_save_regs_in_stack();
1549 /* Unnecessarily flushes register stack, */
1550 /* but that probably doesn't hurt. */
1551 # endif
1552 return fn(&base, arg);
1553 }
1554
1555 #ifdef THREADS
1556
1557 /* Defined in pthread_support.c or win32_threads.c. */
1558 GC_INNER void GC_do_blocking_inner(ptr_t data, void * context);
1559
1560 #else
1561
1562 GC_INNER ptr_t GC_blocked_sp = NULL;
1563 /* NULL value means we are not inside GC_do_blocking() call. */
1564 # ifdef IA64
1565 STATIC ptr_t GC_blocked_register_sp = NULL;
1566 # endif
1567
1568 GC_INNER struct GC_traced_stack_sect_s *GC_traced_stack_sect = NULL;
1569
1570 /* This is nearly the same as in win32_threads.c */
GC_call_with_gc_active(GC_fn_type fn,void * client_data)1571 GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
1572 void * client_data)
1573 {
1574 struct GC_traced_stack_sect_s stacksect;
1575 GC_ASSERT(GC_is_initialized);
1576
1577 /* Adjust our stack base value (this could happen if */
1578 /* GC_get_main_stack_base() is unimplemented or broken for */
1579 /* the platform). */
1580 if (GC_stackbottom HOTTER_THAN (ptr_t)(&stacksect))
1581 GC_stackbottom = (ptr_t)(&stacksect);
1582
1583 if (GC_blocked_sp == NULL) {
1584 /* We are not inside GC_do_blocking() - do nothing more. */
1585 return fn(client_data);
1586 }
1587
1588 /* Setup new "stack section". */
1589 stacksect.saved_stack_ptr = GC_blocked_sp;
1590 # ifdef IA64
1591 /* This is the same as in GC_call_with_stack_base(). */
1592 stacksect.backing_store_end = GC_save_regs_in_stack();
1593 /* Unnecessarily flushes register stack, */
1594 /* but that probably doesn't hurt. */
1595 stacksect.saved_backing_store_ptr = GC_blocked_register_sp;
1596 # endif
1597 stacksect.prev = GC_traced_stack_sect;
1598 GC_blocked_sp = NULL;
1599 GC_traced_stack_sect = &stacksect;
1600
1601 client_data = fn(client_data);
1602 GC_ASSERT(GC_blocked_sp == NULL);
1603 GC_ASSERT(GC_traced_stack_sect == &stacksect);
1604
1605 /* Restore original "stack section". */
1606 GC_traced_stack_sect = stacksect.prev;
1607 # ifdef IA64
1608 GC_blocked_register_sp = stacksect.saved_backing_store_ptr;
1609 # endif
1610 GC_blocked_sp = stacksect.saved_stack_ptr;
1611
1612 return client_data; /* result */
1613 }
1614
1615 /* This is nearly the same as in win32_threads.c */
1616 /*ARGSUSED*/
GC_do_blocking_inner(ptr_t data,void * context)1617 STATIC void GC_do_blocking_inner(ptr_t data, void * context)
1618 {
1619 struct blocking_data * d = (struct blocking_data *) data;
1620 GC_ASSERT(GC_is_initialized);
1621 GC_ASSERT(GC_blocked_sp == NULL);
1622 # if defined(SPARC) && !defined(__FreeBSD__)
1623 GC_blocked_sp = GC_save_regs_in_stack();
1624 # else
1625 GC_blocked_sp = (ptr_t) &d; /* save approx. sp */
1626 # endif
1627 # ifdef IA64
1628 GC_blocked_register_sp = GC_save_regs_in_stack();
1629 # endif
1630
1631 d -> client_data = (d -> fn)(d -> client_data);
1632
1633 # ifdef SPARC
1634 GC_ASSERT(GC_blocked_sp != NULL);
1635 # else
1636 GC_ASSERT(GC_blocked_sp == (ptr_t) &d);
1637 # endif
1638 GC_blocked_sp = NULL;
1639 }
1640
1641 #endif /* !THREADS */
1642
1643 /* Wrapper for functions that are likely to block (or, at least, do not */
1644 /* allocate garbage collected memory and/or manipulate pointers to the */
1645 /* garbage collected heap) for an appreciable length of time. */
1646 /* In the single threaded case, GC_do_blocking() (together */
1647 /* with GC_call_with_gc_active()) might be used to make stack scanning */
1648 /* more precise (i.e. scan only stack frames of functions that allocate */
1649 /* garbage collected memory and/or manipulate pointers to the garbage */
1650 /* collected heap). */
GC_do_blocking(GC_fn_type fn,void * client_data)1651 GC_API void * GC_CALL GC_do_blocking(GC_fn_type fn, void * client_data)
1652 {
1653 struct blocking_data my_data;
1654
1655 my_data.fn = fn;
1656 my_data.client_data = client_data;
1657 GC_with_callee_saves_pushed(GC_do_blocking_inner, (ptr_t)(&my_data));
1658 return my_data.client_data; /* result */
1659 }
1660
1661 #if !defined(NO_DEBUGGING)
GC_dump(void)1662 GC_API void GC_CALL GC_dump(void)
1663 {
1664 GC_printf("***Static roots:\n");
1665 GC_print_static_roots();
1666 GC_printf("\n***Heap sections:\n");
1667 GC_print_heap_sects();
1668 GC_printf("\n***Free blocks:\n");
1669 GC_print_hblkfreelist();
1670 GC_printf("\n***Blocks in use:\n");
1671 GC_print_block_list();
1672 }
1673 #endif /* !NO_DEBUGGING */
1674
1675 /* Getter functions for the public Read-only variables. */
1676
1677 /* GC_get_gc_no() is unsynchronized and should be typically called */
1678 /* inside the context of GC_call_with_alloc_lock() to prevent data */
1679 /* races (on multiprocessors). */
GC_get_gc_no(void)1680 GC_API GC_word GC_CALL GC_get_gc_no(void)
1681 {
1682 return GC_gc_no;
1683 }
1684
1685 #ifdef THREADS
GC_get_parallel(void)1686 GC_API int GC_CALL GC_get_parallel(void)
1687 {
1688 /* GC_parallel is initialized at start-up. */
1689 return GC_parallel;
1690 }
1691 #endif
1692
1693 /* Setter and getter functions for the public R/W function variables. */
1694 /* These functions are synchronized (like GC_set_warn_proc() and */
1695 /* GC_get_warn_proc()). */
1696
GC_set_oom_fn(GC_oom_func fn)1697 GC_API void GC_CALL GC_set_oom_fn(GC_oom_func fn)
1698 {
1699 GC_ASSERT(fn != 0);
1700 DCL_LOCK_STATE;
1701 LOCK();
1702 GC_oom_fn = fn;
1703 UNLOCK();
1704 }
1705
GC_get_oom_fn(void)1706 GC_API GC_oom_func GC_CALL GC_get_oom_fn(void)
1707 {
1708 GC_oom_func fn;
1709 DCL_LOCK_STATE;
1710 LOCK();
1711 fn = GC_oom_fn;
1712 UNLOCK();
1713 return fn;
1714 }
1715
GC_set_finalizer_notifier(GC_finalizer_notifier_proc fn)1716 GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc fn)
1717 {
1718 /* fn may be 0 (means no finalizer notifier). */
1719 DCL_LOCK_STATE;
1720 LOCK();
1721 GC_finalizer_notifier = fn;
1722 UNLOCK();
1723 }
1724
GC_get_finalizer_notifier(void)1725 GC_API GC_finalizer_notifier_proc GC_CALL GC_get_finalizer_notifier(void)
1726 {
1727 GC_finalizer_notifier_proc fn;
1728 DCL_LOCK_STATE;
1729 LOCK();
1730 fn = GC_finalizer_notifier;
1731 UNLOCK();
1732 return fn;
1733 }
1734
1735 /* Setter and getter functions for the public numeric R/W variables. */
1736 /* It is safe to call these functions even before GC_INIT(). */
1737 /* These functions are unsynchronized and should be typically called */
1738 /* inside the context of GC_call_with_alloc_lock() (if called after */
1739 /* GC_INIT()) to prevent data races (unless it is guaranteed the */
1740 /* collector is not multi-threaded at that execution point). */
1741
GC_set_find_leak(int value)1742 GC_API void GC_CALL GC_set_find_leak(int value)
1743 {
1744 /* value is of boolean type. */
1745 GC_find_leak = value;
1746 }
1747
GC_get_find_leak(void)1748 GC_API int GC_CALL GC_get_find_leak(void)
1749 {
1750 return GC_find_leak;
1751 }
1752
1753 GC_INNER void GC_bl_init_no_interiors(void); /* defined in blacklst.c */
1754
GC_set_all_interior_pointers(int value)1755 GC_API void GC_CALL GC_set_all_interior_pointers(int value)
1756 {
1757 DCL_LOCK_STATE;
1758
1759 GC_all_interior_pointers = value ? 1 : 0;
1760 if (GC_is_initialized) {
1761 /* It is not recommended to change GC_all_interior_pointers value */
1762 /* after GC is initialized but it seems GC could work correctly */
1763 /* even after switching the mode. */
1764 LOCK();
1765 GC_initialize_offsets(); /* NOTE: this resets manual offsets as well */
1766 if (!GC_all_interior_pointers)
1767 GC_bl_init_no_interiors();
1768 UNLOCK();
1769 }
1770 }
1771
GC_get_all_interior_pointers(void)1772 GC_API int GC_CALL GC_get_all_interior_pointers(void)
1773 {
1774 return GC_all_interior_pointers;
1775 }
1776
GC_set_finalize_on_demand(int value)1777 GC_API void GC_CALL GC_set_finalize_on_demand(int value)
1778 {
1779 GC_ASSERT(value != -1);
1780 /* value is of boolean type. */
1781 GC_finalize_on_demand = value;
1782 }
1783
GC_get_finalize_on_demand(void)1784 GC_API int GC_CALL GC_get_finalize_on_demand(void)
1785 {
1786 return GC_finalize_on_demand;
1787 }
1788
GC_set_java_finalization(int value)1789 GC_API void GC_CALL GC_set_java_finalization(int value)
1790 {
1791 GC_ASSERT(value != -1);
1792 /* value is of boolean type. */
1793 GC_java_finalization = value;
1794 }
1795
GC_get_java_finalization(void)1796 GC_API int GC_CALL GC_get_java_finalization(void)
1797 {
1798 return GC_java_finalization;
1799 }
1800
GC_set_dont_expand(int value)1801 GC_API void GC_CALL GC_set_dont_expand(int value)
1802 {
1803 GC_ASSERT(value != -1);
1804 /* value is of boolean type. */
1805 GC_dont_expand = value;
1806 }
1807
GC_get_dont_expand(void)1808 GC_API int GC_CALL GC_get_dont_expand(void)
1809 {
1810 return GC_dont_expand;
1811 }
1812
GC_set_no_dls(int value)1813 GC_API void GC_CALL GC_set_no_dls(int value)
1814 {
1815 GC_ASSERT(value != -1);
1816 /* value is of boolean type. */
1817 GC_no_dls = value;
1818 }
1819
GC_get_no_dls(void)1820 GC_API int GC_CALL GC_get_no_dls(void)
1821 {
1822 return GC_no_dls;
1823 }
1824
GC_set_non_gc_bytes(GC_word value)1825 GC_API void GC_CALL GC_set_non_gc_bytes(GC_word value)
1826 {
1827 GC_non_gc_bytes = value;
1828 }
1829
GC_get_non_gc_bytes(void)1830 GC_API GC_word GC_CALL GC_get_non_gc_bytes(void)
1831 {
1832 return GC_non_gc_bytes;
1833 }
1834
GC_set_free_space_divisor(GC_word value)1835 GC_API void GC_CALL GC_set_free_space_divisor(GC_word value)
1836 {
1837 GC_ASSERT(value > 0);
1838 GC_free_space_divisor = value;
1839 }
1840
GC_get_free_space_divisor(void)1841 GC_API GC_word GC_CALL GC_get_free_space_divisor(void)
1842 {
1843 return GC_free_space_divisor;
1844 }
1845
GC_set_max_retries(GC_word value)1846 GC_API void GC_CALL GC_set_max_retries(GC_word value)
1847 {
1848 GC_ASSERT(value != ~(GC_word)0);
1849 GC_max_retries = value;
1850 }
1851
GC_get_max_retries(void)1852 GC_API GC_word GC_CALL GC_get_max_retries(void)
1853 {
1854 return GC_max_retries;
1855 }
1856
GC_set_dont_precollect(int value)1857 GC_API void GC_CALL GC_set_dont_precollect(int value)
1858 {
1859 GC_ASSERT(value != -1);
1860 /* value is of boolean type. */
1861 GC_dont_precollect = value;
1862 }
1863
GC_get_dont_precollect(void)1864 GC_API int GC_CALL GC_get_dont_precollect(void)
1865 {
1866 return GC_dont_precollect;
1867 }
1868
GC_set_full_freq(int value)1869 GC_API void GC_CALL GC_set_full_freq(int value)
1870 {
1871 GC_ASSERT(value >= 0);
1872 GC_full_freq = value;
1873 }
1874
GC_get_full_freq(void)1875 GC_API int GC_CALL GC_get_full_freq(void)
1876 {
1877 return GC_full_freq;
1878 }
1879
GC_set_time_limit(unsigned long value)1880 GC_API void GC_CALL GC_set_time_limit(unsigned long value)
1881 {
1882 GC_ASSERT(value != (unsigned long)-1L);
1883 GC_time_limit = value;
1884 }
1885
GC_get_time_limit(void)1886 GC_API unsigned long GC_CALL GC_get_time_limit(void)
1887 {
1888 return GC_time_limit;
1889 }
1890
GC_set_force_unmap_on_gcollect(int value)1891 GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int value)
1892 {
1893 GC_force_unmap_on_gcollect = (GC_bool)value;
1894 }
1895
GC_get_force_unmap_on_gcollect(void)1896 GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void)
1897 {
1898 return (int)GC_force_unmap_on_gcollect;
1899 }
1900