* alloc.c (check_depth): New variable.
[bpt/emacs.git] / src / alloc.c
index 0ea3891..143f5c7 100644 (file)
@@ -99,7 +99,7 @@ extern __malloc_size_t __malloc_extra_blocks;
    If Emacs sets malloc hooks (! SYSTEM_MALLOC) and the emacs_blocked_*
    functions below are called from malloc, there is a chance that one
    of these threads preempts the Emacs main thread and the hook variables
-   end up in a inconsistent state.  So we have a mutex to prevent that (note
+   end up in an inconsistent state.  So we have a mutex to prevent that (note
    that the backend handles concurrent access to malloc within its own threads
    but Emacs code running in the main thread is not included in that control).
 
@@ -109,7 +109,6 @@ extern __malloc_size_t __malloc_extra_blocks;
    To prevent that, we only call BLOCK/UNBLOCK from the main thread.  */
 
 static pthread_mutex_t alloc_mutex;
-pthread_t main_thread;
 
 #define BLOCK_INPUT_ALLOC                       \
   do                                            \
@@ -201,12 +200,6 @@ extern
 #endif /* VIRT_ADDR_VARIES */
 int malloc_sbrk_unused;
 
-/* Two limits controlling how much undo information to keep.  */
-
-EMACS_INT undo_limit;
-EMACS_INT undo_strong_limit;
-EMACS_INT undo_outer_limit;
-
 /* Number of live and free conses etc.  */
 
 static int total_conses, total_markers, total_symbols, total_vector_size;
@@ -608,6 +601,25 @@ static char xmalloc_overrun_check_trailer[XMALLOC_OVERRUN_CHECK_SIZE] =
           ((unsigned)(ptr[-4]) << 24))
 
 
+/* The call depth in overrun_check  functions.  Realloc may call both malloc
+   and free.  If realloc calls malloc, this may happen:
+   overrun_check_realloc()
+      -> malloc -> (via hook)_-> emacs_blocked_malloc
+         -> overrun_check_malloc
+            call malloc  (hooks are NULL, so real malloc is called).
+            malloc returns 10000.
+            add overhead, return 10016.
+      <- (back in overrun_check_realloc)
+      add overhead again, return 10032
+
+   (time passes).
+
+   overrun_check_free(10032)
+     decrease overhed
+     free(10016)  <-  crash, because 10000 is the original pointer.  */
+
+static int check_depth;
+
 /* Like malloc, but wraps allocated block with header and trailer.  */
 
 POINTER_TYPE *
@@ -615,15 +627,17 @@ overrun_check_malloc (size)
      size_t size;
 {
   register unsigned char *val;
+  size_t overhead = ++check_depth == 1 ? XMALLOC_OVERRUN_CHECK_SIZE*2 : 0;
 
-  val = (unsigned char *) malloc (size + XMALLOC_OVERRUN_CHECK_SIZE*2);
-  if (val)
+  val = (unsigned char *) malloc (size + overhead);
+  if (val && check_depth == 1)
     {
       bcopy (xmalloc_overrun_check_header, val, XMALLOC_OVERRUN_CHECK_SIZE - 4);
       val += XMALLOC_OVERRUN_CHECK_SIZE;
       XMALLOC_PUT_SIZE(val, size);
       bcopy (xmalloc_overrun_check_trailer, val + size, XMALLOC_OVERRUN_CHECK_SIZE);
     }
+  --check_depth;
   return (POINTER_TYPE *)val;
 }
 
@@ -637,8 +651,10 @@ overrun_check_realloc (block, size)
      size_t size;
 {
   register unsigned char *val = (unsigned char *)block;
+  size_t overhead = ++check_depth == 1 ? XMALLOC_OVERRUN_CHECK_SIZE*2 : 0;
 
   if (val
+      && check_depth == 1
       && bcmp (xmalloc_overrun_check_header,
               val - XMALLOC_OVERRUN_CHECK_SIZE,
               XMALLOC_OVERRUN_CHECK_SIZE - 4) == 0)
@@ -653,15 +669,16 @@ overrun_check_realloc (block, size)
       bzero (val, XMALLOC_OVERRUN_CHECK_SIZE);
     }
 
-  val = (unsigned char *) realloc ((POINTER_TYPE *)val, size + XMALLOC_OVERRUN_CHECK_SIZE*2);
+  val = (unsigned char *) realloc ((POINTER_TYPE *)val, size + overhead);
 
-  if (val)
+  if (val && check_depth == 1)
     {
       bcopy (xmalloc_overrun_check_header, val, XMALLOC_OVERRUN_CHECK_SIZE - 4);
       val += XMALLOC_OVERRUN_CHECK_SIZE;
       XMALLOC_PUT_SIZE(val, size);
       bcopy (xmalloc_overrun_check_trailer, val + size, XMALLOC_OVERRUN_CHECK_SIZE);
     }
+  --check_depth;
   return (POINTER_TYPE *)val;
 }
 
@@ -673,7 +690,9 @@ overrun_check_free (block)
 {
   unsigned char *val = (unsigned char *)block;
 
+  ++check_depth;
   if (val
+      && check_depth == 1
       && bcmp (xmalloc_overrun_check_header,
               val - XMALLOC_OVERRUN_CHECK_SIZE,
               XMALLOC_OVERRUN_CHECK_SIZE - 4) == 0)
@@ -689,6 +708,7 @@ overrun_check_free (block)
     }
 
   free (val);
