X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgmem.c;h=6a108ee9e0e38627a3d963b9cc651f11b121f7c1;hb=13e15733f38a40c6ef6a1baede91cce81c86ebaa;hp=68de9057e686c80587e334c990e745eeace1ad0a;hpb=09b118f462fabb228ba26a498001f7c4e72bdde8;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gmem.c b/glib/gmem.c index 68de905..6a108ee 100644 --- a/glib/gmem.c +++ b/glib/gmem.c @@ -12,9 +12,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library; if not, see . */ /* @@ -30,198 +28,427 @@ #include "config.h" +#include "gmem.h" + #include #include #include -#include "glib.h" -#include "gthreadinit.h" -#include "galias.h" +#include "gslice.h" +#include "gbacktrace.h" +#include "gtestutils.h" +#include "gthread.h" +#include "glib_trace.h" + +#define MEM_PROFILE_TABLE_SIZE 4096 + /* notes on macros: - * having DISABLE_MEM_POOLS defined, disables mem_chunks alltogether, their - * allocations are performed through ordinary g_malloc/g_free. * having G_DISABLE_CHECKS defined disables use of glib_mem_profiler_table and * g_mem_profile(). - * REALLOC_0_WORKS is defined if g_realloc (NULL, x) works. - * SANE_MALLOC_PROTOS is defined if the systems malloc() and friends functions - * match the corresponding GLib prototypes, keep configure.in and gmem.h in sync here. - * if ENABLE_GC_FRIENDLY is defined, freed memory should be 0-wiped. + * If g_mem_gc_friendly is TRUE, freed memory should be 0-wiped. */ -#define MEM_PROFILE_TABLE_SIZE 4096 - -#define MEM_AREA_SIZE 4L - -#ifdef G_DISABLE_CHECKS -# define ENTER_MEM_CHUNK_ROUTINE() -# define LEAVE_MEM_CHUNK_ROUTINE() -# define IN_MEM_CHUNK_ROUTINE() FALSE -#else /* !G_DISABLE_CHECKS */ -static GPrivate* mem_chunk_recursion = NULL; -# define MEM_CHUNK_ROUTINE_COUNT() GPOINTER_TO_UINT (g_private_get (mem_chunk_recursion)) -# define ENTER_MEM_CHUNK_ROUTINE() g_private_set (mem_chunk_recursion, GUINT_TO_POINTER (MEM_CHUNK_ROUTINE_COUNT () + 1)) -# define LEAVE_MEM_CHUNK_ROUTINE() g_private_set (mem_chunk_recursion, GUINT_TO_POINTER (MEM_CHUNK_ROUTINE_COUNT () - 1)) -#endif /* !G_DISABLE_CHECKS */ - -#ifndef REALLOC_0_WORKS -static gpointer -standard_realloc (gpointer mem, - gsize n_bytes) -{ - if (!mem) - return malloc (n_bytes); - else - return realloc (mem, n_bytes); -} -#endif /* !REALLOC_0_WORKS */ - -#ifdef SANE_MALLOC_PROTOS -# define standard_malloc malloc -# ifdef REALLOC_0_WORKS -# define standard_realloc realloc -# endif /* REALLOC_0_WORKS */ -# define standard_free free -# define standard_calloc calloc -# define standard_try_malloc malloc -# define standard_try_realloc realloc -#else /* !SANE_MALLOC_PROTOS */ -static gpointer -standard_malloc (gsize n_bytes) -{ - return malloc (n_bytes); -} -# ifdef REALLOC_0_WORKS -static gpointer -standard_realloc (gpointer mem, - gsize n_bytes) -{ - return realloc (mem, n_bytes); -} -# endif /* REALLOC_0_WORKS */ -static void -standard_free (gpointer mem) -{ - free (mem); -} -static gpointer -standard_calloc (gsize n_blocks, - gsize n_bytes) -{ - return calloc (n_blocks, n_bytes); -} -#define standard_try_malloc standard_malloc -#define standard_try_realloc standard_realloc -#endif /* !SANE_MALLOC_PROTOS */ - - /* --- variables --- */ static GMemVTable glib_mem_vtable = { - standard_malloc, - standard_realloc, - standard_free, - standard_calloc, - standard_try_malloc, - standard_try_realloc, + malloc, + realloc, + free, + calloc, + malloc, + realloc, }; +/** + * SECTION:memory + * @Short_Description: general memory-handling + * @Title: Memory Allocation + * + * These functions provide support for allocating and freeing memory. + * + * If any call to allocate memory fails, the application is terminated. + * This also means that there is no need to check if the call succeeded. + * + * It's important to match g_malloc() (and wrappers such as g_new()) with + * g_free(), g_slice_alloc() (and wrappers such as g_slice_new()) with + * g_slice_free(), plain malloc() with free(), and (if you're using C++) + * new with delete and new[] with delete[]. Otherwise bad things can happen, + * since these allocators may use different memory pools (and new/delete call + * constructors and destructors). See also g_mem_set_vtable(). + */ /* --- functions --- */ +/** + * g_malloc: + * @n_bytes: the number of bytes to allocate + * + * Allocates @n_bytes bytes of memory. + * If @n_bytes is 0 it returns %NULL. + * + * Returns: a pointer to the allocated memory + */ gpointer -g_malloc (gulong n_bytes) +g_malloc (gsize n_bytes) { - if (n_bytes) + if (G_LIKELY (n_bytes)) { gpointer mem; mem = glib_mem_vtable.malloc (n_bytes); + TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 0)); if (mem) return mem; - g_error ("%s: failed to allocate %lu bytes", G_STRLOC, n_bytes); + g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_bytes); } + TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 0, 0)); + return NULL; } +/** + * g_malloc0: + * @n_bytes: the number of bytes to allocate + * + * Allocates @n_bytes bytes of memory, initialized to 0's. + * If @n_bytes is 0 it returns %NULL. + * + * Returns: a pointer to the allocated memory + */ gpointer -g_malloc0 (gulong n_bytes) +g_malloc0 (gsize n_bytes) { - if (n_bytes) + if (G_LIKELY (n_bytes)) { gpointer mem; mem = glib_mem_vtable.calloc (1, n_bytes); + TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 1, 0)); if (mem) return mem; - g_error ("%s: failed to allocate %lu bytes", G_STRLOC, n_bytes); + g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_bytes); } + TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 1, 0)); + return NULL; } +/** + * g_realloc: + * @mem: (allow-none): the memory to reallocate + * @n_bytes: new size of the memory in bytes + * + * Reallocates the memory pointed to by @mem, so that it now has space for + * @n_bytes bytes of memory. It returns the new address of the memory, which may + * have been moved. @mem may be %NULL, in which case it's considered to + * have zero-length. @n_bytes may be 0, in which case %NULL will be returned + * and @mem will be freed unless it is %NULL. + * + * Returns: the new address of the allocated memory + */ gpointer g_realloc (gpointer mem, - gulong n_bytes) + gsize n_bytes) { - if (n_bytes) + gpointer newmem; + + if (G_LIKELY (n_bytes)) { - mem = glib_mem_vtable.realloc (mem, n_bytes); - if (mem) - return mem; + newmem = glib_mem_vtable.realloc (mem, n_bytes); + TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 0)); + if (newmem) + return newmem; - g_error ("%s: failed to allocate %lu bytes", G_STRLOC, n_bytes); + g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_bytes); } if (mem) glib_mem_vtable.free (mem); + TRACE (GLIB_MEM_REALLOC((void*) NULL, (void*)mem, 0, 0)); + return NULL; } +/** + * g_free: + * @mem: (allow-none): the memory to free + * + * Frees the memory pointed to by @mem. + * If @mem is %NULL it simply returns. + */ void g_free (gpointer mem) { - if (mem) + if (G_LIKELY (mem)) glib_mem_vtable.free (mem); + TRACE(GLIB_MEM_FREE((void*) mem)); } +/** + * g_clear_pointer: (skip) + * @pp: a pointer to a variable, struct member etc. holding a pointer + * @destroy: a function to which a gpointer can be passed, to destroy *@pp + * + * Clears a reference to a variable. + * + * @pp must not be %NULL. + * + * If the reference is %NULL then this function does nothing. + * Otherwise, the variable is destroyed using @destroy and the + * pointer is set to %NULL. + * + * A macro is also included that allows this function to be used without + * pointer casts. + * + * Since: 2.34 + **/ +#undef g_clear_pointer +void +g_clear_pointer (gpointer *pp, + GDestroyNotify destroy) +{ + gpointer _p; + + _p = *pp; + if (_p) + { + *pp = NULL; + destroy (_p); + } +} + +/** + * g_try_malloc: + * @n_bytes: number of bytes to allocate. + * + * Attempts to allocate @n_bytes, and returns %NULL on failure. + * Contrast with g_malloc(), which aborts the program on failure. + * + * Returns: the allocated memory, or %NULL. + */ gpointer -g_try_malloc (gulong n_bytes) +g_try_malloc (gsize n_bytes) { - if (n_bytes) - return glib_mem_vtable.try_malloc (n_bytes); + gpointer mem; + + if (G_LIKELY (n_bytes)) + mem = glib_mem_vtable.try_malloc (n_bytes); else - return NULL; + mem = NULL; + + TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 1)); + + return mem; } +/** + * g_try_malloc0: + * @n_bytes: number of bytes to allocate + * + * Attempts to allocate @n_bytes, initialized to 0's, and returns %NULL on + * failure. Contrast with g_malloc0(), which aborts the program on failure. + * + * Since: 2.8 + * Returns: the allocated memory, or %NULL + */ gpointer -g_try_malloc0 (gulong n_bytes) -{ +g_try_malloc0 (gsize n_bytes) +{ gpointer mem; - mem = g_try_malloc (n_bytes); - + if (G_LIKELY (n_bytes)) + mem = glib_mem_vtable.try_malloc (n_bytes); + else + mem = NULL; + if (mem) memset (mem, 0, n_bytes); return mem; } +/** + * g_try_realloc: + * @mem: (allow-none): previously-allocated memory, or %NULL. + * @n_bytes: number of bytes to allocate. + * + * Attempts to realloc @mem to a new size, @n_bytes, and returns %NULL + * on failure. Contrast with g_realloc(), which aborts the program + * on failure. If @mem is %NULL, behaves the same as g_try_malloc(). + * + * Returns: the allocated memory, or %NULL. + */ gpointer g_try_realloc (gpointer mem, - gulong n_bytes) + gsize n_bytes) { - if (n_bytes) - return glib_mem_vtable.try_realloc (mem, n_bytes); + gpointer newmem; - if (mem) - glib_mem_vtable.free (mem); + if (G_LIKELY (n_bytes)) + newmem = glib_mem_vtable.try_realloc (mem, n_bytes); + else + { + newmem = NULL; + if (mem) + glib_mem_vtable.free (mem); + } - return NULL; + TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 1)); + + return newmem; +} + + +#define SIZE_OVERFLOWS(a,b) (G_UNLIKELY ((b) > 0 && (a) > G_MAXSIZE / (b))) + +/** + * g_malloc_n: + * @n_blocks: the number of blocks to allocate + * @n_block_bytes: the size of each block in bytes + * + * This function is similar to g_malloc(), allocating (@n_blocks * @n_block_bytes) bytes, + * but care is taken to detect possible overflow during multiplication. + * + * Since: 2.24 + * Returns: a pointer to the allocated memory + */ +gpointer +g_malloc_n (gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + { + g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_blocks, n_block_bytes); + } + + return g_malloc (n_blocks * n_block_bytes); +} + +/** + * g_malloc0_n: + * @n_blocks: the number of blocks to allocate + * @n_block_bytes: the size of each block in bytes + * + * This function is similar to g_malloc0(), allocating (@n_blocks * @n_block_bytes) bytes, + * but care is taken to detect possible overflow during multiplication. + * + * Since: 2.24 + * Returns: a pointer to the allocated memory + */ +gpointer +g_malloc0_n (gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + { + g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_blocks, n_block_bytes); + } + + return g_malloc0 (n_blocks * n_block_bytes); } +/** + * g_realloc_n: + * @mem: (allow-none): the memory to reallocate + * @n_blocks: the number of blocks to allocate + * @n_block_bytes: the size of each block in bytes + * + * This function is similar to g_realloc(), allocating (@n_blocks * @n_block_bytes) bytes, + * but care is taken to detect possible overflow during multiplication. + * + * Since: 2.24 + * Returns: the new address of the allocated memory + */ +gpointer +g_realloc_n (gpointer mem, + gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + { + g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_blocks, n_block_bytes); + } + + return g_realloc (mem, n_blocks * n_block_bytes); +} + +/** + * g_try_malloc_n: + * @n_blocks: the number of blocks to allocate + * @n_block_bytes: the size of each block in bytes + * + * This function is similar to g_try_malloc(), allocating (@n_blocks * @n_block_bytes) bytes, + * but care is taken to detect possible overflow during multiplication. + * + * Since: 2.24 + * Returns: the allocated memory, or %NULL. + */ +gpointer +g_try_malloc_n (gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + return NULL; + + return g_try_malloc (n_blocks * n_block_bytes); +} + +/** + * g_try_malloc0_n: + * @n_blocks: the number of blocks to allocate + * @n_block_bytes: the size of each block in bytes + * + * This function is similar to g_try_malloc0(), allocating (@n_blocks * @n_block_bytes) bytes, + * but care is taken to detect possible overflow during multiplication. + * + * Since: 2.24 + * Returns: the allocated memory, or %NULL + */ +gpointer +g_try_malloc0_n (gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + return NULL; + + return g_try_malloc0 (n_blocks * n_block_bytes); +} + +/** + * g_try_realloc_n: + * @mem: (allow-none): previously-allocated memory, or %NULL. + * @n_blocks: the number of blocks to allocate + * @n_block_bytes: the size of each block in bytes + * + * This function is similar to g_try_realloc(), allocating (@n_blocks * @n_block_bytes) bytes, + * but care is taken to detect possible overflow during multiplication. + * + * Since: 2.24 + * Returns: the allocated memory, or %NULL. + */ +gpointer +g_try_realloc_n (gpointer mem, + gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + return NULL; + + return g_try_realloc (mem, n_blocks * n_block_bytes); +} + + + static gpointer fallback_calloc (gsize n_blocks, gsize n_block_bytes) @@ -238,17 +465,17 @@ fallback_calloc (gsize n_blocks, static gboolean vtable_set = FALSE; /** - * g_mem_is_system_malloc + * g_mem_is_system_malloc: * * Checks whether the allocator used by g_malloc() is the system's * malloc implementation. If it returns %TRUE memory allocated with - * malloc() can be used interchangeable with memory allocated using g_malloc(). + * malloc() can be used interchangeable with memory allocated using g_malloc(). * This function is useful for avoiding an extra copy of allocated memory returned * by a non-GLib-based API. * * A different allocator can be set using g_mem_set_vtable(). * - * Return value: if %TRUE, malloc() and g_malloc() can be mixed. + * Returns: if %TRUE, malloc() and g_malloc() can be mixed. **/ gboolean g_mem_is_system_malloc (void) @@ -256,6 +483,22 @@ g_mem_is_system_malloc (void) return !vtable_set; } +/** + * g_mem_set_vtable: + * @vtable: table of memory allocation routines. + * + * Sets the #GMemVTable to use for memory allocation. You can use this + * to provide custom memory allocation routines. + * + * The @vtable only needs to provide malloc(), realloc(), and free() + * functions; GLib can provide default implementations of the others. + * The malloc() and realloc() implementations should return %NULL on + * failure, GLib will handle error-checking for you. @vtable is copied, + * so need not persist after this function has been called. + * + * Note that this function must be called before using any other GLib + * functions. + */ void g_mem_set_vtable (GMemVTable *vtable) { @@ -281,6 +524,14 @@ g_mem_set_vtable (GMemVTable *vtable) /* --- memory profiling and checking --- */ #ifdef G_DISABLE_CHECKS +/** + * glib_mem_profiler_table: + * + * A #GMemVTable containing profiling variants of the memory + * allocation functions. Use them together with g_mem_profile() + * in order to get information about the memory allocation pattern + * of your program. + */ GMemVTable *glib_mem_profiler_table = &glib_mem_vtable; void g_mem_profile (void) @@ -294,66 +545,50 @@ typedef enum { PROFILER_ZINIT = 4 } ProfilerJob; static guint *profile_data = NULL; -static gulong profile_allocs = 0; -static gulong profile_mc_allocs = 0; -static gulong profile_zinit = 0; -static gulong profile_frees = 0; -static gulong profile_mc_frees = 0; -static GMutex *g_profile_mutex = NULL; -#ifdef G_ENABLE_DEBUG -static volatile gulong g_trap_free_size = 0; -static volatile gulong g_trap_realloc_size = 0; -static volatile gulong g_trap_malloc_size = 0; -#endif /* G_ENABLE_DEBUG */ +static gsize profile_allocs = 0; +static gsize profile_zinit = 0; +static gsize profile_frees = 0; +static GMutex gmem_profile_mutex; #define PROFILE_TABLE(f1,f2,f3) ( ( ((f3) << 2) | ((f2) << 1) | (f1) ) * (MEM_PROFILE_TABLE_SIZE + 1)) static void profiler_log (ProfilerJob job, - gulong n_bytes, + gsize n_bytes, gboolean success) { - g_mutex_lock (g_profile_mutex); + g_mutex_lock (&gmem_profile_mutex); if (!profile_data) { - profile_data = standard_malloc ((MEM_PROFILE_TABLE_SIZE + 1) * 8 * sizeof (profile_data[0])); + profile_data = calloc ((MEM_PROFILE_TABLE_SIZE + 1) * 8, + sizeof (profile_data[0])); if (!profile_data) /* memory system kiddin' me, eh? */ { - g_mutex_unlock (g_profile_mutex); + g_mutex_unlock (&gmem_profile_mutex); return; } } - if (MEM_CHUNK_ROUTINE_COUNT () == 0) - { - if (n_bytes < MEM_PROFILE_TABLE_SIZE) - profile_data[n_bytes + PROFILE_TABLE ((job & PROFILER_ALLOC) != 0, - (job & PROFILER_RELOC) != 0, - success != 0)] += 1; - else - profile_data[MEM_PROFILE_TABLE_SIZE + PROFILE_TABLE ((job & PROFILER_ALLOC) != 0, - (job & PROFILER_RELOC) != 0, - success != 0)] += 1; - if (success) - { - if (job & PROFILER_ALLOC) - { - profile_allocs += n_bytes; - if (job & PROFILER_ZINIT) - profile_zinit += n_bytes; - } - else - profile_frees += n_bytes; - } - } - else if (success) + if (n_bytes < MEM_PROFILE_TABLE_SIZE) + profile_data[n_bytes + PROFILE_TABLE ((job & PROFILER_ALLOC) != 0, + (job & PROFILER_RELOC) != 0, + success != 0)] += 1; + else + profile_data[MEM_PROFILE_TABLE_SIZE + PROFILE_TABLE ((job & PROFILER_ALLOC) != 0, + (job & PROFILER_RELOC) != 0, + success != 0)] += 1; + if (success) { if (job & PROFILER_ALLOC) - profile_mc_allocs += n_bytes; + { + profile_allocs += n_bytes; + if (job & PROFILER_ZINIT) + profile_zinit += n_bytes; + } else - profile_mc_frees += n_bytes; + profile_frees += n_bytes; } - g_mutex_unlock (g_profile_mutex); + g_mutex_unlock (&gmem_profile_mutex); } static void @@ -392,64 +627,68 @@ profile_print_locked (guint *local_data, g_print (" --- none ---\n"); } +/** + * g_mem_profile: + * + * Outputs a summary of memory usage. + * + * It outputs the frequency of allocations of different sizes, + * the total number of bytes which have been allocated, + * the total number of bytes which have been freed, + * and the difference between the previous two values, i.e. the number of bytes + * still in use. + * + * Note that this function will not output anything unless you have + * previously installed the #glib_mem_profiler_table with g_mem_set_vtable(). + */ + void g_mem_profile (void) { - guint local_data[(MEM_PROFILE_TABLE_SIZE + 1) * 8 * sizeof (profile_data[0])]; - gulong local_allocs; - gulong local_zinit; - gulong local_frees; - gulong local_mc_allocs; - gulong local_mc_frees; + guint local_data[(MEM_PROFILE_TABLE_SIZE + 1) * 8]; + gsize local_allocs; + gsize local_zinit; + gsize local_frees; - g_mutex_lock (g_profile_mutex); + g_mutex_lock (&gmem_profile_mutex); local_allocs = profile_allocs; local_zinit = profile_zinit; local_frees = profile_frees; - local_mc_allocs = profile_mc_allocs; - local_mc_frees = profile_mc_frees; if (!profile_data) { - g_mutex_unlock (g_profile_mutex); + g_mutex_unlock (&gmem_profile_mutex); return; } memcpy (local_data, profile_data, (MEM_PROFILE_TABLE_SIZE + 1) * 8 * sizeof (profile_data[0])); - g_mutex_unlock (g_profile_mutex); + g_mutex_unlock (&gmem_profile_mutex); g_print ("GLib Memory statistics (successful operations):\n"); profile_print_locked (local_data, TRUE); g_print ("GLib Memory statistics (failing operations):\n"); profile_print_locked (local_data, FALSE); - g_print ("Total bytes: allocated=%lu, zero-initialized=%lu (%.2f%%), freed=%lu (%.2f%%), remaining=%lu\n", + g_print ("Total bytes: allocated=%"G_GSIZE_FORMAT", " + "zero-initialized=%"G_GSIZE_FORMAT" (%.2f%%), " + "freed=%"G_GSIZE_FORMAT" (%.2f%%), " + "remaining=%"G_GSIZE_FORMAT"\n", local_allocs, local_zinit, ((gdouble) local_zinit) / local_allocs * 100.0, local_frees, ((gdouble) local_frees) / local_allocs * 100.0, local_allocs - local_frees); - g_print ("MemChunk bytes: allocated=%lu, freed=%lu (%.2f%%), remaining=%lu\n", - local_mc_allocs, - local_mc_frees, - ((gdouble) local_mc_frees) / local_mc_allocs * 100.0, - local_mc_allocs - local_mc_frees); } static gpointer profiler_try_malloc (gsize n_bytes) { - gulong *p; + gsize *p; -#ifdef G_ENABLE_DEBUG - if (g_trap_malloc_size == n_bytes) - G_BREAKPOINT (); -#endif /* G_ENABLE_DEBUG */ - - p = standard_malloc (sizeof (gulong) * 2 + n_bytes); + p = malloc (sizeof (gsize) * 2 + n_bytes); if (p) { @@ -480,14 +719,9 @@ profiler_calloc (gsize n_blocks, gsize n_block_bytes) { gsize l = n_blocks * n_block_bytes; - gulong *p; + gsize *p; -#ifdef G_ENABLE_DEBUG - if (g_trap_malloc_size == l) - G_BREAKPOINT (); -#endif /* G_ENABLE_DEBUG */ - - p = standard_calloc (1, sizeof (gulong) * 2 + l); + p = calloc (1, sizeof (gsize) * 2 + l); if (p) { @@ -508,29 +742,25 @@ profiler_calloc (gsize n_blocks, static void profiler_free (gpointer mem) { - gulong *p = mem; + gsize *p = mem; p -= 2; if (p[0]) /* free count */ { - g_warning ("free(%p): memory has been freed %lu times already", p + 2, p[0]); + g_warning ("free(%p): memory has been freed %"G_GSIZE_FORMAT" times already", + p + 2, p[0]); profiler_log (PROFILER_FREE, p[1], /* length */ FALSE); } else { -#ifdef G_ENABLE_DEBUG - if (g_trap_free_size == p[1]) - G_BREAKPOINT (); -#endif /* G_ENABLE_DEBUG */ - profiler_log (PROFILER_FREE, p[1], /* length */ TRUE); memset (p + 2, 0xaa, p[1]); - /* for all those that miss standard_free (p); in this place, yes, + /* for all those that miss free (p); in this place, yes, * we do leak all memory when profiling, and that is intentional * to catch double frees. patch submissions are futile. */ @@ -542,25 +772,22 @@ static gpointer profiler_try_realloc (gpointer mem, gsize n_bytes) { - gulong *p = mem; + gsize *p = mem; p -= 2; - -#ifdef G_ENABLE_DEBUG - if (g_trap_realloc_size == n_bytes) - G_BREAKPOINT (); -#endif /* G_ENABLE_DEBUG */ if (mem && p[0]) /* free count */ { - g_warning ("realloc(%p, %lu): memory has been freed %lu times already", p + 2, (gulong)n_bytes, p[0]); + g_warning ("realloc(%p, %"G_GSIZE_FORMAT"): " + "memory has been freed %"G_GSIZE_FORMAT" times already", + p + 2, (gsize) n_bytes, p[0]); profiler_log (PROFILER_ALLOC | PROFILER_RELOC, n_bytes, FALSE); return NULL; } else { - p = standard_realloc (mem ? p : NULL, sizeof (gulong) * 2 + n_bytes); + p = realloc (mem ? p : NULL, sizeof (gsize) * 2 + n_bytes); if (p) { @@ -601,716 +828,3 @@ static GMemVTable profiler_table = { GMemVTable *glib_mem_profiler_table = &profiler_table; #endif /* !G_DISABLE_CHECKS */ - -/* --- memory slices --- */ -typedef struct { - gpointer dummy, next; -} MemSlice; - -gpointer -g_slice_alloc (guint block_size) -{ - return g_malloc (block_size); -} - -gpointer -g_slice_alloc0 (guint block_size) -{ - return g_malloc0 (block_size); -} - -void -g_slice_free1 (guint block_size, - gpointer mem_block) -{ - if (mem_block) - g_free (mem_block); -} - -void -g_slice_free_chain (guint block_size, - gpointer mem_chain, - guint next_offset) -{ - MemSlice *slice = mem_chain; - g_return_if_fail (next_offset == G_STRUCT_OFFSET (MemSlice, next)); - g_return_if_fail (block_size >= sizeof (MemSlice)); - while (slice) - { - MemSlice *current = slice; - slice = slice->next; - g_slice_free1 (block_size, current); - } -} - -/* --- MemChunks --- */ -#ifndef G_ALLOC_AND_FREE -typedef struct _GAllocator GAllocator; -typedef struct _GMemChunk GMemChunk; -#define G_ALLOC_ONLY 1 -#define G_ALLOC_AND_FREE 2 -#endif - -typedef struct _GFreeAtom GFreeAtom; -typedef struct _GMemArea GMemArea; - -struct _GFreeAtom -{ - GFreeAtom *next; -}; - -struct _GMemArea -{ - GMemArea *next; /* the next mem area */ - GMemArea *prev; /* the previous mem area */ - gulong index; /* the current index into the "mem" array */ - gulong free; /* the number of free bytes in this mem area */ - gulong allocated; /* the number of atoms allocated from this area */ - gulong mark; /* is this mem area marked for deletion */ - gchar mem[MEM_AREA_SIZE]; /* the mem array from which atoms get allocated - * the actual size of this array is determined by - * the mem chunk "area_size". ANSI says that it - * must be declared to be the maximum size it - * can possibly be (even though the actual size - * may be less). - */ -}; - -struct _GMemChunk -{ - const gchar *name; /* name of this MemChunk...used for debugging output */ - gint type; /* the type of MemChunk: ALLOC_ONLY or ALLOC_AND_FREE */ - gint num_mem_areas; /* the number of memory areas */ - gint num_marked_areas; /* the number of areas marked for deletion */ - guint atom_size; /* the size of an atom */ - gulong area_size; /* the size of a memory area */ - GMemArea *mem_area; /* the current memory area */ - GMemArea *mem_areas; /* a list of all the mem areas owned by this chunk */ - GMemArea *free_mem_area; /* the free area...which is about to be destroyed */ - GFreeAtom *free_atoms; /* the free atoms list */ - GTree *mem_tree; /* tree of mem areas sorted by memory address */ - GMemChunk *next; /* pointer to the next chunk */ - GMemChunk *prev; /* pointer to the previous chunk */ -}; - - -#ifndef DISABLE_MEM_POOLS -static gulong g_mem_chunk_compute_size (gulong size, - gulong min_size) G_GNUC_CONST; -static gint g_mem_chunk_area_compare (GMemArea *a, - GMemArea *b); -static gint g_mem_chunk_area_search (GMemArea *a, - gchar *addr); - -/* here we can't use StaticMutexes, as they depend upon a working - * g_malloc, the same holds true for StaticPrivate - */ -static GMutex *mem_chunks_lock = NULL; -static GMemChunk *mem_chunks = NULL; - -GMemChunk* -g_mem_chunk_new (const gchar *name, - gint atom_size, - gulong area_size, - gint type) -{ - GMemChunk *mem_chunk; - gulong rarea_size; - - g_return_val_if_fail (atom_size > 0, NULL); - g_return_val_if_fail (area_size >= atom_size, NULL); - - ENTER_MEM_CHUNK_ROUTINE (); - - area_size = (area_size + atom_size - 1) / atom_size; - area_size *= atom_size; - - mem_chunk = g_new (GMemChunk, 1); - mem_chunk->name = name; - mem_chunk->type = type; - mem_chunk->num_mem_areas = 0; - mem_chunk->num_marked_areas = 0; - mem_chunk->mem_area = NULL; - mem_chunk->free_mem_area = NULL; - mem_chunk->free_atoms = NULL; - mem_chunk->mem_tree = NULL; - mem_chunk->mem_areas = NULL; - mem_chunk->atom_size = atom_size; - - if (mem_chunk->type == G_ALLOC_AND_FREE) - mem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare); - - if (mem_chunk->atom_size % G_MEM_ALIGN) - mem_chunk->atom_size += G_MEM_ALIGN - (mem_chunk->atom_size % G_MEM_ALIGN); - - rarea_size = area_size + sizeof (GMemArea) - MEM_AREA_SIZE; - rarea_size = g_mem_chunk_compute_size (rarea_size, atom_size + sizeof (GMemArea) - MEM_AREA_SIZE); - mem_chunk->area_size = rarea_size - (sizeof (GMemArea) - MEM_AREA_SIZE); - - g_mutex_lock (mem_chunks_lock); - mem_chunk->next = mem_chunks; - mem_chunk->prev = NULL; - if (mem_chunks) - mem_chunks->prev = mem_chunk; - mem_chunks = mem_chunk; - g_mutex_unlock (mem_chunks_lock); - - LEAVE_MEM_CHUNK_ROUTINE (); - - return mem_chunk; -} - -void -g_mem_chunk_destroy (GMemChunk *mem_chunk) -{ - GMemArea *mem_areas; - GMemArea *temp_area; - - g_return_if_fail (mem_chunk != NULL); - - ENTER_MEM_CHUNK_ROUTINE (); - - mem_areas = mem_chunk->mem_areas; - while (mem_areas) - { - temp_area = mem_areas; - mem_areas = mem_areas->next; - g_free (temp_area); - } - - g_mutex_lock (mem_chunks_lock); - if (mem_chunk->next) - mem_chunk->next->prev = mem_chunk->prev; - if (mem_chunk->prev) - mem_chunk->prev->next = mem_chunk->next; - - if (mem_chunk == mem_chunks) - mem_chunks = mem_chunks->next; - g_mutex_unlock (mem_chunks_lock); - - if (mem_chunk->type == G_ALLOC_AND_FREE) - g_tree_destroy (mem_chunk->mem_tree); - - g_free (mem_chunk); - - LEAVE_MEM_CHUNK_ROUTINE (); -} - -gpointer -g_mem_chunk_alloc (GMemChunk *mem_chunk) -{ - GMemArea *temp_area; - gpointer mem; - - ENTER_MEM_CHUNK_ROUTINE (); - - g_return_val_if_fail (mem_chunk != NULL, NULL); - - while (mem_chunk->free_atoms) - { - /* Get the first piece of memory on the "free_atoms" list. - * We can go ahead and destroy the list node we used to keep - * track of it with and to update the "free_atoms" list to - * point to its next element. - */ - mem = mem_chunk->free_atoms; - mem_chunk->free_atoms = mem_chunk->free_atoms->next; - - /* Determine which area this piece of memory is allocated from */ - temp_area = g_tree_search (mem_chunk->mem_tree, - (GCompareFunc) g_mem_chunk_area_search, - mem); - - /* If the area has been marked, then it is being destroyed. - * (ie marked to be destroyed). - * We check to see if all of the segments on the free list that - * reference this area have been removed. This occurs when - * the ammount of free memory is less than the allocatable size. - * If the chunk should be freed, then we place it in the "free_mem_area". - * This is so we make sure not to free the mem area here and then - * allocate it again a few lines down. - * If we don't allocate a chunk a few lines down then the "free_mem_area" - * will be freed. - * If there is already a "free_mem_area" then we'll just free this mem area. - */ - if (temp_area->mark) - { - /* Update the "free" memory available in that area */ - temp_area->free += mem_chunk->atom_size; - - if (temp_area->free == mem_chunk->area_size) - { - if (temp_area == mem_chunk->mem_area) - mem_chunk->mem_area = NULL; - - if (mem_chunk->free_mem_area) - { - mem_chunk->num_mem_areas -= 1; - - if (temp_area->next) - temp_area->next->prev = temp_area->prev; - if (temp_area->prev) - temp_area->prev->next = temp_area->next; - if (temp_area == mem_chunk->mem_areas) - mem_chunk->mem_areas = mem_chunk->mem_areas->next; - - if (mem_chunk->type == G_ALLOC_AND_FREE) - g_tree_remove (mem_chunk->mem_tree, temp_area); - g_free (temp_area); - } - else - mem_chunk->free_mem_area = temp_area; - - mem_chunk->num_marked_areas -= 1; - } - } - else - { - /* Update the number of allocated atoms count. - */ - temp_area->allocated += 1; - - /* The area wasn't marked...return the memory - */ - goto outa_here; - } - } - - /* If there isn't a current mem area or the current mem area is out of space - * then allocate a new mem area. We'll first check and see if we can use - * the "free_mem_area". Otherwise we'll just malloc the mem area. - */ - if ((!mem_chunk->mem_area) || - ((mem_chunk->mem_area->index + mem_chunk->atom_size) > mem_chunk->area_size)) - { - if (mem_chunk->free_mem_area) - { - mem_chunk->mem_area = mem_chunk->free_mem_area; - mem_chunk->free_mem_area = NULL; - } - else - { -#ifdef ENABLE_GC_FRIENDLY - mem_chunk->mem_area = (GMemArea*) g_malloc0 (sizeof (GMemArea) - - MEM_AREA_SIZE + - mem_chunk->area_size); -#else /* !ENABLE_GC_FRIENDLY */ - mem_chunk->mem_area = (GMemArea*) g_malloc (sizeof (GMemArea) - - MEM_AREA_SIZE + - mem_chunk->area_size); -#endif /* ENABLE_GC_FRIENDLY */ - - mem_chunk->num_mem_areas += 1; - mem_chunk->mem_area->next = mem_chunk->mem_areas; - mem_chunk->mem_area->prev = NULL; - - if (mem_chunk->mem_areas) - mem_chunk->mem_areas->prev = mem_chunk->mem_area; - mem_chunk->mem_areas = mem_chunk->mem_area; - - if (mem_chunk->type == G_ALLOC_AND_FREE) - g_tree_insert (mem_chunk->mem_tree, mem_chunk->mem_area, mem_chunk->mem_area); - } - - mem_chunk->mem_area->index = 0; - mem_chunk->mem_area->free = mem_chunk->area_size; - mem_chunk->mem_area->allocated = 0; - mem_chunk->mem_area->mark = 0; - } - - /* Get the memory and modify the state variables appropriately. - */ - mem = (gpointer) &mem_chunk->mem_area->mem[mem_chunk->mem_area->index]; - mem_chunk->mem_area->index += mem_chunk->atom_size; - mem_chunk->mem_area->free -= mem_chunk->atom_size; - mem_chunk->mem_area->allocated += 1; - -outa_here: - - LEAVE_MEM_CHUNK_ROUTINE (); - - return mem; -} - -gpointer -g_mem_chunk_alloc0 (GMemChunk *mem_chunk) -{ - gpointer mem; - - mem = g_mem_chunk_alloc (mem_chunk); - if (mem) - { - memset (mem, 0, mem_chunk->atom_size); - } - - return mem; -} - -void -g_mem_chunk_free (GMemChunk *mem_chunk, - gpointer mem) -{ - GMemArea *temp_area; - GFreeAtom *free_atom; - - g_return_if_fail (mem_chunk != NULL); - g_return_if_fail (mem != NULL); - - ENTER_MEM_CHUNK_ROUTINE (); - -#ifdef ENABLE_GC_FRIENDLY - memset (mem, 0, mem_chunk->atom_size); -#endif /* ENABLE_GC_FRIENDLY */ - - /* Don't do anything if this is an ALLOC_ONLY chunk - */ - if (mem_chunk->type == G_ALLOC_AND_FREE) - { - /* Place the memory on the "free_atoms" list - */ - free_atom = (GFreeAtom*) mem; - free_atom->next = mem_chunk->free_atoms; - mem_chunk->free_atoms = free_atom; - - temp_area = g_tree_search (mem_chunk->mem_tree, - (GCompareFunc) g_mem_chunk_area_search, - mem); - - temp_area->allocated -= 1; - - if (temp_area->allocated == 0) - { - temp_area->mark = 1; - mem_chunk->num_marked_areas += 1; - } - } - - LEAVE_MEM_CHUNK_ROUTINE (); -} - -/* This doesn't free the free_area if there is one */ -void -g_mem_chunk_clean (GMemChunk *mem_chunk) -{ - GMemArea *mem_area; - GFreeAtom *prev_free_atom; - GFreeAtom *temp_free_atom; - gpointer mem; - - g_return_if_fail (mem_chunk != NULL); - - ENTER_MEM_CHUNK_ROUTINE (); - - if (mem_chunk->type == G_ALLOC_AND_FREE) - { - prev_free_atom = NULL; - temp_free_atom = mem_chunk->free_atoms; - - while (temp_free_atom) - { - mem = (gpointer) temp_free_atom; - - mem_area = g_tree_search (mem_chunk->mem_tree, - (GCompareFunc) g_mem_chunk_area_search, - mem); - - /* If this mem area is marked for destruction then delete the - * area and list node and decrement the free mem. - */ - if (mem_area->mark) - { - if (prev_free_atom) - prev_free_atom->next = temp_free_atom->next; - else - mem_chunk->free_atoms = temp_free_atom->next; - temp_free_atom = temp_free_atom->next; - - mem_area->free += mem_chunk->atom_size; - if (mem_area->free == mem_chunk->area_size) - { - mem_chunk->num_mem_areas -= 1; - mem_chunk->num_marked_areas -= 1; - - if (mem_area->next) - mem_area->next->prev = mem_area->prev; - if (mem_area->prev) - mem_area->prev->next = mem_area->next; - if (mem_area == mem_chunk->mem_areas) - mem_chunk->mem_areas = mem_chunk->mem_areas->next; - if (mem_area == mem_chunk->mem_area) - mem_chunk->mem_area = NULL; - - if (mem_chunk->type == G_ALLOC_AND_FREE) - g_tree_remove (mem_chunk->mem_tree, mem_area); - g_free (mem_area); - } - } - else - { - prev_free_atom = temp_free_atom; - temp_free_atom = temp_free_atom->next; - } - } - } - LEAVE_MEM_CHUNK_ROUTINE (); -} - -void -g_mem_chunk_reset (GMemChunk *mem_chunk) -{ - GMemArea *mem_areas; - GMemArea *temp_area; - - g_return_if_fail (mem_chunk != NULL); - - ENTER_MEM_CHUNK_ROUTINE (); - - mem_areas = mem_chunk->mem_areas; - mem_chunk->num_mem_areas = 0; - mem_chunk->mem_areas = NULL; - mem_chunk->mem_area = NULL; - - while (mem_areas) - { - temp_area = mem_areas; - mem_areas = mem_areas->next; - g_free (temp_area); - } - - mem_chunk->free_atoms = NULL; - - if (mem_chunk->mem_tree) - { - g_tree_destroy (mem_chunk->mem_tree); - mem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare); - } - - LEAVE_MEM_CHUNK_ROUTINE (); -} - -void -g_mem_chunk_print (GMemChunk *mem_chunk) -{ - GMemArea *mem_areas; - gulong mem; - - g_return_if_fail (mem_chunk != NULL); - - mem_areas = mem_chunk->mem_areas; - mem = 0; - - while (mem_areas) - { - mem += mem_chunk->area_size - mem_areas->free; - mem_areas = mem_areas->next; - } - - g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, - "%s: %ld bytes using %d mem areas", - mem_chunk->name, mem, mem_chunk->num_mem_areas); -} - -void -g_mem_chunk_info (void) -{ - GMemChunk *mem_chunk; - gint count; - - count = 0; - g_mutex_lock (mem_chunks_lock); - mem_chunk = mem_chunks; - while (mem_chunk) - { - count += 1; - mem_chunk = mem_chunk->next; - } - g_mutex_unlock (mem_chunks_lock); - - g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "%d mem chunks", count); - - g_mutex_lock (mem_chunks_lock); - mem_chunk = mem_chunks; - g_mutex_unlock (mem_chunks_lock); - - while (mem_chunk) - { - g_mem_chunk_print ((GMemChunk*) mem_chunk); - mem_chunk = mem_chunk->next; - } -} - -void -g_blow_chunks (void) -{ - GMemChunk *mem_chunk; - - g_mutex_lock (mem_chunks_lock); - mem_chunk = mem_chunks; - g_mutex_unlock (mem_chunks_lock); - while (mem_chunk) - { - g_mem_chunk_clean ((GMemChunk*) mem_chunk); - mem_chunk = mem_chunk->next; - } -} - -static gulong -g_mem_chunk_compute_size (gulong size, - gulong min_size) -{ - gulong power_of_2; - gulong lower, upper; - - power_of_2 = 16; - while (power_of_2 < size) - power_of_2 <<= 1; - - lower = power_of_2 >> 1; - upper = power_of_2; - - if (size - lower < upper - size && lower >= min_size) - return lower; - else - return upper; -} - -static gint -g_mem_chunk_area_compare (GMemArea *a, - GMemArea *b) -{ - if (a->mem > b->mem) - return 1; - else if (a->mem < b->mem) - return -1; - return 0; -} - -static gint -g_mem_chunk_area_search (GMemArea *a, - gchar *addr) -{ - if (a->mem <= addr) - { - if (addr < &a->mem[a->index]) - return 0; - return 1; - } - return -1; -} - -#else /* DISABLE_MEM_POOLS */ - -typedef struct { - guint alloc_size; /* the size of an atom */ -} GMinimalMemChunk; - -GMemChunk* -g_mem_chunk_new (const gchar *name, - gint atom_size, - gulong area_size, - gint type) -{ - GMinimalMemChunk *mem_chunk; - - g_return_val_if_fail (atom_size > 0, NULL); - - mem_chunk = g_new (GMinimalMemChunk, 1); - mem_chunk->alloc_size = atom_size; - - return ((GMemChunk*) mem_chunk); -} - -void -g_mem_chunk_destroy (GMemChunk *mem_chunk) -{ - g_return_if_fail (mem_chunk != NULL); - - g_free (mem_chunk); -} - -gpointer -g_mem_chunk_alloc (GMemChunk *mem_chunk) -{ - GMinimalMemChunk *minimal = (GMinimalMemChunk *)mem_chunk; - - g_return_val_if_fail (mem_chunk != NULL, NULL); - - return g_malloc (minimal->alloc_size); -} - -gpointer -g_mem_chunk_alloc0 (GMemChunk *mem_chunk) -{ - GMinimalMemChunk *minimal = (GMinimalMemChunk *)mem_chunk; - - g_return_val_if_fail (mem_chunk != NULL, NULL); - - return g_malloc0 (minimal->alloc_size); -} - -void -g_mem_chunk_free (GMemChunk *mem_chunk, - gpointer mem) -{ - g_return_if_fail (mem_chunk != NULL); - - g_free (mem); -} - -void g_mem_chunk_clean (GMemChunk *mem_chunk) {} -void g_mem_chunk_reset (GMemChunk *mem_chunk) {} -void g_mem_chunk_print (GMemChunk *mem_chunk) {} -void g_mem_chunk_info (void) {} -void g_blow_chunks (void) {} - -#endif /* DISABLE_MEM_POOLS */ - -struct _GAllocator -{ - gchar *name; - guint16 n_preallocs; - guint is_unused : 1; - guint type : 4; - GAllocator *last; - GMemChunk *mem_chunk; - gpointer free_list; -}; - -GAllocator* -g_allocator_new (const gchar *name, - guint n_preallocs) -{ - static const GAllocator dummy = { - "GAllocator is deprecated", 1, TRUE, 0, NULL, NULL, NULL, - }; - /* some (broken) GAllocator uses depend on non-NULL allocators */ - return (GAllocator*) &dummy; -} - -void -g_allocator_free (GAllocator *allocator) -{ -} - -void -_g_mem_thread_init (void) -{ -#ifndef DISABLE_MEM_POOLS - mem_chunks_lock = g_mutex_new (); -#endif -#ifndef G_DISABLE_CHECKS - g_profile_mutex = g_mutex_new (); -#endif -} - -void -_g_mem_thread_private_init (void) -{ -#ifndef G_DISABLE_CHECKS - g_assert (mem_chunk_recursion == NULL); - mem_chunk_recursion = g_private_new (NULL); -#endif -} - -#define __G_MEM_C__ -#include "galiasdef.c"