Proposal: g_set_out_of_mem_handler



Hi, all

We recently had a discussion on orbit-list about the current fail at "out of
memory" behaviour of GLib. While I still think, out of memory can in general
not be handled sane, we might as well add a hook to be called whenever Glib
find itself out of mem. For the greatest possible generality this routine only
have to return a pointer to the requested memory, thus this memory can be
taken from a preallocated place in memory, and now comes the clue: gmem.c
keeps a list of pointers, that are obtained that way. This list is normally
empty (almost no overhead in testing it) and if not empty every freed memory
is testet against that list and if contained freed with another callback. That
way you can preallocate, say, 500 KB and whenever an "out of mem" occurs you
just give out the requested memory and be fine. (and hope, XLib will not try
allocate memory, that is ;-). That might actually work, is not much overhead
and will at least save us from more and more questions regarding that. I
appended a sample implementation. Its not yet fully testet, but should
actually work. Then you could do something like:

#define SIZE 100000
static int mem_count = 0; /* whenever this is not zero, we are in some
                             kind of trouble */

gpointer my_malloc(gulong size)
{
  static long mem[SIZE]; /* It's long for alignment reasons */
  static gunit next_free_index = 0;
  gpointer ret_val;

  if (mem_count==0) /* No emergency till now */
    next_free_index = 0;

  ret_val = &mem[next_free_index];

  next_free_index += (size - 1) % sizeof (long) + 1;

  if (next_free_index > SIZE)
    return NULL; /* Oops; fatal */

  mem_count++;

  return ret_val;
}

gpointer my_free(gpointer mem, gulong size)
{
  mem_count--; /* This is a very unsophisticated memory handler, but
                  anyway it works */
}

main()
{
  g_set_out_of_mem_handler (my_malloc, my_free);
}

BTW.: Now, that we have memprof (Great work...), isn't it about time to remove
some of that memory profiling in GLib. Its really looking ugly and I doubt,
anybody use it...

Bye,
Sebastian
-- 
Sebastian Wilhelmi                   |            här ovanför alla molnen
mailto:wilhelmi@ira.uka.de           |     är himmlen så förunderligt blå
http://goethe.ira.uka.de/~wilhelmi   |
Index: glib.h
===================================================================
RCS file: /cvs/gnome/glib/glib.h,v
retrieving revision 1.148
diff -u -b -B -r1.148 glib.h
--- glib.h	1999/11/16 10:30:25	1.148
+++ glib.h	1999/11/16 14:38:23
@@ -1453,6 +1453,14 @@
 
 /* Memory allocation and debugging
  */
+
+typedef gpointer	(*GOutOfMemMallocFunc) (gulong   size);
+typedef void		(*GOutOfMemFreeFunc)   (gpointer mem,
+					        gulong   size);
+
+void    g_set_out_of_mem_handler (GOutOfMemMallocFunc new_malloc, 
+				  GOutOfMemFreeFunc   new_free);
+
 #ifdef USE_DMALLOC
 
 #define g_malloc(size)	     ((gpointer) MALLOC (size))
Index: gmem.c
===================================================================
RCS file: /cvs/gnome/glib/gmem.c,v
retrieving revision 1.16
diff -u -b -B -r1.16 gmem.c
--- gmem.c	1999/09/17 09:02:48	1.16
+++ gmem.c	1999/11/16 14:38:23
@@ -150,6 +150,72 @@
 
 #ifndef USE_DMALLOC
 
