OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / boehm-gc / misc.c
index 8ece80d..069c7d5 100644 (file)
@@ -1,6 +1,7 @@
 /* 
  * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
  * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1999-2001 by Hewlett-Packard Company. All rights reserved.
  *
  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
@@ -15,6 +16,7 @@
 
 
 #include <stdio.h>
+#include <limits.h>
 #ifndef _WIN32_WCE
 #include <signal.h>
 #endif
@@ -22,7 +24,7 @@
 #define I_HIDE_POINTERS        /* To make GC_call_with_alloc_lock visible */
 #include "private/gc_pmark.h"
 
-#ifdef SOLARIS_THREADS
+#ifdef GC_SOLARIS_THREADS
 # include <sys/syscall.h>
 #endif
 #if defined(MSWIN32) || defined(MSWINCE)
        /* Critical section counter is defined in the M3 runtime        */
        /* That's all we use.                                           */
 #     else
-#      ifdef SOLARIS_THREADS
+#      ifdef GC_SOLARIS_THREADS
          mutex_t GC_allocate_ml;       /* Implicitly initialized.      */
 #      else
-#          ifdef WIN32_THREADS
-#            if defined(_DLL) || defined(GC_DLL)
+#          if defined(GC_WIN32_THREADS) 
+#             if defined(GC_PTHREADS)
+                 pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
+#            elif defined(GC_DLL)
                 __declspec(dllexport) CRITICAL_SECTION GC_allocate_ml;
 #            else
                 CRITICAL_SECTION GC_allocate_ml;
 #            endif
 #          else
-#             if defined(IRIX_THREADS) \
-                || (defined(LINUX_THREADS) && defined(USE_SPIN_LOCK))
-               pthread_t GC_lock_holder = NO_THREAD;
-#            else
-#              if defined(HPUX_THREADS) \
-                  || defined(LINUX_THREADS) && !defined(USE_SPIN_LOCK)
+#             if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS)
+#              if defined(USE_SPIN_LOCK)
+                 pthread_t GC_lock_holder = NO_THREAD;
+#              else
                  pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
                  pthread_t GC_lock_holder = NO_THREAD;
                        /* Used only for assertions, and to prevent      */
                        /* recursive reentry in the system call wrapper. */
-#              else 
+#              endif 
+#            else
                  --> declare allocator lock here
-#              endif
 #            endif
 #         endif
 #      endif
 #   endif
 # endif
 
-#ifdef ECOS
+#if defined(NOSYS) || defined(ECOS)
 #undef STACKBASE
 #endif
 
+/* Dont unnecessarily call GC_register_main_static_data() in case      */
+/* dyn_load.c isn't linked in.                                         */
+#ifdef DYNAMIC_LOADING
+# define GC_REGISTER_MAIN_STATIC_DATA() GC_register_main_static_data()
+#else
+# define GC_REGISTER_MAIN_STATIC_DATA() TRUE
+#endif
+
 GC_FAR struct _GC_arrays GC_arrays /* = { 0 } */;
 
 
@@ -82,6 +92,7 @@ GC_bool GC_debugging_started = FALSE;
        /* defined here so we don't have to load debug_malloc.o */
 
 void (*GC_check_heap) GC_PROTO((void)) = (void (*) GC_PROTO((void)))0;
+void (*GC_print_all_smashed) GC_PROTO((void)) = (void (*) GC_PROTO((void)))0;
 
 void (*GC_start_call_back) GC_PROTO((void)) = (void (*) GC_PROTO((void)))0;
 
@@ -99,6 +110,17 @@ GC_bool GC_quiet = 0;
 
 GC_bool GC_print_stats = 0;
 
+GC_bool GC_print_back_height = 0;
+
+#ifndef NO_DEBUGGING
+  GC_bool GC_dump_regularly = 0;  /* Generate regular debugging dumps. */
+#endif
+
+#ifdef KEEP_BACK_PTRS
+  long GC_backtraces = 0;      /* Number of random backtraces to       */
+                               /* generate for each GC.                */
+#endif
+
 #ifdef FIND_LEAK
   int GC_find_leak = 1;
 #else
@@ -111,6 +133,12 @@ GC_bool GC_print_stats = 0;
   int GC_all_interior_pointers = 0;
 #endif
 
