unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* (make-vector 100000000000 nil) shouldn't force a quick exit
@ 2011-05-30 17:08 Paul Eggert
  2011-06-02 18:43 ` bug#8762: patch installed into Emacs trunk Paul Eggert
  0 siblings, 1 reply; 2+ messages in thread
From: Paul Eggert @ 2011-05-30 17:08 UTC (permalink / raw)
  To: bug-gnu-emacs, bug-gnulib

Currently, when the user allocates a too-large vector, e.g.,
(make-vector 100000000000 nil), the malloc failure causes Emacs to go
into "I'm about to die!" mode, because it assumes that memory is so
low that the user should simply exit as soon as possible.  But there's
typically plenty of memory available.  Here's a proposed patch to
cause Emacs to go into low-memory mode only when memory is really low.

This proposed patch also would affect gnulib (lib/allocator.h and
lib/careadlinkat.c) so I'm CC'ing bug-gnulib.


[ChangeLog]
Malloc failure behavior now depends on size of allocation.
* lib/allocator.h (struct allocator.die): New size arg.
* lib/careadlinkat.c (careadlinkat): Pass size to 'die' function.
If the actual problem is an ssize_t limitation, not a size_t or
malloc failure, fail with errno == ENAMETOOLONG instead of calling 'die'.
[src/ChangeLog]
Malloc failure behavior now depends on size of allocation.
* alloc.c (buffer_memory_full, memory_full): New arg NBYTES.
* lisp.h: Change signatures accordingly.
* alloc.c, buffer.c, editfns.c, menu.c, minibuf.c, xterm.c:
All callers changed.
=== modified file 'lib/allocator.h'
--- lib/allocator.h	2011-04-09 18:44:05 +0000
+++ lib/allocator.h	2011-05-30 16:12:20 +0000
@@ -45,10 +45,11 @@
   /* Call FREE to free memory, like 'free'.  */
   void (*free) (void *);

-  /* If nonnull, call DIE if MALLOC or REALLOC fails.  DIE should not
-     return.  DIE can be used by code that detects memory overflow
-     while calculating sizes to be passed to MALLOC or REALLOC.  */
-  void (*die) (void);
+  /* If nonnull, call DIE (SIZE) if MALLOC (SIZE) or REALLOC (...,
+     SIZE) fails.  DIE should not return.  SIZE should equal SIZE_MAX
+     if size_t overflow was detected while calculating sizes to be
+     passed to MALLOC or REALLOC.  */
+  void (*die) (size_t);
 };

 /* An allocator using the stdlib functions and a null DIE function.  */

=== modified file 'lib/careadlinkat.c'
--- lib/careadlinkat.c	2011-04-10 16:00:46 +0000
+++ lib/careadlinkat.c	2011-05-30 16:12:20 +0000
@@ -135,6 +135,7 @@
           if (buf == stack_buf)
             {
               char *b = (char *) alloc->allocate (link_size);
+              buf_size = link_size;
               if (! b)
                 break;
               memcpy (b, buf, link_size);
@@ -158,6 +159,11 @@
         buf_size *= 2;
       else if (buf_size < buf_size_max)
         buf_size = buf_size_max;
+      else if (buf_size_max < SIZE_MAX)
+        {
+          errno = ENAMETOOLONG;
+          return NULL;
+        }
       else
         break;
       buf = (char *) alloc->allocate (buf_size);
@@ -165,7 +171,7 @@
   while (buf);

   if (alloc->die)
-    alloc->die ();
+    alloc->die (buf_size);
   errno = ENOMEM;
   return NULL;
 }

=== modified file 'src/alloc.c'
--- src/alloc.c	2011-05-30 16:09:29 +0000
+++ src/alloc.c	2011-05-30 16:23:20 +0000
@@ -471,7 +471,7 @@
 /* Called if we can't allocate relocatable space for a buffer.  */

 void
-buffer_memory_full (void)
+buffer_memory_full (EMACS_INT nbytes)
 {
   /* If buffers use the relocating allocator, no need to free
      spare_memory, because we may have plenty of malloc space left
@@ -481,7 +481,7 @@
      malloc.  */

 #ifndef REL_ALLOC
-  memory_full ();
+  memory_full (nbytes);
 #endif

   /* This used to call error, but if we've run out of memory, we could
@@ -677,7 +677,7 @@
   MALLOC_UNBLOCK_INPUT;

   if (!val && size)
-    memory_full ();
+    memory_full (size);
   return val;
 }

@@ -698,7 +698,8 @@
     val = (POINTER_TYPE *) realloc (block, size);
   MALLOC_UNBLOCK_INPUT;

-  if (!val && size) memory_full ();
+  if (!val && size)
+    memory_full (size);
   return val;
 }

@@ -791,7 +792,7 @@

   MALLOC_UNBLOCK_INPUT;
   if (!val && nbytes)
-    memory_full ();
+    memory_full (nbytes);
   return val;
 }

@@ -938,7 +939,7 @@
       if (base == 0)
 	{
 	  MALLOC_UNBLOCK_INPUT;
-	  memory_full ();
+	  memory_full (ABLOCKS_BYTES);
 	}

       aligned = (base == abase);
@@ -964,7 +965,7 @@
 	      lisp_malloc_loser = base;
 	      free (base);
 	      MALLOC_UNBLOCK_INPUT;
-	      memory_full ();
+	      memory_full (SIZE_MAX);
 	    }
 	}
 #endif
@@ -3270,35 +3271,58 @@
  ************************************************************************/


-/* Called if malloc returns zero.  */
+/* Called if malloc (NBYTES) returns zero.  If NBYTES == SIZE_MAX,
+   there may have been size_t overflow so that malloc was never
+   called, or perhaps malloc was invoked successfully but the
+   resulting pointer had problems fitting into a tagged EMACS_INT.  In
+   either case this counts as memory being full even though malloc did
+   not fail.  */

 void
-memory_full (void)
+memory_full (size_t nbytes)
 {
-  int i;
-
-  Vmemory_full = Qt;
-
-  memory_full_cons_threshold = sizeof (struct cons_block);
-
-  /* The first time we get here, free the spare memory.  */
-  for (i = 0; i < sizeof (spare_memory) / sizeof (char *); i++)
-    if (spare_memory[i])
-      {
-	if (i == 0)
-	  free (spare_memory[i]);
-	else if (i >= 1 && i <= 4)
-	  lisp_align_free (spare_memory[i]);
-	else
-	  lisp_free (spare_memory[i]);
-	spare_memory[i] = 0;
-      }
-
-  /* Record the space now used.  When it decreases substantially,
-     we can refill the memory reserve.  */
+  /* Do not go into hysterics merely because a large request failed.  */
+  int enough_free_memory = 0;
+  if (SPARE_MEMORY < nbytes)
+    {
+      void *p = malloc (SPARE_MEMORY);
+      if (p)
+	{
+	  if (spare_memory[0])
+	    free (p);
+	  else
+	    spare_memory[0] = p;
+	  enough_free_memory = 1;
+	}
+    }
+
+  if (! enough_free_memory)
+    {
+      int i;
+
+      Vmemory_full = Qt;
+
+      memory_full_cons_threshold = sizeof (struct cons_block);
+
+      /* The first time we get here, free the spare memory.  */
+      for (i = 0; i < sizeof (spare_memory) / sizeof (char *); i++)
+	if (spare_memory[i])
+	  {
+	    if (i == 0)
+	      free (spare_memory[i]);
+	    else if (i >= 1 && i <= 4)
+	      lisp_align_free (spare_memory[i]);
+	    else
+	      lisp_free (spare_memory[i]);
+	    spare_memory[i] = 0;
+	  }
+
+      /* Record the space now used.  When it decreases substantially,
+	 we can refill the memory reserve.  */
 #if !defined SYSTEM_MALLOC && !defined SYNC_INPUT
-  bytes_used_when_full = BYTES_USED;
+      bytes_used_when_full = BYTES_USED;
 #endif
+    }

   /* This used to call error, but if we've run out of memory, we could
      get infinite recursion trying to build the string.  */

=== modified file 'src/buffer.c'
--- src/buffer.c	2011-05-12 07:07:06 +0000
+++ src/buffer.c	2011-05-30 16:12:20 +0000
@@ -328,7 +328,7 @@
   alloc_buffer_text (b, BUF_GAP_SIZE (b) + 1);
   UNBLOCK_INPUT;
   if (! BUF_BEG_ADDR (b))
-    buffer_memory_full ();
+    buffer_memory_full (BUF_GAP_SIZE (b) + 1);

   b->pt = BEG;
   b->begv = BEG;
@@ -4892,7 +4892,7 @@
   if (p == NULL)
     {
       UNBLOCK_INPUT;
-      memory_full ();
+      memory_full (nbytes);
     }

   b->text->beg = (unsigned char *) p;
@@ -4920,7 +4920,7 @@
   if (p == NULL)
     {
       UNBLOCK_INPUT;
-      memory_full ();
+      memory_full (nbytes);
     }

   BUF_BEG_ADDR (b) = (unsigned char *) p;

=== modified file 'src/editfns.c'
--- src/editfns.c	2011-05-27 19:37:32 +0000
+++ src/editfns.c	2011-05-30 16:12:20 +0000
@@ -3636,7 +3636,7 @@
   {
     EMACS_INT i;
     if ((SIZE_MAX - formatlen) / sizeof (struct info) <= nargs)
-      memory_full ();
+      memory_full (SIZE_MAX);
     SAFE_ALLOCA (info, struct info *, (nargs + 1) * sizeof *info + formatlen);
     discarded = (char *) &info[nargs + 1];
     for (i = 0; i < nargs + 1; i++)

=== modified file 'src/lisp.h'
--- src/lisp.h	2011-05-30 05:39:59 +0000
+++ src/lisp.h	2011-05-30 16:12:20 +0000
@@ -2685,8 +2685,8 @@
 extern void reset_malloc_hooks (void);
 extern void uninterrupt_malloc (void);
 extern void malloc_warning (const char *);
-extern void memory_full (void) NO_RETURN;
-extern void buffer_memory_full (void) NO_RETURN;
+extern void memory_full (size_t) NO_RETURN;
+extern void buffer_memory_full (EMACS_INT) NO_RETURN;
 extern int survives_gc_p (Lisp_Object);
 extern void mark_object (Lisp_Object);
 #if defined REL_ALLOC && !defined SYSTEM_MALLOC

=== modified file 'src/menu.c'
--- src/menu.c	2011-05-18 03:03:15 +0000
+++ src/menu.c	2011-05-30 16:12:20 +0000
@@ -178,7 +178,7 @@
 grow_menu_items (void)
 {
   if ((INT_MAX - MENU_ITEMS_PANE_LENGTH) / 2 < menu_items_allocated)
-    memory_full ();
+    memory_full (SIZE_MAX);
   menu_items_allocated *= 2;
   menu_items = larger_vector (menu_items, menu_items_allocated, Qnil);
 }

=== modified file 'src/minibuf.c'
--- src/minibuf.c	2011-05-12 07:07:06 +0000
+++ src/minibuf.c	2011-05-30 16:12:20 +0000
@@ -245,7 +245,7 @@
 	     len == size - 1 && line[len - 1] != '\n'))
     {
       if ((size_t) -1 / 2 < size)
-	memory_full ();
+	memory_full (SIZE_MAX);
       size *= 2;
       line = (char *) xrealloc (line, size);
     }

=== modified file 'src/xterm.c'
--- src/xterm.c	2011-05-27 16:17:59 +0000
+++ src/xterm.c	2011-05-30 16:12:20 +0000
@@ -4225,7 +4225,7 @@
       size_t old_nbytes = scroll_bar_windows_size * sizeof *scroll_bar_windows;

       if ((size_t) -1 / sizeof *scroll_bar_windows < new_size)
-	memory_full ();
+	memory_full (SIZE_MAX);
       scroll_bar_windows = (struct window **) xrealloc (scroll_bar_windows,
 							nbytes);
       memset (&scroll_bar_windows[i], 0, nbytes - old_nbytes);




^ permalink raw reply	[flat|nested] 2+ messages in thread

* bug#8762: patch installed into Emacs trunk
  2011-05-30 17:08 (make-vector 100000000000 nil) shouldn't force a quick exit Paul Eggert
@ 2011-06-02 18:43 ` Paul Eggert
  0 siblings, 0 replies; 2+ messages in thread
From: Paul Eggert @ 2011-06-02 18:43 UTC (permalink / raw)
  To: 8761-done, 8762-done

I merged the abovementioned patch into the Emacs trunk,
as bzr #104480, and am marking this bug as done.
If there are any further problems with it please let me know.





^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2011-06-02 18:43 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-05-30 17:08 (make-vector 100000000000 nil) shouldn't force a quick exit Paul Eggert
2011-06-02 18:43 ` bug#8762: patch installed into Emacs trunk Paul Eggert

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).