Re: Proposal: g_set_out_of_mem_handler
- From: Tim Janik <timj gtk org>
- To: gtk-devel-list redhat com
- Subject: Re: Proposal: g_set_out_of_mem_handler
- Date: Fri, 19 Nov 1999 00:32:31 +0100 (CET)
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]