+long GC_large_alloc_warn_interval = 5;
+       /* Interval between unsuppressed warnings.      */
+
+long GC_large_alloc_warn_suppressed = 0;
+       /* Number of warnings suppressed so far.        */
+
 /*ARGSUSED*/
 GC_PTR GC_default_oom_fn GC_PROTO((size_t bytes_requested))
 {
@@ -121,6 +149,13 @@ GC_PTR (*GC_oom_fn) GC_PROTO((size_t bytes_requested)) = GC_default_oom_fn;
 
 extern signed_word GC_mem_found;
 
+void * GC_project2(arg1, arg2)
+void *arg1;
+void *arg2;
+{
+  return arg2;
+}
+
 # ifdef MERGE_SIZES
     /* Set things up so that GC_size_map[i] >= words(i),               */
     /* but not too much bigger                                         */
@@ -211,7 +246,7 @@ extern signed_word GC_mem_found;
        byte_sz = WORDS_TO_BYTES(word_sz);
        if (GC_all_interior_pointers) {
            /* We need one extra byte; don't fill in GC_size_map[byte_sz] */
-           byte_sz--;
+           byte_sz -= EXTRA_BYTES;
        }
 
        for (j = low_limit; j <= byte_sz; j++) GC_size_map[j] = word_sz;  
@@ -301,6 +336,8 @@ ptr_t arg;
     if (++random_no % 13 == 0) {
        limit = sp;
        MAKE_HOTTER(limit, BIG_CLEAR_SIZE*sizeof(word));
+        limit &= ~0xf; /* Make it sufficiently aligned for assembly    */
+                       /* implementations of GC_clear_stack_inner.     */
        return GC_clear_stack_inner(arg, limit);
     } else {
        BZERO(dummy, SMALL_CLEAR_SIZE*sizeof(word));
@@ -436,11 +473,45 @@ void GC_init()
     DCL_LOCK_STATE;
     
     DISABLE_SIGNALS();
+
+#if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS)
+    if (!GC_is_initialized) {
+      BOOL (WINAPI *pfn) (LPCRITICAL_SECTION, DWORD) = NULL;
+      HMODULE hK32 = GetModuleHandle("kernel32.dll");
+      if (hK32)
+         pfn = (BOOL (WINAPI *) (LPCRITICAL_SECTION, DWORD))
+               GetProcAddress (hK32,
+                               "InitializeCriticalSectionAndSpinCount");
+      if (pfn)
+          pfn(&GC_allocate_ml, 4000);
+      else
+         InitializeCriticalSection (&GC_allocate_ml);
+    }
+#endif /* MSWIN32 */
+
     LOCK();
     GC_init_inner();
     UNLOCK();
     ENABLE_SIGNALS();
 
+#   if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
+       /* Make sure marker threads and started and thread local */
+       /* allocation is initialized, in case we didn't get      */
+       /* called from GC_init_parallel();                       */
+        {
+         extern void GC_init_parallel(void);
+         GC_init_parallel();
+       }
+#   endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */
+
+#   if defined(DYNAMIC_LOADING) && defined(DARWIN)
+    {
+        /* This must be called WITHOUT the allocation lock held
+        and before any threads are created */
+        extern void GC_init_dyld();
+        GC_init_dyld();
+    }
+#   endif
 }
 
 #if defined(MSWIN32) || defined(MSWINCE)
@@ -453,6 +524,22 @@ void GC_init()
 
 extern void GC_setpagesize();
 
+
+#ifdef MSWIN32
+extern GC_bool GC_no_win32_dlls;
+#else
+# define GC_no_win32_dlls FALSE
+#endif
+
+void GC_exit_check GC_PROTO((void))
+{
+   GC_gcollect();
+}
+
+#ifdef SEARCH_FOR_DATA_START
+  extern void GC_init_linux_data_start GC_PROTO((void));
+#endif
+
 #ifdef UNIX_LIKE
 
 extern void GC_set_and_save_fault_handler GC_PROTO((void (*handler)(int)));
@@ -463,6 +550,23 @@ int sig;
     GC_err_printf1("Caught signal %d: looping in handler\n", sig);
     for(;;);
 }
+
+static GC_bool installed_looping_handler = FALSE;
+
+static void maybe_install_looping_handler()
+{
+    /* Install looping handler before the write fault handler, so we   */
+    /* handle write faults correctly.                                  */
+      if (!installed_looping_handler && 0 != GETENV("GC_LOOP_ON_ABORT")) {
+        GC_set_and_save_fault_handler(looping_handler);
+        installed_looping_handler = TRUE;
+      }
+}
+
+#else /* !UNIX_LIKE */
+
+# define maybe_install_looping_handler()
+
 #endif
 
 void GC_init_inner()
