Re: Proposal: g_set_out_of_mem_handler



On Tue, 16 Nov 1999, Sebastian Wilhelmi wrote:

> 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.

note that -using this approach- after a short period of memory bottleneck, you
might end up with a significant amount of small memory portions (a bunch of
which can actually be RO allocations) that are all kept in your pointer list
and are checked against upon all further free() operations (a very frequently
used function btw, due to the highly dynamic nature glib/gtk code is coded in
general). this can cause a major slowdown for the rest of the application's
runtime, eventhough the real bottleneck quickly vanished.

> 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 ;-).

which is pretty naive as soon as you get into matters of handling the event
queue...

> 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:
[...]

> 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...

ok, i should note first that i wholeheartedly agree with you that out-of-memory
situations can't be handled comprehensively (and portably!) in a sane way at
the application level.
but since the "broad public" (whatever that is ;) seems to feel a need to be
enabled to handle this situations at least in edge cases, i think we should
come up with a more generic solution that will:

1) get rid of the old half-backed debugging/"profiling" code in gmem.c.
   we got three different defines to alter allocation/reporting behaviour
   currently, which tend to be rarely used and over-complexify glib's
   basic allocation strategies.
2) be comprehensible enough to cover future feature requests that may popup
   in this area, for instance to facilitate platform specific allocation
   debuggers or memory-region locks (the interested linux user may want to
   ponder about mprotect(2) for a moment).

some considerations that imho should be taken into account:
- we certainly can't feature something as versatile as the GAllocator
  mechanism, since g_malloc() is used for probably 90% of a normal gtk
  program's allocations at all programming layers, including the low-level
  glib stuff.
- this seems like a good opportunity to make an effort on specializing for
  read-only allocated regions, so we
  * avoid a major portion of so-thought-leak "bug reports"
  * can use provided hooks to report real leaks and meaningfull statistics
    of the g_malloc() allocations being made by the application
  * provde hints for the fallback-allocation routines used upon EMEM, so
    it can deal with allocation requests cleverly, i.e. not bother about
    RO regions being freed.
  (i estimate about a 1/2 to 1 day (for a core hacker) to go over glib/gtk
  code and substitute ro allocations with the new function calls)

i think a good aproach on this, would be:

typedef struct _GMallocTable GMallocTable;
struct _GMallocTable
{
  gpointer (*malloc)		(gulong         size); /* obligatory */
  gpointer (*romalloc)		(gulong         size);
  void     (*free)		(gpointer       mem);  /* obligatory */
  gpointer (*fallback_malloc)	(gulong         size,
                                 gboolean       ro_allocation);
  void     (*report)            (const gchar   *log_domain,
                                 GLogLevelFlags log_level);
};

extern GMallocTable *g_default_malloc_table;

/* this function has to be called *prior* to any other
 * GLib function if at all (including g_thread_init()).
 */
GMallocTable* g_malloc_table_set (GMallocTable *table);

void g_malloc_report (const gchar   *log_domain,
                      GLogLevelFlags log_level);

#define g_new(type, count)		/* as present */
#define g_new0(type, count)		/* as present */
#define g_renew(type, mem, count)	/* as present */

gpointer g_malloc      (gulong    size);
gpointer g_malloc0     (gulong    size);
gpointer g_realloc     (gpointer  mem,
                        gulong    size);
void     g_free        (gpointer  mem);

/* ReadOnly allocations, memory portions returned from these allocators
 * may *never* be passed into g_free(), no further checks are performed
 */
gpointer g_romalloc    (gulong    size);
gpointer g_romalloc0   (gulong    size);
#define g_ronew(type, count)		/* g_new(), calling g_romalloc() */
#define g_ronew0(type, count)		/* g_new0(), calling g_romalloc0() */

sample implementation:

#include <stdlib.h>
static GMallocTable default_malloc_table = { malloc, malloc, free, NULL };
GMallocTable *g_default_malloc_table = &default_malloc_table;
static GMallocTable *g_malloc_table = &default_malloc_table;

gpointer
g_malloc (gulong size)
{
  gpointer p;
  
  /* we guarrantee to *never* return NULL; except for size==0 */
  if (size == 0)
    return NULL;
        
  p = g_malloc_table->malloc (size);
  if (!p)
    {
      p = g_malloc_table->fallback_malloc (size, FALSE);
      if (!p)
        g_error ("could not allocate %ld bytes", size);
    }

  return p;
}

this will even allow to keep maintaining the current ENABLE_MEM_CHECK
behaviour by providing

extern GMallocTable *g_debug_malloc_table;

if people insist on the possibility (though there are better ways to
facilitate allocation debuging/bounds checking).

a viable implementation for customized fallback_malloc()/free() implementations
could then be, to

- preallocate a certain memory region, sticking to sebastian's example: 512KB
  keep the start and end pointers of that region
- return NULL for memory requests that could not be fullfilled
- allocate ro regions from the top, shrinking the end pointer
- allocate rw regions from a freed_regions list or from the bottom
- check regions to be freed against the end and start pointers then
    add them to the freed_regions list (with optional merging)
  else
    defer freeing to the underlying system specific implementation


the ->report() member of GMallocTable would be otionally featured by
g_malloc_report() to print a detailed allocation report at a certain
log level, this is optional suggar that i just throw in for discussion.
more sophisticated approaches are also possible, e.g. ->report() filling
some GMallocStats structure

struct _GMallocStats
{
  gulong  current_rw_allocations;
  gulong  current_ro_allocations;
  gulong  total_rw_allocations; /* since app start */
  gulong  total_freed_allocations; /* since app start */
  gulong  total_ro_allocations; /* since app start */
  guint   n_listed_rw_allocations;
  gulong *rw_allocations; /* to be free()ed after usage */
  guint   n_listed_ro_allocations;
  gulong *ro_allocations; /* to be free()ed after usage */
};

i'm not really proposing this last part, implementation depends on what
people think would be necessary and usefull.

> 
> Bye,
> Sebastian

---
ciaoTJ



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