+Thu Dec 1 17:32:46 2005 Tim Janik <timj@imendio.com>
+
+ * glib/gslice.[hc]: new slice allocator implementation.
+
+ * tests/slice-test.c: added random slice allocation test.
+
+ * glib/gthread.[hc]: removed newly added private thread mem API.
+
+ * glib/gthreadinit.h:
+ * glib/gmessages.c:
+ * glib/gthread.c:
+ * glib/gmem.c: divided glib threading initialisation into three phases,
+ initialisation where private keys and messaging are not available (only
+ needed by gmem.c), initialisation without messaging but private keys
+ available (gslice.c, gmessage.c), and full fledged initialisers that
+ server the rest of glib. initialisation functions got renamed to reflect
+ the limitations of their corresponding phases.
+
+ * glib/gmem.c: removed memchunk code, defer allocations to
+ g_slice_* instead.
+
+ * glib/gmem.[hc]: removed g_slice_* skeletons.
+
+ * glib/glib.symbols: added g_slice_* symbols.
+
+ * configure.in: check for availability of posix_memalign(3), memalign(3)
+ and valloc(3).
+
+ * glib/Makefile.am: added gslice.[hc].
+
2005-12-01 Tor Lillqvist <tml@novell.com>
* glib/gstdio.c (g_stat): In the Win32 implementation, strip
+Thu Dec 1 17:32:46 2005 Tim Janik <timj@imendio.com>
+
+ * glib/gslice.[hc]: new slice allocator implementation.
+
+ * tests/slice-test.c: added random slice allocation test.
+
+ * glib/gthread.[hc]: removed newly added private thread mem API.
+
+ * glib/gthreadinit.h:
+ * glib/gmessages.c:
+ * glib/gthread.c:
+ * glib/gmem.c: divided glib threading initialisation into three phases,
+ initialisation where private keys and messaging are not available (only
+ needed by gmem.c), initialisation without messaging but private keys
+ available (gslice.c, gmessage.c), and full fledged initialisers that
+ server the rest of glib. initialisation functions got renamed to reflect
+ the limitations of their corresponding phases.
+
+ * glib/gmem.c: removed memchunk code, defer allocations to
+ g_slice_* instead.
+
+ * glib/gmem.[hc]: removed g_slice_* skeletons.
+
+ * glib/glib.symbols: added g_slice_* symbols.
+
+ * configure.in: check for availability of posix_memalign(3), memalign(3)
+ and valloc(3).
+
+ * glib/Makefile.am: added gslice.[hc].
+
2005-12-01 Tor Lillqvist <tml@novell.com>
* glib/gstdio.c (g_stat): In the Win32 implementation, strip
+Thu Dec 1 17:32:46 2005 Tim Janik <timj@imendio.com>
+
+ * glib/gslice.[hc]: new slice allocator implementation.
+
+ * tests/slice-test.c: added random slice allocation test.
+
+ * glib/gthread.[hc]: removed newly added private thread mem API.
+
+ * glib/gthreadinit.h:
+ * glib/gmessages.c:
+ * glib/gthread.c:
+ * glib/gmem.c: divided glib threading initialisation into three phases,
+ initialisation where private keys and messaging are not available (only
+ needed by gmem.c), initialisation without messaging but private keys
+ available (gslice.c, gmessage.c), and full fledged initialisers that
+ server the rest of glib. initialisation functions got renamed to reflect
+ the limitations of their corresponding phases.
+
+ * glib/gmem.c: removed memchunk code, defer allocations to
+ g_slice_* instead.
+
+ * glib/gmem.[hc]: removed g_slice_* skeletons.
+
+ * glib/glib.symbols: added g_slice_* symbols.
+
+ * configure.in: check for availability of posix_memalign(3), memalign(3)
+ and valloc(3).
+
+ * glib/Makefile.am: added gslice.[hc].
+
2005-12-01 Tor Lillqvist <tml@novell.com>
* glib/gstdio.c (g_stat): In the Win32 implementation, strip
AC_FUNC_VPRINTF
AC_FUNC_MMAP
AC_FUNC_ALLOCA
+AC_CHECK_FUNCS(posix_memalign)
+AC_CHECK_FUNCS(memalign)
+AC_CHECK_FUNCS(valloc)
AC_CHECK_FUNCS(atexit on_exit)
grand.c \
gscanner.c \
gshell.c \
+ gslice.c \
gslist.c \
gstdio.c \
gstrfuncs.c \
grel.h \
gscanner.h \
gshell.h \
+ gslice.h \
gslist.h \
gspawn.h \
gstdio.h \
g_try_malloc G_GNUC_MALLOC
g_try_malloc0 G_GNUC_MALLOC
g_try_realloc
-g_slice_alloc
-g_slice_alloc0
-g_slice_free1
-g_slice_free_chain
#ifndef G_DISABLE_DEPRECATED
g_allocator_free
g_allocator_new
#endif
#endif
+#if IN_HEADER(__G_SLICE_H__)
+#if IN_FILE(__G_SLICE_C__)
+g_slice_alloc G_GNUC_MALLOC
+g_slice_alloc0 G_GNUC_MALLOC
+g_slice_free1
+g_slice_free_chain
+g_slice_set_config
+g_slice_get_config
+g_slice_get_config_state
+#endif
+#endif
+
#if IN_HEADER(__G_MESSAGES_H__)
#if IN_FILE(__G_MESSAGES_C__)
g_printf_string_upper_bound
#include "galias.h"
/* 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.
#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,
} 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;
+static GMutex *gmem_profile_mutex = NULL;
#ifdef G_ENABLE_DEBUG
static volatile gulong g_trap_free_size = 0;
static volatile gulong g_trap_realloc_size = 0;
gulong 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]));
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
gulong local_allocs;
gulong local_zinit;
gulong local_frees;
- gulong local_mc_allocs;
- gulong local_mc_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);
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
#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;
#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 */
+struct _GMemChunk {
+ guint alloc_size; /* the size of an atom */
};
-
-#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,
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 = g_slice_new (GMemChunk);
mem_chunk->alloc_size = atom_size;
-
- return ((GMemChunk*) mem_chunk);
+ return mem_chunk;
}
void
{
g_return_if_fail (mem_chunk != NULL);
- g_free (mem_chunk);
+ g_slice_free (GMemChunk, 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);
+ return g_slice_alloc (mem_chunk->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);
+ return g_slice_alloc0 (mem_chunk->alloc_size);
}
void
{
g_return_if_fail (mem_chunk != NULL);
- g_free (mem);
+ g_slice_free1 (mem_chunk->alloc_size, mem);
}
void g_mem_chunk_clean (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 = {
+ static const struct _GAllocator {
+ gchar *name;
+ guint16 n_preallocs;
+ guint is_unused : 1;
+ guint type : 4;
+ GAllocator *last;
+ GMemChunk *mem_chunk;
+ gpointer free_list;
+ } dummy = {
"GAllocator is deprecated", 1, TRUE, 0, NULL, NULL, NULL,
};
/* some (broken) GAllocator uses depend on non-NULL allocators */
- return (GAllocator*) &dummy;
+ return (void*) &dummy;
}
void
}
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)
+_g_mem_thread_init_noprivate_nomessage (void)
{
+ /* we may only create mutexes here, locking/unlocking itself
+ * does not yet work.
+ */
#ifndef G_DISABLE_CHECKS
- g_assert (mem_chunk_recursion == NULL);
- mem_chunk_recursion = g_private_new (NULL);
+ gmem_profile_mutex = g_mutex_new ();
#endif
}
#ifndef __G_MEM_H__
#define __G_MEM_H__
+#include <glib/gslice.h>
#include <glib/gtypes.h>
G_BEGIN_DECLS
GLIB_VAR GMemVTable *glib_mem_profiler_table;
void g_mem_profile (void);
-/* slices - fast allocation/release of small memory blocks
- */
-gpointer g_slice_alloc (guint block_size);
-gpointer g_slice_alloc0 (guint block_size);
-void g_slice_free1 (guint block_size,
- gpointer mem_block);
-void g_slice_free_chain (guint block_size,
- gpointer mem_chain,
- guint next_offset);
-#define g_slice_new(type) ((type*) g_slice_alloc (sizeof (type)))
-#define g_slice_new0(type) ((type*) g_slice_alloc0 (sizeof (type)))
-#define g_slice_free(type,mem) g_slice_free1 (sizeof (type), mem)
-
/* deprecated memchunks and allocators */
#if !defined G_DISABLE_DEPRECATED || 1
}
void
-_g_messages_thread_init (void)
+_g_messages_thread_init_nomessage (void)
{
g_messages_lock = g_mutex_new ();
+ g_log_depth = g_private_new (NULL);
g_messages_prefixed_init ();
_g_debug_init ();
}
-void
-_g_messages_thread_private_init (void)
-{
- g_assert (g_log_depth == NULL);
- g_log_depth = g_private_new (NULL);
-}
-
gboolean _g_debug_initialized = FALSE;
guint _g_debug_flags = 0;
--- /dev/null
+/* GLIB sliced memory - fast concurrent memory chunk allocator
+ * Copyright (C) 2005 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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.
+ */
+/* MT safe */
+#define _XOPEN_SOURCE 600 /* posix_memalign() */
+#include <stdlib.h> /* posix_memalign() */
+#include <unistd.h> /* sysconf() */
+#include <assert.h> /* assert() for nomessage phase */
+#include <string.h>
+#include <errno.h>
+#include "config.h"
+#include "gmem.h" /* gslice.h */
+#include "gthreadinit.h"
+#include "galias.h"
+#include "glib.h"
+
+/* the GSlice allocator is split up into 4 layers, roughly modelled after the slab
+ * allocator and magazine extensions as outlined in:
+ * + [Bonwick94] Jeff Bonwick, The slab allocator: An object-caching kernel
+ * memory allocator. USENIX 1994, http://citeseer.ist.psu.edu/bonwick94slab.html
+ * + [Bonwick01] Bonwick and Jonathan Adams, Magazines and vmem: Extending the
+ * slab allocator to many cpu's and arbitrary resources.
+ * USENIX 2001, http://citeseer.ist.psu.edu/bonwick01magazines.html
+ * the layers are:
+ * - the thread magazines. for each (aligned) chunk size, a magazine (a list)
+ * of recently freed and soon to be allocated chunks is maintained per thread.
+ * this way, most alloc/free requests can be quickly satisfied from per-thread
+ * free lists which only require one g_private_get() call to retrive the
+ * thread handle.
+ * - the magazine cache. allocating and freeing chunks to/from threads only
+ * occours at magazine sizes from a global depot of magazines. the depot
+ * maintaines a 15 second working set of allocated magazines, so full
+ * magazines are not allocated and released too often.
+ * the chunk size dependent magazine sizes automatically adapt (within limits,
+ * see [3]) to lock contention to properly scale performance across a variety
+ * of SMP systems.
+ * - the slab allocator. this allocator allocates slabs (blocks of memory) close
+ * to the system page size or multiples thereof which have to be page aligned.
+ * the blocks are divided into smaller chunks which are used to satisfy
+ * allocations from the upper layers. the space provided by the reminder of
+ * the chunk size division is used for cache colorization (random distribution
+ * of chunk addresses) to improve processor cache utilization. multiple slabs
+ * with the same chunk size are kept in a partially sorted ring to allow O(1)
+ * freeing and allocation of chunks (as long as the allocation of an entirely
+ * new slab can be avoided).
+ * - the page allocator. on most modern systems, posix_memalign(3) or
+ * memalign(3) should be available, so this is used to allocate blocks with
+ * system page size based alignments and sizes or multiples thereof.
+ * if no memalign variant is provided, valloc() is used instead and
+ * block sizes are limited to the system page size (no multiples thereof).
+ * as a fallback, on system without even valloc(), a malloc(3)-based page
+ * allocator with alloc-only behaviour is used.
+ *
+ * NOTES:
+ * [1] some systems memalign(3) implementations may rely on boundary tagging for
+ * the handed out memory chunks. to avoid excessive page-wise fragmentation,
+ * we reserve 2 * sizeof (void*) per block size for the systems memalign(3),
+ * specified in NATIVE_MALLOC_PADDING.
+ * [2] using the slab allocator alone already provides for a fast and efficient
+ * allocator, it doesn't properly scale beyond single-threaded uses though.
+ * also, the slab allocator implements eager free(3)-ing, i.e. does not
+ * provide any form of caching or working set maintenance. so if used alone,
+ * it's vulnerable to trashing for sequences of balanced (alloc, free) pairs
+ * at certain thresholds.
+ * [3] magazine sizes are bound by an implementation specific minimum size and
+ * a chunk size specific maximum to limit magazine storage sizes to roughly
+ * 16KB.
+ * [4] allocating ca. 8 chunks per block/page keeps a good balance between
+ * external and internal fragmentation (<= 12.5%) [Bonwick94]
+ */
+
+/* --- macros and constants --- */
+#define LARGEALIGNMENT (256)
+#define P2ALIGNMENT (2 * sizeof (gsize)) /* fits 2 pointers (assumed to be 2 * GLIB_SIZEOF_SIZE_T below) */
+#define ALIGN(size, base) ((base) * (gsize) (((size) + (base) - 1) / (base)))
+#define NATIVE_MALLOC_PADDING P2ALIGNMENT /* per-page padding left for native malloc(3) see [1] */
+#define SLAB_INFO_SIZE P2ALIGN (sizeof (SlabInfo) + NATIVE_MALLOC_PADDING)
+#define MAX_MAGAZINE_SIZE (256) /* see [3] and allocator_get_magazine_threshold() for this */
+#define MIN_MAGAZINE_SIZE (4)
+#define MAX_STAMP_COUNTER (13) /* distributes the load of gettimeofday() */
+#define MAX_SLAB_CHUNK_SIZE(al) (((al)->max_page_size - SLAB_INFO_SIZE) / 8) /* we want at last 8 chunks per page, see [4] */
+#define MAX_SLAB_INDEX(al) (SLAB_INDEX (al, MAX_SLAB_CHUNK_SIZE (al)) + 1)
+#define SLAB_INDEX(al, asize) ((asize) / P2ALIGNMENT - 1) /* asize must be P2ALIGNMENT aligned */
+#define SLAB_CHUNK_SIZE(al, ix) (((ix) + 1) * P2ALIGNMENT)
+#define SLAB_PAGE_SIZE(al,csz) (ALIGN (8 * (csz) + SLAB_INFO_SIZE, (al)->min_page_size))
+
+/* optimized version of ALIGN (size, P2ALIGNMENT) */
+#if GLIB_SIZEOF_SIZE_T * 2 == 8 /* P2ALIGNMENT */
+#define P2ALIGN(size) (((size) + 0x7) & ~(gsize) 0x7)
+#elif GLIB_SIZEOF_SIZE_T * 2 == 16 /* P2ALIGNMENT */
+#define P2ALIGN(size) (((size) + 0xf) & ~(gsize) 0xf)
+#else
+#define P2ALIGN(size) ALIGN (size, P2ALIGNMENT)
+#endif
+
+/* --- structures --- */
+typedef struct _ChunkLink ChunkLink;
+typedef struct _SlabInfo SlabInfo;
+typedef struct _CachedMagazine CachedMagazine;
+struct _ChunkLink {
+ ChunkLink *next;
+ ChunkLink *data;
+};
+struct _SlabInfo {
+ ChunkLink *chunks;
+ guint n_allocated;
+ SlabInfo *next, *prev;
+};
+typedef struct {
+ ChunkLink *chunks;
+ gsize count; /* approximative chunks list length */
+} Magazine;
+typedef struct {
+ Magazine *magazine1; /* array of MAX_SLAB_INDEX (allocator) */
+ Magazine *magazine2; /* array of MAX_SLAB_INDEX (allocator) */
+} ThreadMemory;
+typedef struct {
+ gboolean always_malloc;
+ gboolean bypass_magazines;
+ gboolean always_free;
+ gsize working_set_msecs;
+} SliceConfig;
+typedef struct {
+ /* const after initialization */
+ gsize min_page_size, max_page_size;
+ SliceConfig config;
+ guint max_slab_chunk_size_for_magazine_cache;
+ /* magazine cache */
+ GMutex *magazine_mutex;
+ ChunkLink **magazines; /* array of MAX_SLAB_INDEX (allocator) */
+ guint *contention_counters; /* array of MAX_SLAB_INDEX (allocator) */
+ gint mutex_counter;
+ guint stamp_counter;
+ guint last_stamp;
+ /* slab allocator */
+ GMutex *slab_mutex;
+ SlabInfo **slab_stack; /* array of MAX_SLAB_INDEX (allocator) */
+ guint color_accu;
+} Allocator;
+
+/* --- prototypes --- */
+static gpointer slab_allocator_alloc_chunk (guint chunk_size);
+static void slab_allocator_free_chunk (guint chunk_size,
+ gpointer mem);
+static void private_thread_memory_cleanup (gpointer data);
+static gpointer allocator_memalign (gsize alignment,
+ gsize memsize);
+static void allocator_memfree (gsize memsize,
+ gpointer mem);
+static inline void magazine_cache_update_stamp (void);
+static inline guint allocator_get_magazine_threshold (Allocator *allocator,
+ guint ix);
+
+/* --- variables --- */
+static GPrivate *private_thread_memory = NULL;
+static gsize sys_page_size = 0;
+static Allocator allocator[1] = { { 0, }, };
+static SliceConfig slice_config = {
+ FALSE, /* always_malloc */
+ FALSE, /* bypass_magazines */
+ FALSE, /* always_free */
+ 15 * 1000, /* working_set_msecs */
+};
+
+/* --- auxillary funcitons --- */
+void
+g_slice_set_config (GSliceConfig ckey,
+ gint64 value)
+{
+ g_return_if_fail (sys_page_size == 0);
+ switch (ckey)
+ {
+ case G_SLICE_CONFIG_ALWAYS_MALLOC:
+ slice_config.always_malloc = value != 0;
+ break;
+ case G_SLICE_CONFIG_BYPASS_MAGAZINES:
+ slice_config.bypass_magazines = value != 0;
+ break;
+ case G_SLICE_CONFIG_ALWAYS_FREE:
+ slice_config.always_free = value != 0;
+ break;
+ case G_SLICE_CONFIG_WORKING_SET_MSECS:
+ slice_config.working_set_msecs = value;
+ break;
+ default: ;
+ }
+}
+
+gint64
+g_slice_get_config (GSliceConfig ckey)
+{
+ switch (ckey)
+ {
+ case G_SLICE_CONFIG_ALWAYS_MALLOC:
+ return slice_config.always_malloc;
+ case G_SLICE_CONFIG_BYPASS_MAGAZINES:
+ return slice_config.bypass_magazines;
+ case G_SLICE_CONFIG_ALWAYS_FREE:
+ return slice_config.always_free;
+ case G_SLICE_CONFIG_WORKING_SET_MSECS:
+ return slice_config.working_set_msecs;
+ case G_SLICE_CONFIG_CHUNK_SIZES:
+ return MAX_SLAB_INDEX (allocator);
+ default:
+ return 0;
+ }
+}
+
+gint64*
+g_slice_get_config_state (GSliceConfig ckey,
+ gint64 address,
+ guint *n_values)
+{
+ guint i = 0;
+ g_return_val_if_fail (n_values != NULL, NULL);
+ *n_values = 0;
+ switch (ckey)
+ {
+ gint64 array[64];
+ case G_SLICE_CONFIG_CONTENTION_COUNTER:
+ array[i++] = SLAB_CHUNK_SIZE (allocator, address);
+ array[i++] = allocator->contention_counters[address];
+ array[i++] = allocator_get_magazine_threshold (allocator, address);
+ *n_values = i;
+ return g_memdup (array, sizeof (array[0]) * *n_values);
+ default:
+ return NULL;
+ }
+}
+
+static void
+g_slice_init_nomessage (void)
+{
+ /* we may not use g_error() or friends here */
+ assert (sys_page_size == 0);
+
+ sys_page_size = sysconf (_SC_PAGESIZE); /* = sysconf (_SC_PAGE_SIZE); = getpagesize(); */
+ assert (sys_page_size >= 2 * LARGEALIGNMENT);
+ allocator->config = slice_config;
+ allocator->min_page_size = sys_page_size;
+#if HAVE_POSIX_MEMALIGN || HAVE_MEMALIGN
+ /* allow allocation of pages up to 8KB (with 8KB alignment).
+ * this is useful because many medium to large sized structures
+ * fit less than 8 times (see [4]) into 4KB pages.
+ */
+ allocator->min_page_size = MAX (allocator->min_page_size, 4096);
+ allocator->max_page_size = MAX (allocator->min_page_size, 8192);
+#else
+ /* we can only align to system page size */
+ allocator->max_page_size = sys_page_size;
+#endif
+ allocator->magazine_mutex = NULL; /* _g_slice_thread_init_nomessage() */
+ allocator->magazines = g_new0 (ChunkLink*, MAX_SLAB_INDEX (allocator));
+ allocator->contention_counters = g_new0 (guint, MAX_SLAB_INDEX (allocator));
+ allocator->mutex_counter = 0;
+ allocator->stamp_counter = MAX_STAMP_COUNTER; /* force initial update */
+ allocator->last_stamp = 0;
+ allocator->slab_mutex = NULL; /* _g_slice_thread_init_nomessage() */
+ allocator->slab_stack = g_new0 (SlabInfo*, MAX_SLAB_INDEX (allocator));
+ allocator->color_accu = 0;
+ magazine_cache_update_stamp();
+ /* values cached for performance reasons */
+ allocator->max_slab_chunk_size_for_magazine_cache = MAX_SLAB_CHUNK_SIZE (allocator);
+ if (allocator->config.always_malloc || allocator->config.bypass_magazines)
+ allocator->max_slab_chunk_size_for_magazine_cache = 0; /* non-optimized cases */
+}
+
+static inline guint
+allocator_categorize (guint aligned_chunk_size)
+{
+ /* speed up the likely path */
+ if (G_LIKELY (aligned_chunk_size && aligned_chunk_size <= allocator->max_slab_chunk_size_for_magazine_cache))
+ return 1; /* use magazine cache */
+
+ /* the above will fail (max_slab_chunk_size_for_magazine_cache == 0) if the
+ * allocator is still uninitialized, or if we are not configured to use the
+ * magazine cache.
+ */
+ if (!sys_page_size)
+ g_slice_init_nomessage ();
+ if (!allocator->config.always_malloc &&
+ aligned_chunk_size &&
+ aligned_chunk_size <= MAX_SLAB_CHUNK_SIZE (allocator))
+ {
+ if (allocator->config.bypass_magazines)
+ return 2; /* use slab allocator, see [2] */
+ return 1; /* use magazine cache */
+ }
+ return 0; /* use malloc() */
+}
+
+void
+_g_slice_thread_init_nomessage (void)
+{
+ /* we may not use g_error() or friends here */
+ if (!sys_page_size)
+ g_slice_init_nomessage();
+ private_thread_memory = g_private_new (private_thread_memory_cleanup);
+ allocator->magazine_mutex = g_mutex_new();
+ allocator->slab_mutex = g_mutex_new();
+}
+
+static inline void
+g_mutex_lock_a (GMutex *mutex,
+ guint *threshold)
+{
+ gboolean contention = FALSE;
+ if (!g_mutex_trylock (mutex))
+ {
+ g_mutex_lock (mutex);
+ contention = TRUE;
+ }
+ if (contention)
+ {
+ allocator->mutex_counter++;
+ if (allocator->mutex_counter >= 1) /* quickly adapt to contention */
+ {
+ allocator->mutex_counter = 0;
+ *threshold = MIN (*threshold + 1, MAX_MAGAZINE_SIZE);
+ }
+ }
+ else /* !contention */
+ {
+ allocator->mutex_counter--;
+ if (allocator->mutex_counter < -11) /* moderately recover magazine sizes */
+ {
+ allocator->mutex_counter = 0;
+ *threshold = MAX (*threshold, 1) - 1;
+ }
+ }
+}
+
+static inline ThreadMemory*
+thread_memory_from_self (void)
+{
+ ThreadMemory *tmem = g_private_get (private_thread_memory);
+ if (G_UNLIKELY (!tmem))
+ {
+ const guint n_magazines = MAX_SLAB_INDEX (allocator);
+ tmem = g_malloc0 (sizeof (ThreadMemory) + sizeof (Magazine) * 2 * n_magazines);
+ tmem->magazine1 = (Magazine*) (tmem + 1);
+ tmem->magazine2 = &tmem->magazine1[n_magazines];
+ g_private_set (private_thread_memory, tmem);
+ }
+ return tmem;
+}
+
+static inline ChunkLink*
+magazine_chain_pop_head (ChunkLink **magazine_chunks)
+{
+ /* magazine chains are linked via ChunkLink->next.
+ * each ChunkLink->data of the toplevel chain may point to a subchain,
+ * linked via ChunkLink->next. ChunkLink->data of the subchains just
+ * contains uninitialized junk.
+ */
+ ChunkLink *chunk = (*magazine_chunks)->data;
+ if (G_UNLIKELY (chunk))
+ {
+ /* allocating from freed list */
+ (*magazine_chunks)->data = chunk->next;
+ }
+ else
+ {
+ chunk = *magazine_chunks;
+ *magazine_chunks = chunk->next;
+ }
+ return chunk;
+}
+
+static guint
+magazine_count (ChunkLink *head)
+{
+ guint count = 0;
+ if (!head)
+ return 0;
+ while (head)
+ {
+ ChunkLink *child = head->data;
+ count += 1;
+ for (child = head->data; child; child = child->next)
+ count += 1;
+ head = head->next;
+ }
+ return count;
+}
+
+static inline guint
+allocator_get_magazine_threshold (Allocator *allocator,
+ guint ix)
+{
+ /* the magazine size calculated here has a lower bound of MIN_MAGAZINE_SIZE,
+ * which is required by the implementation. also, for moderately sized chunks
+ * (say >= 64 bytes), magazine sizes shouldn't be much smaller then the number
+ * of chunks available per page to avoid excessive traffic in the magazine
+ * cache for small to medium sized structures.
+ * the upper bound of the magazine size is effectively provided by
+ * MAX_MAGAZINE_SIZE. for larger chunks, this number is scaled down so that
+ * the content of a single magazine doesn't exceed ca. 16KB.
+ */
+ guint chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
+ guint threshold = MAX (MIN_MAGAZINE_SIZE, sys_page_size / MAX (chunk_size, 64));
+ guint contention_counter = allocator->contention_counters[ix];
+ if (G_UNLIKELY (contention_counter)) /* single CPU bias */
+ {
+ /* adapt contention counter thresholds to chunk sizes */
+ contention_counter = contention_counter * 64 / chunk_size;
+ threshold = MAX (threshold, contention_counter);
+ }
+ return threshold;
+}
+
+/* --- magazine cache --- */
+static inline void
+magazine_cache_update_stamp (void)
+{
+ if (allocator->stamp_counter >= MAX_STAMP_COUNTER)
+ {
+ GTimeVal tv;
+ g_get_current_time (&tv);
+ allocator->last_stamp = tv.tv_sec * 1000 + tv.tv_usec / 1000; /* milli seconds */
+ allocator->stamp_counter = 0;
+ }
+ else
+ allocator->stamp_counter++;
+}
+
+static inline ChunkLink*
+magazine_chain_prepare_fields (ChunkLink *magazine_chunks)
+{
+ g_assert (MIN_MAGAZINE_SIZE >= 4);
+ /* ensure a magazine with at least 4 unused data pointers */
+ ChunkLink *chunk1 = magazine_chain_pop_head (&magazine_chunks);
+ ChunkLink *chunk2 = magazine_chain_pop_head (&magazine_chunks);
+ ChunkLink *chunk3 = magazine_chain_pop_head (&magazine_chunks);
+ ChunkLink *chunk4 = magazine_chain_pop_head (&magazine_chunks);
+ chunk4->next = magazine_chunks;
+ chunk3->next = chunk4;
+ chunk2->next = chunk3;
+ chunk1->next = chunk2;
+ return chunk1;
+}
+
+/* access the first 3 fields of a specially prepared magazine chain */
+#define magazine_chain_prev(mc) ((mc)->data)
+#define magazine_chain_stamp(mc) ((mc)->next->data)
+#define magazine_chain_next(mc) ((mc)->next->next->data)
+#define magazine_chain_count(mc) ((mc)->next->next->next->data)
+
+static void
+magazine_cache_trim (Allocator *allocator,
+ guint ix,
+ guint stamp)
+{
+ /* g_mutex_lock (allocator->mutex); done by caller */
+ /* trim magazine cache from tail */
+ ChunkLink *current = magazine_chain_prev (allocator->magazines[ix]);
+ ChunkLink *trash = NULL;
+ while (allocator->config.always_free ||
+ ABS (stamp - (guint) magazine_chain_stamp (current)) > allocator->config.working_set_msecs)
+ {
+ /* unlink */
+ ChunkLink *prev = magazine_chain_prev (current);
+ ChunkLink *next = magazine_chain_next (current);
+ magazine_chain_next (prev) = next;
+ magazine_chain_prev (next) = prev;
+ /* clear special fields, put on trash stack */
+ magazine_chain_next (current) = NULL;
+ magazine_chain_count (current) = NULL;
+ magazine_chain_stamp (current) = NULL;
+ magazine_chain_prev (current) = trash;
+ trash = current;
+ /* fixup list head if required */
+ if (current == allocator->magazines[ix])
+ {
+ allocator->magazines[ix] = NULL;
+ break;
+ }
+ current = prev;
+ }
+ g_mutex_unlock (allocator->magazine_mutex);
+ /* free trash */
+ if (trash)
+ {
+ const guint chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
+ g_mutex_lock (allocator->slab_mutex);
+ while (trash)
+ {
+ current = trash;
+ trash = magazine_chain_prev (current);
+ magazine_chain_prev (current) = NULL; /* clear special field */
+ while (current)
+ {
+ ChunkLink *chunk = magazine_chain_pop_head (¤t);
+ slab_allocator_free_chunk (chunk_size, chunk);
+ }
+ }
+ g_mutex_unlock (allocator->slab_mutex);
+ }
+}
+
+static void
+magazine_cache_push_magazine (guint ix,
+ ChunkLink *magazine_chunks,
+ gsize count) /* must be >= MIN_MAGAZINE_SIZE */
+{
+ ChunkLink *current = magazine_chain_prepare_fields (magazine_chunks);
+ ChunkLink *next, *prev;
+ g_mutex_lock (allocator->magazine_mutex);
+ /* add magazine at head */
+ next = allocator->magazines[ix];
+ if (next)
+ prev = magazine_chain_prev (next);
+ else
+ next = prev = current;
+ magazine_chain_next (prev) = current;
+ magazine_chain_prev (next) = current;
+ magazine_chain_prev (current) = prev;
+ magazine_chain_next (current) = next;
+ magazine_chain_count (current) = (gpointer) count;
+ /* stamp magazine */
+ magazine_cache_update_stamp();
+ magazine_chain_stamp (current) = (gpointer) allocator->last_stamp;
+ allocator->magazines[ix] = current;
+ /* free old magazines beyond a certain threshold */
+ magazine_cache_trim (allocator, ix, allocator->last_stamp);
+ /* g_mutex_unlock (allocator->mutex); was done by magazine_cache_trim() */
+}
+
+static ChunkLink*
+magazine_cache_pop_magazine (guint ix,
+ gsize *countp)
+{
+ g_mutex_lock_a (allocator->magazine_mutex, &allocator->contention_counters[ix]);
+ if (!allocator->magazines[ix])
+ {
+ guint magazine_threshold = allocator_get_magazine_threshold (allocator, ix);
+ gsize i, chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
+ ChunkLink *current = NULL;
+ g_mutex_unlock (allocator->magazine_mutex);
+ g_mutex_lock (allocator->slab_mutex);
+ for (i = 0; i < magazine_threshold; i++)
+ {
+ ChunkLink *chunk = slab_allocator_alloc_chunk (chunk_size);
+ chunk->data = NULL;
+ chunk->next = current;
+ current = chunk;
+ }
+ g_mutex_unlock (allocator->slab_mutex);
+ *countp = i;
+ return current;
+ }
+ else
+ {
+ ChunkLink *current = allocator->magazines[ix];
+ ChunkLink *prev = magazine_chain_prev (current);
+ ChunkLink *next = magazine_chain_next (current);
+ /* unlink */
+ magazine_chain_next (prev) = next;
+ magazine_chain_prev (next) = prev;
+ allocator->magazines[ix] = next == current ? NULL : next;
+ g_mutex_unlock (allocator->magazine_mutex);
+ /* clear special fields and hand out */
+ *countp = (gsize) magazine_chain_count (current);
+ magazine_chain_prev (current) = NULL;
+ magazine_chain_next (current) = NULL;
+ magazine_chain_count (current) = NULL;
+ magazine_chain_stamp (current) = NULL;
+ return current;
+ }
+}
+
+/* --- thread magazines --- */
+static void
+private_thread_memory_cleanup (gpointer data)
+{
+ ThreadMemory *tmem = data;
+ const guint n_magazines = MAX_SLAB_INDEX (allocator);
+ guint ix;
+ for (ix = 0; ix < n_magazines; ix++)
+ {
+ Magazine *mags[2];
+ guint j;
+ mags[0] = &tmem->magazine1[ix];
+ mags[1] = &tmem->magazine2[ix];
+ for (j = 0; j < 2; j++)
+ {
+ Magazine *mag = mags[j];
+ if (mag->count >= MIN_MAGAZINE_SIZE)
+ magazine_cache_push_magazine (ix, mag->chunks, mag->count);
+ else
+ {
+ const guint chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
+ g_mutex_lock (allocator->slab_mutex);
+ while (mag->chunks)
+ {
+ ChunkLink *chunk = magazine_chain_pop_head (&mag->chunks);
+ slab_allocator_free_chunk (chunk_size, chunk);
+ }
+ g_mutex_unlock (allocator->slab_mutex);
+ }
+ }
+ }
+ g_free (tmem);
+}
+
+static void
+thread_memory_magazine1_reload (ThreadMemory *tmem,
+ guint ix)
+{
+ Magazine *mag = &tmem->magazine1[ix];
+ g_assert (mag->chunks == NULL); /* ensure that we may reset mag->count */
+ mag->count = 0;
+ mag->chunks = magazine_cache_pop_magazine (ix, &mag->count);
+}
+
+static void
+thread_memory_magazine2_unload (ThreadMemory *tmem,
+ guint ix)
+{
+ Magazine *mag = &tmem->magazine2[ix];
+ magazine_cache_push_magazine (ix, mag->chunks, mag->count);
+ mag->chunks = NULL;
+ mag->count = 0;
+}
+
+static inline void
+thread_memory_swap_magazines (ThreadMemory *tmem,
+ guint ix)
+{
+ Magazine xmag = tmem->magazine1[ix];
+ tmem->magazine1[ix] = tmem->magazine2[ix];
+ tmem->magazine2[ix] = xmag;
+}
+
+static inline gboolean
+thread_memory_magazine1_is_empty (ThreadMemory *tmem,
+ guint ix)
+{
+ return tmem->magazine1[ix].chunks == NULL;
+}
+
+static inline gboolean
+thread_memory_magazine2_is_full (ThreadMemory *tmem,
+ guint ix)
+{
+ return tmem->magazine2[ix].count >= allocator_get_magazine_threshold (allocator, ix);
+}
+
+static inline gpointer
+thread_memory_magazine1_alloc (ThreadMemory *tmem,
+ guint ix)
+{
+ Magazine *mag = &tmem->magazine1[ix];
+ ChunkLink *chunk = magazine_chain_pop_head (&mag->chunks);
+ if (G_LIKELY (mag->count > 0))
+ mag->count--;
+ return chunk;
+}
+
+static inline void
+thread_memory_magazine2_free (ThreadMemory *tmem,
+ guint ix,
+ gpointer mem)
+{
+ Magazine *mag = &tmem->magazine2[ix];
+ ChunkLink *chunk = mem;
+ chunk->data = NULL;
+ chunk->next = mag->chunks;
+ mag->chunks = chunk;
+ mag->count++;
+}
+
+/* --- API functions --- */
+gpointer
+g_slice_alloc (gsize mem_size)
+{
+ gsize chunk_size;
+ gpointer mem;
+ guint acat;
+ chunk_size = P2ALIGN (mem_size);
+ acat = allocator_categorize (chunk_size);
+ if (G_LIKELY (acat == 1)) /* allocate through magazine layer */
+ {
+ ThreadMemory *tmem = thread_memory_from_self();
+ guint ix = SLAB_INDEX (allocator, chunk_size);
+ if (G_UNLIKELY (thread_memory_magazine1_is_empty (tmem, ix)))
+ {
+ thread_memory_swap_magazines (tmem, ix);
+ if (G_UNLIKELY (thread_memory_magazine1_is_empty (tmem, ix)))
+ thread_memory_magazine1_reload (tmem, ix);
+ }
+ mem = thread_memory_magazine1_alloc (tmem, ix);
+ }
+ else if (acat == 2) /* allocate through slab allocator */
+ {
+ g_mutex_lock (allocator->slab_mutex);
+ mem = slab_allocator_alloc_chunk (chunk_size);
+ g_mutex_unlock (allocator->slab_mutex);
+ }
+ else /* delegate to system malloc */
+ mem = g_malloc (mem_size);
+ return mem;
+}
+
+gpointer
+g_slice_alloc0 (guint mem_size)
+{
+ gpointer mem = g_slice_alloc (mem_size);
+ if (mem)
+ memset (mem, 0, mem_size);
+ return mem;
+}
+
+void
+g_slice_free1 (guint mem_size,
+ gpointer mem_block)
+{
+ guint chunk_size = P2ALIGN (mem_size);
+ guint acat = allocator_categorize (chunk_size);
+ if (G_UNLIKELY (!mem_block))
+ /* pass */;
+ else if (G_LIKELY (acat == 1)) /* allocate through magazine layer */
+ {
+ ThreadMemory *tmem = thread_memory_from_self();
+ guint ix = SLAB_INDEX (allocator, chunk_size);
+ if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
+ {
+ thread_memory_swap_magazines (tmem, ix);
+ if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
+ thread_memory_magazine2_unload (tmem, ix);
+ }
+ thread_memory_magazine2_free (tmem, ix, mem_block);
+ }
+ else if (acat == 2) /* allocate through slab allocator */
+ {
+ g_mutex_lock (allocator->slab_mutex);
+ slab_allocator_free_chunk (chunk_size, mem_block);
+ g_mutex_unlock (allocator->slab_mutex);
+ }
+ else /* delegate to system malloc */
+ g_free (mem_block);
+}
+
+void
+g_slice_free_chain (guint mem_size,
+ gpointer mem_chain,
+ guint next_offset)
+{
+ GSList *slice = mem_chain;
+ g_return_if_fail (next_offset == G_STRUCT_OFFSET (GSList, next));
+ g_return_if_fail (mem_size >= sizeof (GSList));
+ while (slice)
+ {
+ GSList *current = slice;
+ slice = slice->next;
+ g_slice_free1 (mem_size, current);
+ }
+ /* while the thread magazines and the magazine cache are implemented so that
+ * they can easily be extended to allow for free lists containing more free
+ * lists for the first level nodes, which would allow O(1) freeing in this
+ * function, the benefit of such an extension is questionable, because:
+ * - the magazine size counts will become mere lower bounds which confuses
+ * the code adapting to lock contention;
+ * - freeing a single node to the thread magazines is very fast, so this
+ * O(list_length) operation is multiplied by a fairly small factor;
+ * - memory usage histograms on larger applications seem to indicate that
+ * the amount of released multi node lists is negligible in comparison
+ * to single node releases.
+ */
+}
+
+/* --- single page allocator --- */
+static void
+allocator_slab_stack_push (Allocator *allocator,
+ guint ix,
+ SlabInfo *sinfo)
+{
+ /* insert slab at slab ring head */
+ if (!allocator->slab_stack[ix])
+ {
+ sinfo->next = sinfo;
+ sinfo->prev = sinfo;
+ }
+ else
+ {
+ SlabInfo *next = allocator->slab_stack[ix], *prev = next->prev;
+ next->prev = sinfo;
+ prev->next = sinfo;
+ sinfo->next = next;
+ sinfo->prev = prev;
+ }
+ allocator->slab_stack[ix] = sinfo;
+}
+
+static void
+allocator_add_slab (Allocator *allocator,
+ guint ix,
+ guint chunk_size)
+{
+ SlabInfo *sinfo;
+ gsize padding, n_chunks, color = 0;
+ gsize page_size = SLAB_PAGE_SIZE (allocator, chunk_size);
+ /* allocate 1 page for the chunks and the slab */
+ gpointer aligned_memory = allocator_memalign (page_size, page_size - NATIVE_MALLOC_PADDING);
+ guint8 *mem = aligned_memory;
+ if (!mem)
+ g_error ("%s: failed to allocate %lu bytes: %s", "GSlicedMemory", (gulong) (page_size - NATIVE_MALLOC_PADDING), g_strerror (errno));
+ /* mask page adress */
+ gsize addr = ((gsize) mem / page_size) * page_size;
+ /* assert alignment */
+ g_assert (aligned_memory == (gpointer) addr);
+ /* basic slab info setup */
+ sinfo = (SlabInfo*) (mem + page_size - SLAB_INFO_SIZE);
+ sinfo->n_allocated = 0;
+ sinfo->chunks = NULL;
+ /* figure cache colorization */
+ n_chunks = ((guint8*) sinfo - mem) / chunk_size;
+ padding = ((guint8*) sinfo - mem) - n_chunks * chunk_size;
+ if (padding)
+ {
+ color = (allocator->color_accu * P2ALIGNMENT) % padding;
+ allocator->color_accu += 1; /* alternatively: + 0x7fffffff */
+ }
+ /* add chunks to free list */
+ ChunkLink *chunk = (ChunkLink*) (mem + color);
+ guint i;
+ sinfo->chunks = chunk;
+ for (i = 0; i < n_chunks - 1; i++)
+ {
+ chunk->next = (ChunkLink*) ((guint8*) chunk + chunk_size);
+ chunk = chunk->next;
+ }
+ chunk->next = NULL; /* last chunk */
+ /* add slab to slab ring */
+ allocator_slab_stack_push (allocator, ix, sinfo);
+}
+
+static gpointer
+slab_allocator_alloc_chunk (guint chunk_size)
+{
+ guint ix = SLAB_INDEX (allocator, chunk_size);
+ /* ensure non-empty slab */
+ if (!allocator->slab_stack[ix] || !allocator->slab_stack[ix]->chunks)
+ allocator_add_slab (allocator, ix, chunk_size);
+ /* allocate chunk */
+ ChunkLink *chunk = allocator->slab_stack[ix]->chunks;
+ allocator->slab_stack[ix]->chunks = chunk->next;
+ allocator->slab_stack[ix]->n_allocated++;
+ /* rotate empty slabs */
+ if (!allocator->slab_stack[ix]->chunks)
+ allocator->slab_stack[ix] = allocator->slab_stack[ix]->next;
+ return chunk;
+}
+
+static void
+slab_allocator_free_chunk (guint chunk_size,
+ gpointer mem)
+{
+ guint ix = SLAB_INDEX (allocator, chunk_size);
+ gsize page_size = SLAB_PAGE_SIZE (allocator, chunk_size);
+ gsize addr = ((gsize) mem / page_size) * page_size;
+ /* mask page adress */
+ guint8 *page = (guint8*) addr;
+ SlabInfo *sinfo = (SlabInfo*) (page + page_size - SLAB_INFO_SIZE);
+ /* assert valid chunk count */
+ g_assert (sinfo->n_allocated > 0);
+ /* add chunk to free list */
+ gboolean was_empty = sinfo->chunks == NULL;
+ ChunkLink *chunk = (ChunkLink*) mem;
+ chunk->next = sinfo->chunks;
+ sinfo->chunks = chunk;
+ sinfo->n_allocated--;
+ /* keep slab ring partially sorted, empty slabs at end */
+ if (was_empty)
+ {
+ /* unlink slab */
+ SlabInfo *next = sinfo->next, *prev = sinfo->prev;
+ next->prev = prev;
+ prev->next = next;
+ if (allocator->slab_stack[ix] == sinfo)
+ allocator->slab_stack[ix] = next == sinfo ? NULL : next;
+ /* insert slab at head */
+ allocator_slab_stack_push (allocator, ix, sinfo);
+ }
+ /* eagerly free complete unused slabs */
+ if (!sinfo->n_allocated)
+ {
+ /* unlink slab */
+ SlabInfo *next = sinfo->next, *prev = sinfo->prev;
+ next->prev = prev;
+ prev->next = next;
+ if (allocator->slab_stack[ix] == sinfo)
+ allocator->slab_stack[ix] = next == sinfo ? NULL : next;
+ /* free slab */
+ allocator_memfree (page_size, page);
+ }
+}
+
+/* --- memalign implementation --- */
+#include <malloc.h> /* memalign() */
+
+/* from config.h:
+ * define HAVE_POSIX_MEMALIGN 1 // if free(posix_memalign(3)) works, <stdlib.h>
+ * define HAVE_MEMALIGN 1 // if free(memalign(3)) works, <malloc.h>
+ * define HAVE_VALLOC 1 // if free(valloc(3)) works, <stdlib.h> or <malloc.h>
+ * if none is provided, we implement malloc(3)-based alloc-only page alignment
+ */
+
+#if !(HAVE_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_VALLOC)
+static GTrashStack *compat_valloc_trash = NULL;
+#endif
+
+static gpointer
+allocator_memalign (gsize alignment,
+ gsize memsize)
+{
+ gpointer aligned_memory = NULL;
+ gint err = ENOMEM;
+#if HAVE_POSIX_MEMALIGN
+ err = posix_memalign (&aligned_memory, alignment, memsize);
+#elif HAVE_MEMALIGN
+ errno = 0;
+ aligned_memory = memalign (alignment, memsize);
+ err = errno;
+#elif HAVE_VALLOC
+ errno = 0;
+ aligned_memory = valloc (memsize);
+ err = errno;
+#else
+ /* simplistic non-freeing page allocator */
+ g_assert (alignment == sys_page_size);
+ g_assert (memsize <= sys_page_size);
+ if (!compat_valloc_trash)
+ {
+ const guint n_pages = 16;
+ guint8 *mem = malloc (n_pages * sys_page_size);
+ err = errno;
+ if (mem)
+ {
+ gint i = n_pages;
+ guint8 *amem = (guint8*) ALIGN ((gsize) mem, sys_page_size);
+ if (amem != mem)
+ i--; /* mem wasn't page aligned */
+ while (--i >= 0)
+ g_trash_stack_push (&compat_valloc_trash, amem + i * sys_page_size);
+ }
+ }
+ aligned_memory = g_trash_stack_pop (&compat_valloc_trash);
+#endif
+ if (!aligned_memory)
+ errno = err;
+ return aligned_memory;
+}
+
+static void
+allocator_memfree (gsize memsize,
+ gpointer mem)
+{
+#if HAVE_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_VALLOC
+ free (mem);
+#else
+ g_assert (memsize <= sys_page_size);
+ g_trash_stack_push (&compat_valloc_trash, mem);
+#endif
+}
+
+#define __G_SLICE_C__
+#include "galiasdef.c"
--- /dev/null
+/* GLIB sliced memory - fast threaded memory chunk allocator
+ * Copyright (C) 2005 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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.
+ */
+#ifndef __G_SLICE_H__
+#define __G_SLICE_H__
+
+#ifndef __G_MEM_H__
+#error Include <glib.h> instead of <gslice.h>
+#endif
+
+#include <glib/gtypes.h>
+
+G_BEGIN_DECLS
+
+/* slices - fast allocation/release of small memory blocks
+ */
+gpointer g_slice_alloc (gsize block_size) G_GNUC_MALLOC;
+gpointer g_slice_alloc0 (gsize block_size) G_GNUC_MALLOC;
+void g_slice_free1 (gsize block_size,
+ gpointer mem_block);
+void g_slice_free_chain (gsize block_size,
+ gpointer mem_chain,
+ gsize next_offset);
+#define g_slice_new(type) ((type*) g_slice_alloc (sizeof (type)))
+#define g_slice_new0(type) ((type*) g_slice_alloc0 (sizeof (type)))
+/* g_slice_free(type,mem) g_slice_free1 (sizeof (type), mem) */
+
+#if __GNUC__ >= 2
+/* for GCC, define a type-safe variant of g_slice_free() */
+#define g_slice_free(type, mem) ({ \
+ static inline void g_slice_free (gsize, type*); \
+ while (0) g_slice_free (sizeof (type), mem); \
+ g_slice_free1 (sizeof (type), mem); \
+})
+#else
+#define g_slice_free(type, mem) g_slice_free1 (sizeof (type) + (gsize) (type*) 0, mem)
+/* we go through the extra (gsize)(type*)0 hoop to ensure a known type argument */
+#endif
+
+/* --- internal debugging API --- */
+typedef enum {
+ G_SLICE_CONFIG_ALWAYS_MALLOC = 1,
+ G_SLICE_CONFIG_BYPASS_MAGAZINES,
+ G_SLICE_CONFIG_ALWAYS_FREE,
+ G_SLICE_CONFIG_WORKING_SET_MSECS,
+ G_SLICE_CONFIG_CHUNK_SIZES,
+ G_SLICE_CONFIG_CONTENTION_COUNTER,
+} GSliceConfig;
+void g_slice_set_config (GSliceConfig ckey, gint64 value);
+gint64 g_slice_get_config (GSliceConfig ckey);
+gint64* g_slice_get_config_state (GSliceConfig ckey, gint64 address, guint *n_values);
+
+G_END_DECLS
+
+#endif /* __G_SLICE_H__ */
{
GThread thread;
gpointer private_data;
- gpointer mem_private;
GRealThread *next;
gpointer retval;
GSystemThread system_thread;
*/
GRealThread* main_thread = (GRealThread*) g_thread_self ();
+ /* mutex and cond creation works without g_threads_got_initialized */
g_once_mutex = g_mutex_new ();
g_once_cond = g_cond_new ();
+ /* we may only create mutex and cond in here */
+ _g_mem_thread_init_noprivate_nomessage ();
+
+ /* setup the basic threading system */
+ g_threads_got_initialized = TRUE;
+ g_thread_specific_private = g_private_new (g_thread_cleanup);
+ g_private_set (g_thread_specific_private, main_thread);
+ G_THREAD_UF (thread_self, (&main_thread->system_thread));
+
+ /* complete memory system initialization, g_private_*() works now */
+ _g_slice_thread_init_nomessage ();
+
+ /* accomplish log system initialization to enable messaging */
+ _g_messages_thread_init_nomessage ();
+
+ /* we may run full-fledged initializers from here */
_g_convert_thread_init ();
_g_rand_thread_init ();
_g_main_thread_init ();
- _g_mem_thread_init ();
- _g_messages_thread_init ();
_g_atomic_thread_init ();
_g_utils_thread_init ();
#ifdef G_OS_WIN32
_g_win32_thread_init ();
#endif
-
- g_threads_got_initialized = TRUE;
-
- g_thread_specific_private = g_private_new (g_thread_cleanup);
- g_private_set (g_thread_specific_private, main_thread);
- G_THREAD_UF (thread_self, (&main_thread->system_thread));
-
- _g_mem_thread_private_init ();
- _g_messages_thread_private_init ();
-
}
#endif /* G_THREADS_ENABLED */
g_static_mutex_free (&lock->mutex);
}
-/*
- * Memory allocation can't use the regular GPrivate
- * API, since that relies on GArray, which uses
- * chunked memory.
- */
-gpointer
-_g_thread_mem_private_get (GThread *thread)
-{
- GRealThread *real_thread = (GRealThread*) thread;
-
- return real_thread->mem_private;
-}
-
-void
-_g_thread_mem_private_set (GThread *thread,
- gpointer data)
-{
- GRealThread *real_thread = (GRealThread*) thread;
-
- real_thread->mem_private = data;
-}
-
/**
* g_thread_foreach
* @thread_func: function to call for all GThread structures
# define G_TRYLOCK(name) (TRUE)
#endif /* !G_THREADS_ENABLED */
-/* --- internal API --- */
-gpointer _g_thread_mem_private_get (GThread *thread);
-void _g_thread_mem_private_set (GThread *thread,
- gpointer data);
G_END_DECLS
/* Is called from gthread/gthread-impl.c */
void g_thread_init_glib (void);
-/* Are called from glib/gthread.c. May not contain g_private_new calls */
-void _g_mem_thread_init (void) G_GNUC_INTERNAL;
-void _g_messages_thread_init (void) G_GNUC_INTERNAL;
+/* base initializers, may only use g_mutex_new(), g_cond_new() */
+void _g_mem_thread_init_noprivate_nomessage (void) G_GNUC_INTERNAL;
+/* initializers that may also use g_private_new() */
+void _g_slice_thread_init_nomessage (void) G_GNUC_INTERNAL;
+void _g_messages_thread_init_nomessage (void) G_GNUC_INTERNAL;
+
+/* full fledged initializersa */
void _g_convert_thread_init (void) G_GNUC_INTERNAL;
void _g_rand_thread_init (void) G_GNUC_INTERNAL;
void _g_main_thread_init (void) G_GNUC_INTERNAL;
void _g_win32_thread_init (void) G_GNUC_INTERNAL;
#endif
-/* Are called from glib/gthread.c. Must only contain g_private_new calls */
-void _g_mem_thread_private_init (void) G_GNUC_INTERNAL;
+/* initialization functions called from glib/gthread.c.
+ * may contain g_mutex_new().
+ * may contain g_private_new() calls.
+ */
void _g_messages_thread_private_init (void) G_GNUC_INTERNAL;
G_END_DECLS
relation-test \
shell-test \
slist-test \
+ slice-test \
spawn-test \
$(spawn_test_win32_gui) \
strfunc-test \
relation_test_LDADD = $(progs_ldadd)
shell_test_LDADD = $(progs_ldadd)
slist_test_LDADD = $(progs_ldadd)
+slice_test_LDADD = $(thread_ldadd)
spawn_test_LDADD = $(progs_ldadd)
strfunc_test_LDADD = $(progs_ldadd)
string_test_LDADD = $(progs_ldadd)
--- /dev/null
+/* GLIB sliced memory - fast threaded memory chunk allocator
+ * Copyright (C) 2005 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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.
+ */
+#include <glib.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h> // gettimeofday
+
+#define quick_rand32() (rand_accu = 1664525 * rand_accu + 1013904223, rand_accu)
+static guint prime_size = 1021; // 769; // 509
+
+static gpointer
+test_sliced_mem_thread (gpointer data)
+{
+ guint32 rand_accu = 2147483563;
+ /* initialize random numbers */
+ if (data)
+ rand_accu = *(guint32*) data;
+ else
+ {
+ struct timeval rand_tv;
+ gettimeofday (&rand_tv, NULL);
+ rand_accu = rand_tv.tv_usec + (rand_tv.tv_sec << 16);
+ }
+
+ guint i, m = 10000; /* number of blocks */
+ guint j, n = 10000; /* number of alloc+free repetitions */
+ guint8 **ps = g_new (guint8*, m);
+ guint *ss = g_new (guint, m);
+ /* create m random sizes */
+ for (i = 0; i < m; i++)
+ ss[i] = quick_rand32() % prime_size;
+ /* allocate m blocks */
+ for (i = 0; i < m; i++)
+ ps[i] = g_slice_alloc (ss[i]);
+ for (j = 0; j < n; j++)
+ {
+ /* free m/2 blocks */
+ for (i = 0; i < m; i += 2)
+ g_slice_free1 (ss[i], ps[i]);
+ /* allocate m/2 blocks with new sizes */
+ for (i = 0; i < m; i += 2)
+ {
+ ss[i] = quick_rand32() % prime_size;
+ ps[i] = g_slice_alloc (ss[i]);
+ }
+ }
+ /* free m blocks */
+ for (i = 0; i < m; i++)
+ g_slice_free1 (ss[i], ps[i]);
+ /* alloc and free many equally sized chunks in a row */
+ for (i = 0; i < n; i++)
+ {
+ guint sz = quick_rand32() % prime_size;
+ guint k = m / 100;
+ for (j = 0; j < k; j++)
+ ps[j] = g_slice_alloc (sz);
+ for (j = 0; j < k; j++)
+ g_slice_free1 (sz, ps[j]);
+ }
+
+ return NULL;
+}
+
+static void
+usage (void)
+{
+ g_print ("Usage: gslicedmemory [n_threads] [G|S|M][f][c] [maxblocksize] [seed]\n");
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ guint seed32, *seedp = NULL;
+ gboolean ccounters = FALSE;
+ guint n_threads = 1;
+ const gchar *mode = "slab allocator + magazine cache", *emode = " ";
+ if (argc > 1)
+ n_threads = g_ascii_strtoull (argv[1], NULL, 10);
+ if (argc > 2)
+ {
+ guint i, l = strlen (argv[2]);
+ for (i = 0; i < l; i++)
+ switch (argv[2][i])
+ {
+ case 'G': /* GLib mode */
+ g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE);
+ g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES, FALSE);
+ mode = "slab allocator + magazine cache";
+ break;
+ case 'S': /* slab mode */
+ g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE);
+ g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES, TRUE);
+ mode = "slab allocator";
+ break;
+ case 'M': /* malloc mode */
+ g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE);
+ mode = "system malloc";
+ break;
+ case 'f': /* eager freeing */
+ g_slice_set_config (G_SLICE_CONFIG_ALWAYS_FREE, TRUE);
+ emode = " with eager freeing";
+ break;
+ case 'c': /* print contention counters */
+ ccounters = TRUE;
+ break;
+ default:
+ usage();
+ return 1;
+ }
+ }
+ if (argc > 3)
+ prime_size = g_ascii_strtoull (argv[3], NULL, 10);
+ if (argc > 4)
+ {
+ seed32 = g_ascii_strtoull (argv[4], NULL, 10);
+ seedp = &seed32;
+ }
+
+ g_thread_init (NULL);
+
+ if (argc <= 1)
+ usage();
+
+ gchar strseed[64] = "<random>";
+ if (seedp)
+ g_snprintf (strseed, 64, "%u", *seedp);
+ g_print ("Starting %d threads allocating random blocks <= %u bytes with seed=%s using %s%s\n", n_threads, prime_size, strseed, mode, emode);
+
+ GThread *threads[n_threads];
+ guint i;
+ for (i = 0; i < n_threads; i++)
+ threads[i] = g_thread_create_full (test_sliced_mem_thread, seedp, 0, TRUE, FALSE, 0, NULL);
+ for (i = 0; i < n_threads; i++)
+ g_thread_join (threads[i]);
+
+ if (ccounters)
+ {
+ guint n, n_chunks = g_slice_get_config (G_SLICE_CONFIG_CHUNK_SIZES);
+ g_print (" ChunkSize | MagazineSize | Contention\n");
+ for (i = 0; i < n_chunks; i++)
+ {
+ gint64 *vals = g_slice_get_config_state (G_SLICE_CONFIG_CONTENTION_COUNTER, i, &n);
+ g_print (" %9llu | %9llu | %9llu\n", vals[0], vals[2], vals[1]);
+ g_free (vals);
+ }
+ }
+ else
+ g_print ("Done.\n");
+ return 0;
+}