new slice allocator implementation.
authorTim Janik <timj@imendio.com>
Thu, 1 Dec 2005 16:34:33 +0000 (16:34 +0000)
committerTim Janik <timj@src.gnome.org>
Thu, 1 Dec 2005 16:34:33 +0000 (16:34 +0000)
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].

16 files changed:
ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-12
configure.in
glib/Makefile.am
glib/glib.symbols
glib/gmem.c
glib/gmem.h
glib/gmessages.c
glib/gslice.c [new file with mode: 0644]
glib/gslice.h [new file with mode: 0644]
glib/gthread.c
glib/gthread.h
glib/gthreadinit.h
tests/Makefile.am
tests/slice-test.c [new file with mode: 0644]

index fcbeee2..d1cfe5c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,33 @@
+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
index fcbeee2..d1cfe5c 100644 (file)
@@ -1,3 +1,33 @@
+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
index fcbeee2..d1cfe5c 100644 (file)
@@ -1,3 +1,33 @@
+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
index eb4497a..fa2f964 100644 (file)
@@ -523,6 +523,9 @@ AC_HEADER_STDC
 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)
 
index 1df9003..711bf05 100644 (file)
@@ -104,6 +104,7 @@ libglib_2_0_la_SOURCES =    \
        grand.c                 \
        gscanner.c              \
        gshell.c                \
+       gslice.c                \
        gslist.c                \
        gstdio.c                \
        gstrfuncs.c             \
@@ -179,6 +180,7 @@ glibsubinclude_HEADERS =   \
        grel.h          \
        gscanner.h      \
        gshell.h        \
+       gslice.h        \
        gslist.h        \
        gspawn.h        \
        gstdio.h        \
index 1cab86f..cde4883 100644 (file)
@@ -584,10 +584,6 @@ g_realloc
 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
@@ -605,6 +601,18 @@ g_blow_chunks
 #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
index 68de905..849ade5 100644 (file)
@@ -39,8 +39,6 @@
 #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,
@@ -295,11 +280,9 @@ typedef enum {
 } 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;
@@ -313,47 +296,37 @@ profiler_log (ProfilerJob job,
              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
@@ -399,27 +372,23 @@ g_mem_profile (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);
@@ -432,11 +401,6 @@ g_mem_profile (void)
           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
@@ -602,47 +566,6 @@ 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;
@@ -651,63 +574,10 @@ typedef struct _GMemChunk  GMemChunk;
 #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,
@@ -715,509 +585,11 @@ g_mem_chunk_new (const gchar  *name,
                 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
@@ -1225,27 +597,23 @@ g_mem_chunk_destroy (GMemChunk *mem_chunk)
 {
   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
@@ -1254,7 +622,7 @@ g_mem_chunk_free (GMemChunk *mem_chunk,
 {
   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)  {}
@@ -1263,28 +631,23 @@ 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 = {
+  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
@@ -1293,22 +656,13 @@ 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)
+_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
 }
 
index 6e1773e..ae8756e 100644 (file)
@@ -27,6 +27,7 @@
 #ifndef __G_MEM_H__
 #define __G_MEM_H__
 
+#include <glib/gslice.h>
 #include <glib/gtypes.h>
 
 G_BEGIN_DECLS
@@ -96,19 +97,6 @@ gboolean g_mem_is_system_malloc (void);
 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
index 38298bf..726db4e 100644 (file)
@@ -1041,20 +1041,14 @@ g_printf_string_upper_bound (const gchar *format,
 }
 
 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;
 
diff --git a/glib/gslice.c b/glib/gslice.c
new file mode 100644 (file)
index 0000000..0cc903a
--- /dev/null
@@ -0,0 +1,983 @@
+/* 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 (&current);
+              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"
diff --git a/glib/gslice.h b/glib/gslice.h
new file mode 100644 (file)
index 0000000..dc0cc2e
--- /dev/null
@@ -0,0 +1,70 @@
+/* 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__ */
index 1beca52..0dbfbc8 100644 (file)
@@ -76,7 +76,6 @@ struct  _GRealThread
 {
   GThread thread;
   gpointer private_data;
-  gpointer mem_private;
   GRealThread *next;
   gpointer retval;
   GSystemThread system_thread;
@@ -145,29 +144,34 @@ g_thread_init_glib (void)
    */
   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 */
 
@@ -868,28 +872,6 @@ g_static_rw_lock_free (GStaticRWLock* lock)
   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
index 743981f..1234ec7 100644 (file)
@@ -370,10 +370,6 @@ extern void glib_dummy_decl (void);
 #  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
 
index 5082cf8..6095de7 100644 (file)
@@ -26,9 +26,13 @@ G_BEGIN_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;
@@ -38,8 +42,10 @@ void _g_utils_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
index 9424f51..2ea5634 100644 (file)
@@ -91,6 +91,7 @@ test_programs =                                       \
        relation-test                           \
        shell-test                              \
        slist-test                              \
+       slice-test                              \
        spawn-test                              \
        $(spawn_test_win32_gui)                 \
        strfunc-test                            \
@@ -150,6 +151,7 @@ rand_test_LDADD = $(progs_ldadd)
 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)
diff --git a/tests/slice-test.c b/tests/slice-test.c
new file mode 100644 (file)
index 0000000..fd70143
--- /dev/null
@@ -0,0 +1,168 @@
+/* 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;
+}