+static GOutOfMemMallocFunc     out_of_mem_malloc = NULL;
+static GOutOfMemFreeFunc       out_of_mem_free   = NULL;
+
+typedef struct _OutOfMemHeader OutOfMemHeader;
+struct _OutOfMemHeader
+{
+  OutOfMemHeader *next;
+  gulong size;
+};
+
+static OutOfMemHeader *first_out_of_mem_allocation = NULL;
+
+void    
+g_set_out_of_mem_handler (GOutOfMemMallocFunc new_malloc, 
+			  GOutOfMemFreeFunc   new_free)
+{
+  out_of_mem_malloc = new_malloc;
+  out_of_mem_free = out_of_mem_malloc ? new_free : NULL;
+}
+
+static gpointer
+g_out_of_mem_malloc (gulong size)
+{
+  OutOfMemHeader *mem;
+  gulong needed_size = size + sizeof (OutOfMemHeader); 
+
+  if (!out_of_mem_malloc || !(mem = out_of_mem_malloc (needed_size)))
+    {
+      g_error ("could not allocate %ld bytes", size);
+      return NULL; /* Just to make gcc -Wall happy, will not be reached */
+    }
+
+  mem->next = first_out_of_mem_allocation;
+  mem->size = needed_size;
+  first_out_of_mem_allocation = mem;
+
+  return ((gpointer)mem) + sizeof (OutOfMemHeader); 
+}
+
+static void
+g_out_of_mem_free (gpointer mem)
+{
+  OutOfMemHeader *header = mem - sizeof (OutOfMemHeader);
+  OutOfMemHeader **last_next_pointer = &first_out_of_mem_allocation;
+  gulong size;
+
+  while (*last_next_pointer != NULL && (*last_next_pointer) != header)
+    last_next_pointer = &((*last_next_pointer)->next);
+
+  if (*last_next_pointer == NULL)
+    {
+      /* This mem was not allocated while "out of mem" */
+      free(mem);
+      return;
+    }
+    
+  if (!out_of_mem_free)
+    /* Just ignore it */
+    return;
+
+  size = (*last_next_pointer)->size;
+  (*last_next_pointer) = (*last_next_pointer)->next;
+
+  out_of_mem_free (mem, size);
+}
+
 gpointer
 g_malloc (gulong size)
 {
@@ -176,7 +242,7 @@
   
   p = (gpointer) malloc (size);
   if (!p)
-    g_error ("could not allocate %ld bytes", size);
+    p = g_out_of_mem_malloc (size);
   
   
 #ifdef ENABLE_MEM_CHECK
@@ -241,7 +307,10 @@
   
   p = (gpointer) calloc (size, 1);
   if (!p)
-    g_error ("could not allocate %ld bytes", size);
+    {
+      p = g_out_of_mem_malloc (size);
+      memset(p, 0, size);
+    }
   
   
 #ifdef ENABLE_MEM_CHECK
@@ -315,6 +384,8 @@
 #else /* !REALLOC_0_WORKS */
       p = (gpointer) malloc (size);
 #endif /* !REALLOC_0_WORKS */
+      if (!p)
+	p = g_out_of_mem_malloc (size);
     }
   else
     {
@@ -334,13 +405,26 @@
 	g_warning ("trying to realloc freed memory\n");
       mem = t;
 #endif /* ENABLE_MEM_CHECK */
-      
+      if (!first_out_of_mem_allocation)
+	{
       p = (gpointer) realloc (mem, size);
+	  if (!p)
+	    {
+	      p = g_out_of_mem_malloc (size);
+	      g_memmove (p, mem, size);
+	      free (mem);
     }
   
+	}
+      else
+	{
+	  p = (gpointer) malloc (size);
   if (!p)
-    g_error ("could not reallocate %lu bytes", (gulong) size);
-  
+	    p = g_out_of_mem_malloc (size);
+	  g_memmove (p, mem, size);
+	  g_out_of_mem_free (mem);
+	}
+    }
   
 #ifdef ENABLE_MEM_CHECK
   size -= SIZEOF_LONG;
@@ -408,7 +492,11 @@
       
       memset ((guchar*) mem + 8, 0, size);
 #else /* ENABLE_MEM_CHECK */
+      if(!first_out_of_mem_allocation)
       free (mem);
+      else
+	g_out_of_mem_free (mem);
+      
 #endif /* ENABLE_MEM_CHECK */
     }
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]