+  --check_depth;
 }
 
 #undef malloc
@@ -1310,8 +1330,6 @@ uninterrupt_malloc ()
   pthread_mutexattr_init (&attr);
   pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
   pthread_mutex_init (&alloc_mutex, &attr);
-
-  main_thread = pthread_self ();
 #endif /* HAVE_GTK_AND_PTHREAD */
 
   if (__free_hook != emacs_blocked_free)
@@ -4647,13 +4665,48 @@ returns nil, because real GC can't be done.  */)
   if (abort_on_gc)
     abort ();
 
-  EMACS_GET_TIME (t1);
-
   /* Can't GC if pure storage overflowed because we can't determine
      if something is a pure object or not.  */
   if (pure_bytes_used_before_overflow)
     return Qnil;
 
+  /* Don't keep undo information around forever.
+     Do this early on, so it is no problem if the user quits.  */
+  {
+    register struct buffer *nextb = all_buffers;
+
+    while (nextb)
+      {
+       /* If a buffer's undo list is Qt, that means that undo is
+          turned off in that buffer.  Calling truncate_undo_list on
+          Qt tends to return NULL, which effectively turns undo back on.
+          So don't call truncate_undo_list if undo_list is Qt.  */
+       if (! EQ (nextb->undo_list, Qt))
+         truncate_undo_list (nextb);
+
+       /* Shrink buffer gaps, but skip indirect and dead buffers.  */
+       if (nextb->base_buffer == 0 && !NILP (nextb->name))
+         {
+           /* If a buffer's gap size is more than 10% of the buffer
+              size, or larger than 2000 bytes, then shrink it
+              accordingly.  Keep a minimum size of 20 bytes.  */
+           int size = min (2000, max (20, (nextb->text->z_byte / 10)));
+
+           if (nextb->text->gap_size > size)
+             {
+               struct buffer *save_current = current_buffer;
+               current_buffer = nextb;
+               make_gap (-(nextb->text->gap_size - size));
+               current_buffer = save_current;
+             }
+         }
+
+       nextb = nextb->next;
+      }
+  }
+
+  EMACS_GET_TIME (t1);
+
   /* In case user calls debug_print during GC,
      don't let that cause a recursive GC.  */
   consing_since_gc = 0;
@@ -4692,42 +4745,6 @@ returns nil, because real GC can't be done.  */)
 
   shrink_regexp_cache ();
 
-  /* Don't keep undo information around forever.  */
-  {
-    register struct buffer *nextb = all_buffers;
-
-    while (nextb)
-      {
-       /* If a buffer's undo list is Qt, that means that undo is
-          turned off in that buffer.  Calling truncate_undo_list on
-          Qt tends to return NULL, which effectively turns undo back on.
-          So don't call truncate_undo_list if undo_list is Qt.  */
-       if (! EQ (nextb->undo_list, Qt))
-         nextb->undo_list
-           = truncate_undo_list (nextb->undo_list, undo_limit,
-                                 undo_strong_limit, undo_outer_limit);
-
-       /* Shrink buffer gaps, but skip indirect and dead buffers.  */
-       if (nextb->base_buffer == 0 && !NILP (nextb->name))
-         {
-           /* If a buffer's gap size is more than 10% of the buffer
-              size, or larger than 2000 bytes, then shrink it
-              accordingly.  Keep a minimum size of 20 bytes.  */
-           int size = min (2000, max (20, (nextb->text->z_byte / 10)));
-
-           if (nextb->text->gap_size > size)
-             {
-               struct buffer *save_current = current_buffer;
-               current_buffer = nextb;
-               make_gap (-(nextb->text->gap_size - size));
-               current_buffer = save_current;
-             }
-         }
-
-       nextb = nextb->next;
-      }
-  }
-
   gc_in_progress = 1;
 
   /* clear_marks (); */
@@ -6002,29 +6019,6 @@ prevent garbage collection during a part of the program.  */);
               doc: /* Non-nil means loading Lisp code in order to dump an executable.
 This means that certain objects should be allocated in shared (pure) space.  */);
 
-  DEFVAR_INT ("undo-limit", &undo_limit,
-             doc: /* Keep no more undo information once it exceeds this size.
-This limit is applied when garbage collection happens.
-The size is counted as the number of bytes occupied,
-which includes both saved text and other data.  */);
-  undo_limit = 20000;
-
-  DEFVAR_INT ("undo-strong-limit", &undo_strong_limit,
-             doc: /* Don't keep more than this much size of undo information.
-A previous command which pushes the undo list past this size
-is entirely forgotten when GC happens.
-The size is counted as the number of bytes occupied,
-which includes both saved text and other data.  */);
-  undo_strong_limit = 30000;
-
-  DEFVAR_INT ("undo-outer-limit", &undo_outer_limit,
-             doc: /* Don't keep more than this much size of undo information.
-If the current command has produced more than this much undo information,
-GC discards it.  This is a last-ditch limit to prevent memory overflow.
-The size is counted as the number of bytes occupied,
-which includes both saved text and other data.  */);
-  undo_outer_limit = 300000;
-
   DEFVAR_BOOL ("garbage-collection-messages", &garbage_collection_messages,
               doc: /* Non-nil means display messages at start and end of garbage collection.  */);
   garbage_collection_messages = 0;