LCOV - code coverage report
Current view: top level - mm/boehm-gc - misc.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 139 530 26.2 %
Date: 2017-07-14 10:03:36 Functions: 11 83 13.3 %

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

Generated by: LCOV version 1.11