@@ -476,11 +580,31 @@ void GC_init_inner()
 #   ifdef PRINTSTATS
       GC_print_stats = 1;
 #   endif
+#   if defined(MSWIN32) || defined(MSWINCE)
+      InitializeCriticalSection(&GC_write_cs);
+#   endif
     if (0 != GETENV("GC_PRINT_STATS")) {
       GC_print_stats = 1;
     } 
+#   ifndef NO_DEBUGGING
+      if (0 != GETENV("GC_DUMP_REGULARLY")) {
+        GC_dump_regularly = 1;
+      }
+#   endif
+#   ifdef KEEP_BACK_PTRS
+      {
+        char * backtraces_string = GETENV("GC_BACKTRACES");
+        if (0 != backtraces_string) {
+          GC_backtraces = atol(backtraces_string);
+         if (backtraces_string[0] == '\0') GC_backtraces = 1;
+        }
+      }
+#   endif
     if (0 != GETENV("GC_FIND_LEAK")) {
       GC_find_leak = 1;
+#     ifdef __STDC__
+        atexit(GC_exit_check);
+#     endif
     }
     if (0 != GETENV("GC_ALL_INTERIOR_POINTERS")) {
       GC_all_interior_pointers = 1;
@@ -488,18 +612,41 @@ void GC_init_inner()
     if (0 != GETENV("GC_DONT_GC")) {
       GC_dont_gc = 1;
     }
-#   ifdef UNIX_LIKE
-      if (0 != GETENV("GC_LOOP_ON_ABORT")) {
-        GC_set_and_save_fault_handler(looping_handler);
+    if (0 != GETENV("GC_PRINT_BACK_HEIGHT")) {
+      GC_print_back_height = 1;
+    }
+    if (0 != GETENV("GC_NO_BLACKLIST_WARNING")) {
+      GC_large_alloc_warn_interval = LONG_MAX;
+    }
+    {
+      char * time_limit_string = GETENV("GC_PAUSE_TIME_TARGET");
+      if (0 != time_limit_string) {
+        long time_limit = atol(time_limit_string);
+        if (time_limit < 5) {
+         WARN("GC_PAUSE_TIME_TARGET environment variable value too small "
+              "or bad syntax: Ignoring\n", 0);
+        } else {
+         GC_time_limit = time_limit;
+        }
       }
-#   endif
+    }
+    {
+      char * interval_string = GETENV("GC_LARGE_ALLOC_WARN_INTERVAL");
+      if (0 != interval_string) {
+        long interval = atol(interval_string);
+        if (interval <= 0) {
+         WARN("GC_LARGE_ALLOC_WARN_INTERVAL environment variable has "
+              "bad value: Ignoring\n", 0);
+        } else {
+         GC_large_alloc_warn_interval = interval;
+        }
+      }
+    }
+    maybe_install_looping_handler();
     /* Adjust normal object descriptor for extra allocation.   */
     if (ALIGNMENT > GC_DS_TAGS && EXTRA_BYTES != 0) {
       GC_obj_kinds[NORMAL].ok_descriptor = ((word)(-ALIGNMENT) | GC_DS_LENGTH);
     }
-#   if defined(MSWIN32) || defined(MSWINCE)
-       InitializeCriticalSection(&GC_write_cs);
-#   endif
     GC_setpagesize();
     GC_exclude_static_roots(beginGC_arrays, endGC_arrays);
     GC_exclude_static_roots(beginGC_obj_kinds, endGC_obj_kinds);
@@ -513,30 +660,45 @@ void GC_init_inner()
 #   if defined(SEARCH_FOR_DATA_START)
        GC_init_linux_data_start();
 #   endif
-#   if defined(NETBSD) && defined(__ELF__)
+#   if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__)
        GC_init_netbsd_elf();
 #   endif
-#   if defined(IRIX_THREADS) || defined(LINUX_THREADS) \
-       || defined(HPUX_THREADS) || defined(SOLARIS_THREADS)
+#   if defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS) \
+       || defined(GC_WIN32_THREADS)
         GC_thr_init();
 #   endif
-#   ifdef SOLARIS_THREADS
+#   ifdef GC_SOLARIS_THREADS
        /* We need dirty bits in order to find live stack sections.     */
         GC_dirty_init();
 #   endif
-#   if !defined(THREADS) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \
-       || defined(IRIX_THREADS) || defined(LINUX_THREADS) \
-       || defined(HPUX_THREADS)
+#   if !defined(THREADS) || defined(GC_PTHREADS) || defined(GC_WIN32_THREADS) \
+       || defined(GC_SOLARIS_THREADS)
       if (GC_stackbottom == 0) {
-       GC_stackbottom = GC_get_stack_base();
-#       if defined(LINUX) && defined(IA64)
+        # if defined(GC_PTHREADS) && ! defined(GC_SOLARIS_THREADS)
+       /* Use thread_stack_base if available, as GC could be initialized from
+          a thread that is not the "main" thread.  */
+       GC_stackbottom = GC_get_thread_stack_base();
+       # endif
+       if (GC_stackbottom == 0)
+         GC_stackbottom = GC_get_stack_base();
+#       if (defined(LINUX) || defined(HPUX)) && defined(IA64)
          GC_register_stackbottom = GC_get_register_stack_base();
 #       endif
+      } else {
+#       if (defined(LINUX) || defined(HPUX)) && defined(IA64)
+         if (GC_register_stackbottom == 0) {
+           WARN("GC_register_stackbottom should be set with GC_stackbottom", 0);
+           /* The following may fail, since we may rely on             */
+           /* alignment properties that may not hold with a user set   */
+           /* GC_stackbottom.                                          */
+           GC_register_stackbottom = GC_get_register_stack_base();
+         }
+#      endif
       }
 #   endif
-    GC_ASSERT(sizeof (ptr_t) == sizeof(word));
-    GC_ASSERT(sizeof (signed_word) == sizeof(word));
-    GC_ASSERT(sizeof (struct hblk) == HBLKSIZE);
+    GC_STATIC_ASSERT(sizeof (ptr_t) == sizeof(word));
+    GC_STATIC_ASSERT(sizeof (signed_word) == sizeof(word));
+    GC_STATIC_ASSERT(sizeof (struct hblk) == HBLKSIZE);
 #   ifndef THREADS
 #     if defined(STACK_GROWS_UP) && defined(STACK_GROWS_DOWN)
        ABORT(
@@ -560,7 +722,7 @@ void GC_init_inner()
     
     /* Add initial guess of root sets.  Do this first, since sbrk(0)   */
     /* might be used.                                                  */
-      GC_register_data_segments();
+      if (GC_REGISTER_MAIN_STATIC_DATA()) GC_register_data_segments();
     GC_init_headers();
     GC_bl_init();
     GC_mark_init();
@@ -575,6 +737,18 @@ void GC_init_inner()
          initial_heap_sz = divHBLKSZ(initial_heap_sz);
        }
     }
+    {
+       char * sz_str = GETENV("GC_MAXIMUM_HEAP_SIZE");
+       if (sz_str != NULL) {
+         word max_heap_sz = (word)atol(sz_str);
+         if (max_heap_sz < initial_heap_sz * HBLKSIZE) {
+           WARN("Bad maximum heap size %s - ignoring it.\n",
+                sz_str);
+         } 
+         if (0 == GC_max_retries) GC_max_retries = 2;
+         GC_set_max_heap_size(max_heap_sz);
+       }
+    }
     if (!GC_expand_hp_inner(initial_heap_sz)) {
         GC_err_printf0("Can't start up: not enough memory\n");
         EXIT();
@@ -599,8 +773,20 @@ void GC_init_inner()
       PCR_IL_Unlock();
       GC_pcr_install();
 #   endif
-    /* Get black list set up */
-      if (!GC_dont_precollect) GC_gcollect_inner();
+#   if !defined(SMALL_CONFIG)
+      if (!GC_no_win32_dlls && 0 != GETENV("GC_ENABLE_INCREMENTAL")) {
+       GC_ASSERT(!GC_incremental);
+        GC_setpagesize();
+#       ifndef GC_SOLARIS_THREADS
+          GC_dirty_init();
+#       endif
+        GC_ASSERT(GC_words_allocd == 0)
+       GC_incremental = TRUE;
+      }
+#   endif /* !SMALL_CONFIG */
+    COND_DUMP;
+    /* Get black list set up and/or incremental GC started */
+      if (!GC_dont_precollect || GC_incremental) GC_gcollect_inner();
     GC_is_initialized = TRUE;
 #   ifdef STUBBORN_ALLOC
        GC_stubborn_init();
@@ -625,7 +811,10 @@ void GC_init_inner()
 
 void GC_enable_incremental GC_PROTO(())
 {
-# if !defined(SMALL_CONFIG)
+# if !defined(SMALL_CONFIG) && !defined(KEEP_BACK_PTRS)
+  /* If we are keeping back pointers, the GC itself dirties all        */
+  /* pages on which objects have been marked, making           */
+  /* incremental GC pointless.                                 */
   if (!GC_find_leak) {
     DCL_LOCK_STATE;
     
@@ -633,20 +822,15 @@ void GC_enable_incremental GC_PROTO(())
     LOCK();
     if (GC_incremental) goto out;
     GC_setpagesize();
-#   ifdef MSWIN32
-      {
-        extern GC_bool GC_is_win32s();
-
-       /* VirtualProtect is not functional under win32s.       */
-       if (GC_is_win32s()) goto out;
-      }
-#   endif /* MSWIN32 */
-#   ifndef SOLARIS_THREADS
-        GC_dirty_init();
+    if (GC_no_win32_dlls) goto out;
+#   ifndef GC_SOLARIS_THREADS 
+      maybe_install_looping_handler();  /* Before write fault handler! */
+      GC_dirty_init();
 #   endif
     if (!GC_is_initialized) {
         GC_init_inner();
     }
+    if (GC_incremental) goto out;
     if (GC_dont_gc) {
         /* Can't easily do it. */
         UNLOCK();
@@ -682,7 +866,7 @@ out:
   }
 
   int GC_write(buf, len)
-  char * buf;
+  GC_CONST char * buf;
   size_t len;
   {
       BOOL tmp;
@@ -732,17 +916,18 @@ int GC_tmp;  /* Should really be local ... */
 # endif
 #endif
 
-#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(OS2) && !defined(MACOS)
+#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(OS2) \
+    && !defined(MACOS)  && !defined(ECOS) && !defined(NOSYS)
 int GC_write(fd, buf, len)
 int fd;
-char *buf;
+GC_CONST char *buf;
 size_t len;
 {
      register int bytes_written = 0;
      register int result;
      
      while (bytes_written < len) {
-#      ifdef SOLARIS_THREADS
+#      ifdef GC_SOLARIS_THREADS
            result = syscall(SYS_write, fd, buf + bytes_written,
                                            len - bytes_written);
 #      else
@@ -755,7 +940,7 @@ size_t len;
 }
 #endif /* UN*X */
 
-#if defined(ECOS)
+#ifdef ECOS
 int GC_write(fd, buf, len)
 {
   _Jv_diag_write (buf, len);
@@ -763,6 +948,14 @@ int GC_write(fd, buf, len)
 }
 #endif
 
+#ifdef NOSYS
+int GC_write(fd, buf, len)
+{
+  /* No writing.  */
+  return len;
+}
+#endif
+
 
 #if defined(MSWIN32) || defined(MSWINCE)
 #   define WRITE(f, buf, len) GC_write(buf, len)
@@ -844,6 +1037,9 @@ GC_warn_proc GC_current_warn_proc = GC_default_warn_proc;
 {
     GC_warn_proc result;
 
+#   ifdef GC_WIN32_THREADS
+      GC_ASSERT(GC_is_initialized);
+#   endif
     LOCK();
     result = GC_current_warn_proc;
     GC_current_warn_proc = p;
@@ -851,6 +1047,17 @@ GC_warn_proc GC_current_warn_proc = GC_default_warn_proc;
     return(result);
 }
 
+# if defined(__STDC__) || defined(__cplusplus)
+    GC_word GC_set_free_space_divisor (GC_word value)
+# else
+    GC_word GC_set_free_space_divisor (value)
+    GC_word value;
+# endif
+{
+    GC_word old = GC_free_space_divisor;
+    GC_free_space_divisor = value;
+    return old;
+}
 
 #ifndef PCR
 void GC_abort(msg)
@@ -858,7 +1065,6 @@ GC_CONST char * msg;
 {
 #   if defined(MSWIN32)
       (void) MessageBoxA(NULL, msg, "Fatal error in gc", MB_ICONERROR|MB_OK);
-      DebugBreak();
 #   else
       GC_err_printf1("%s\n", msg);
 #   endif
@@ -867,9 +1073,9 @@ GC_CONST char * msg;
            /* It's arguably nicer to sleep, but that makes it harder   */
            /* to look at the thread if the debugger doesn't know much  */
            /* about threads.                                           */
-           for(;;);
+           for(;;) {}
     }
-#   ifdef MSWIN32
+#   if defined(MSWIN32) || defined(MSWINCE)
        DebugBreak();
 #   else
         (void) abort();
@@ -877,51 +1083,89 @@ GC_CONST char * msg;
 }
 #endif
 
-#ifdef NEED_CALLINFO
+void GC_enable()
+{
+    LOCK();
+    GC_dont_gc--;
+    UNLOCK();
+}
 
-void GC_print_callers (info)
-struct callinfo info[NFRAMES];
+void GC_disable()
 {
-    register int i;
-    
-#   if NFRAMES == 1
-      GC_err_printf0("\tCaller at allocation:\n");
-#   else
-      GC_err_printf0("\tCall chain at allocation:\n");
-#   endif
-    for (i = 0; i < NFRAMES; i++) {
-       if (info[i].ci_pc == 0) break;
-#      if NARGS > 0
-       {
-         int j;
-
-         GC_err_printf0("\t\targs: ");
-         for (j = 0; j < NARGS; j++) {
-           if (j != 0) GC_err_printf0(", ");
-           GC_err_printf2("%d (0x%X)", ~(info[i].ci_arg[j]),
-                                       ~(info[i].ci_arg[j]));
-         }
-         GC_err_printf0("\n");
-       }
-#      endif
-       GC_err_printf1("\t\t##PC##= 0x%X\n", info[i].ci_pc);
-    }
+    LOCK();
+    GC_dont_gc++;
+    UNLOCK();
 }
 
-#endif /* SAVE_CALL_CHAIN */
+/* Helper procedures for new kind creation.    */
+void ** GC_new_free_list_inner()
+{
+    void *result = GC_INTERNAL_MALLOC((MAXOBJSZ+1)*sizeof(ptr_t), PTRFREE);
+    if (result == 0) ABORT("Failed to allocate freelist for new kind");
+    BZERO(result, (MAXOBJSZ+1)*sizeof(ptr_t));
+    return result;
+}
 
-/* Needed by SRC_M3, gcj, and should perhaps be the official interface */
-/* to GC_dont_gc.                                                      */
-void GC_enable()
+void ** GC_new_free_list()
 {
-    GC_dont_gc--;
+    void *result;
+    LOCK(); DISABLE_SIGNALS();
+    result = GC_new_free_list_inner();
+    UNLOCK(); ENABLE_SIGNALS();
+    return result;
 }
 
-void GC_disable()
+int GC_new_kind_inner(fl, descr, adjust, clear)
+void **fl;
+GC_word descr;
+int adjust;
+int clear;
 {
-    GC_dont_gc++;
+    int result = GC_n_kinds++;
+
+    if (GC_n_kinds > MAXOBJKINDS) ABORT("Too many kinds");
+    GC_obj_kinds[result].ok_freelist = (ptr_t *)fl;
+    GC_obj_kinds[result].ok_reclaim_list = 0;
+    GC_obj_kinds[result].ok_descriptor = descr;
+    GC_obj_kinds[result].ok_relocate_descr = adjust;
+    GC_obj_kinds[result].ok_init = clear;
+    return result;
+}
+
+int GC_new_kind(fl, descr, adjust, clear)
+void **fl;
+GC_word descr;
+int adjust;
+int clear;
+{
+    int result;
+    LOCK(); DISABLE_SIGNALS();
+    result = GC_new_kind_inner(fl, descr, adjust, clear);
+    UNLOCK(); ENABLE_SIGNALS();
+    return result;
 }
 
+int GC_new_proc_inner(proc)
+GC_mark_proc proc;
+{
+    int result = GC_n_mark_procs++;
+
+    if (GC_n_mark_procs > MAX_MARK_PROCS) ABORT("Too many mark procedures");
+    GC_mark_procs[result] = proc;
+    return result;
+}
+
+int GC_new_proc(proc)
+GC_mark_proc proc;
+{
+    int result;
+    LOCK(); DISABLE_SIGNALS();
+    result = GC_new_proc_inner(proc);
+    UNLOCK(); ENABLE_SIGNALS();
+    return result;
+}
+
+
 #if !defined(NO_DEBUGGING)
 
 void GC_dump()
@@ -934,6 +1178,8 @@ void GC_dump()
     GC_print_hblkfreelist();
     GC_printf0("\n***Blocks in use:\n");
     GC_print_block_list();
+    GC_printf0("\n***Finalization statistics:\n");
+    GC_print_finalization_stats();
 }
 
 #endif /* NO_DEBUGGING */