9da316a8567077557dedacb3dfadd15bf9fa2765
[platform/upstream/glib.git] / glib / gslice.c
1 /* GLIB sliced memory - fast concurrent memory chunk allocator
2  * Copyright (C) 2005 Tim Janik
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16  */
17 /* MT safe */
18
19 #include "config.h"
20 #include "glibconfig.h"
21
22 #if     defined HAVE_POSIX_MEMALIGN && defined POSIX_MEMALIGN_WITH_COMPLIANT_ALLOCS
23 #  define HAVE_COMPLIANT_POSIX_MEMALIGN 1
24 #endif
25
26 #if defined(HAVE_COMPLIANT_POSIX_MEMALIGN) && !defined(_XOPEN_SOURCE)
27 #define _XOPEN_SOURCE 600       /* posix_memalign() */
28 #endif
29 #include <stdlib.h>             /* posix_memalign() */
30 #include <string.h>
31 #include <errno.h>
32
33 #ifdef G_OS_UNIX
34 #include <unistd.h>             /* sysconf() */
35 #endif
36 #ifdef G_OS_WIN32
37 #include <windows.h>
38 #include <process.h>
39 #endif
40
41 #include <stdio.h>              /* fputs/fprintf */
42
43 #include "gslice.h"
44
45 #include "gmain.h"
46 #include "gmem.h"               /* gslice.h */
47 #include "gstrfuncs.h"
48 #include "gutils.h"
49 #include "gtrashstack.h"
50 #include "gtestutils.h"
51 #include "gthread.h"
52 #include "glib_trace.h"
53
54 #include "valgrind.h"
55
56 /**
57  * SECTION:memory_slices
58  * @title: Memory Slices
59  * @short_description: efficient way to allocate groups of equal-sized
60  *     chunks of memory
61  *
62  * Memory slices provide a space-efficient and multi-processing scalable
63  * way to allocate equal-sized pieces of memory, just like the original
64  * #GMemChunks (from GLib 2.8), while avoiding their excessive
65  * memory-waste, scalability and performance problems.
66  *
67  * To achieve these goals, the slice allocator uses a sophisticated,
68  * layered design that has been inspired by Bonwick's slab allocator
69  * (<ulink url="http://citeseer.ist.psu.edu/bonwick94slab.html">[Bonwick94]</ulink> Jeff Bonwick, The slab allocator: An object-caching kernel
70  * memory allocator. USENIX 1994, and
71  * <ulink url="http://citeseer.ist.psu.edu/bonwick01magazines.html">[Bonwick01]</ulink> Bonwick and Jonathan Adams, Magazines and vmem: Extending the
72  * slab allocator to many cpu's and arbitrary resources. USENIX 2001)
73  *
74  * It uses posix_memalign() to optimize allocations of many equally-sized
75  * chunks, and has per-thread free lists (the so-called magazine layer)
76  * to quickly satisfy allocation requests of already known structure sizes.
77  * This is accompanied by extra caching logic to keep freed memory around
78  * for some time before returning it to the system. Memory that is unused
79  * due to alignment constraints is used for cache colorization (random
80  * distribution of chunk addresses) to improve CPU cache utilization. The
81  * caching layer of the slice allocator adapts itself to high lock contention
82  * to improve scalability.
83  *
84  * The slice allocator can allocate blocks as small as two pointers, and
85  * unlike malloc(), it does not reserve extra space per block. For large block
86  * sizes, g_slice_new() and g_slice_alloc() will automatically delegate to the
87  * system malloc() implementation. For newly written code it is recommended
88  * to use the new <literal>g_slice</literal> API instead of g_malloc() and
89  * friends, as long as objects are not resized during their lifetime and the
90  * object size used at allocation time is still available when freeing.
91  *
92  * <example>
93  * <title>Using the slice allocator</title>
94  * <programlisting>
95  * gchar *mem[10000];
96  * gint i;
97  *
98  * /&ast; Allocate 10000 blocks. &ast;/
99  * for (i = 0; i &lt; 10000; i++)
100  *   {
101  *     mem[i] = g_slice_alloc (50);
102  *
103  *     /&ast; Fill in the memory with some junk. &ast;/
104  *     for (j = 0; j &lt; 50; j++)
105  *       mem[i][j] = i * j;
106  *   }
107  *
108  * /&ast; Now free all of the blocks. &ast;/
109  * for (i = 0; i &lt; 10000; i++)
110  *   {
111  *     g_slice_free1 (50, mem[i]);
112  *   }
113  * </programlisting></example>
114  *
115  * <example>
116  * <title>Using the slice allocator with data structures</title>
117  * <programlisting>
118  * GRealArray *array;
119  *
120  * /&ast; Allocate one block, using the g_slice_new() macro. &ast;/
121  * array = g_slice_new (GRealArray);
122
123  * /&ast; We can now use array just like a normal pointer to a structure. &ast;/
124  * array->data            = NULL;
125  * array->len             = 0;
126  * array->alloc           = 0;
127  * array->zero_terminated = (zero_terminated ? 1 : 0);
128  * array->clear           = (clear ? 1 : 0);
129  * array->elt_size        = elt_size;
130  *
131  * /&ast; We can free the block, so it can be reused. &ast;/
132  * g_slice_free (GRealArray, array);
133  * </programlisting></example>
134  */
135
136 /* the GSlice allocator is split up into 4 layers, roughly modelled after the slab
137  * allocator and magazine extensions as outlined in:
138  * + [Bonwick94] Jeff Bonwick, The slab allocator: An object-caching kernel
139  *   memory allocator. USENIX 1994, http://citeseer.ist.psu.edu/bonwick94slab.html
140  * + [Bonwick01] Bonwick and Jonathan Adams, Magazines and vmem: Extending the
141  *   slab allocator to many cpu's and arbitrary resources.
142  *   USENIX 2001, http://citeseer.ist.psu.edu/bonwick01magazines.html
143  * the layers are:
144  * - the thread magazines. for each (aligned) chunk size, a magazine (a list)
145  *   of recently freed and soon to be allocated chunks is maintained per thread.
146  *   this way, most alloc/free requests can be quickly satisfied from per-thread
147  *   free lists which only require one g_private_get() call to retrive the
148  *   thread handle.
149  * - the magazine cache. allocating and freeing chunks to/from threads only
150  *   occours at magazine sizes from a global depot of magazines. the depot
151  *   maintaines a 15 second working set of allocated magazines, so full
152  *   magazines are not allocated and released too often.
153  *   the chunk size dependent magazine sizes automatically adapt (within limits,
154  *   see [3]) to lock contention to properly scale performance across a variety
155  *   of SMP systems.
156  * - the slab allocator. this allocator allocates slabs (blocks of memory) close
157  *   to the system page size or multiples thereof which have to be page aligned.
158  *   the blocks are divided into smaller chunks which are used to satisfy
159  *   allocations from the upper layers. the space provided by the reminder of
160  *   the chunk size division is used for cache colorization (random distribution
161  *   of chunk addresses) to improve processor cache utilization. multiple slabs
162  *   with the same chunk size are kept in a partially sorted ring to allow O(1)
163  *   freeing and allocation of chunks (as long as the allocation of an entirely
164  *   new slab can be avoided).
165  * - the page allocator. on most modern systems, posix_memalign(3) or
166  *   memalign(3) should be available, so this is used to allocate blocks with
167  *   system page size based alignments and sizes or multiples thereof.
168  *   if no memalign variant is provided, valloc() is used instead and
169  *   block sizes are limited to the system page size (no multiples thereof).
170  *   as a fallback, on system without even valloc(), a malloc(3)-based page
171  *   allocator with alloc-only behaviour is used.
172  *
173  * NOTES:
174  * [1] some systems memalign(3) implementations may rely on boundary tagging for
175  *     the handed out memory chunks. to avoid excessive page-wise fragmentation,
176  *     we reserve 2 * sizeof (void*) per block size for the systems memalign(3),
177  *     specified in NATIVE_MALLOC_PADDING.
178  * [2] using the slab allocator alone already provides for a fast and efficient
179  *     allocator, it doesn't properly scale beyond single-threaded uses though.
180  *     also, the slab allocator implements eager free(3)-ing, i.e. does not
181  *     provide any form of caching or working set maintenance. so if used alone,
182  *     it's vulnerable to trashing for sequences of balanced (alloc, free) pairs
183  *     at certain thresholds.
184  * [3] magazine sizes are bound by an implementation specific minimum size and
185  *     a chunk size specific maximum to limit magazine storage sizes to roughly
186  *     16KB.
187  * [4] allocating ca. 8 chunks per block/page keeps a good balance between
188  *     external and internal fragmentation (<= 12.5%). [Bonwick94]
189  */
190
191 /* --- macros and constants --- */
192 #define LARGEALIGNMENT          (256)
193 #define P2ALIGNMENT             (2 * sizeof (gsize))                            /* fits 2 pointers (assumed to be 2 * GLIB_SIZEOF_SIZE_T below) */
194 #define ALIGN(size, base)       ((base) * (gsize) (((size) + (base) - 1) / (base)))
195 #define NATIVE_MALLOC_PADDING   P2ALIGNMENT                                     /* per-page padding left for native malloc(3) see [1] */
196 #define SLAB_INFO_SIZE          P2ALIGN (sizeof (SlabInfo) + NATIVE_MALLOC_PADDING)
197 #define MAX_MAGAZINE_SIZE       (256)                                           /* see [3] and allocator_get_magazine_threshold() for this */
198 #define MIN_MAGAZINE_SIZE       (4)
199 #define MAX_STAMP_COUNTER       (7)                                             /* distributes the load of gettimeofday() */
200 #define MAX_SLAB_CHUNK_SIZE(al) (((al)->max_page_size - SLAB_INFO_SIZE) / 8)    /* we want at last 8 chunks per page, see [4] */
201 #define MAX_SLAB_INDEX(al)      (SLAB_INDEX (al, MAX_SLAB_CHUNK_SIZE (al)) + 1)
202 #define SLAB_INDEX(al, asize)   ((asize) / P2ALIGNMENT - 1)                     /* asize must be P2ALIGNMENT aligned */
203 #define SLAB_CHUNK_SIZE(al, ix) (((ix) + 1) * P2ALIGNMENT)
204 #define SLAB_BPAGE_SIZE(al,csz) (8 * (csz) + SLAB_INFO_SIZE)
205
206 /* optimized version of ALIGN (size, P2ALIGNMENT) */
207 #if     GLIB_SIZEOF_SIZE_T * 2 == 8  /* P2ALIGNMENT */
208 #define P2ALIGN(size)   (((size) + 0x7) & ~(gsize) 0x7)
209 #elif   GLIB_SIZEOF_SIZE_T * 2 == 16 /* P2ALIGNMENT */
210 #define P2ALIGN(size)   (((size) + 0xf) & ~(gsize) 0xf)
211 #else
212 #define P2ALIGN(size)   ALIGN (size, P2ALIGNMENT)
213 #endif
214
215 /* special helpers to avoid gmessage.c dependency */
216 static void mem_error (const char *format, ...) G_GNUC_PRINTF (1,2);
217 #define mem_assert(cond)    do { if (G_LIKELY (cond)) ; else mem_error ("assertion failed: %s", #cond); } while (0)
218
219 /* --- structures --- */
220 typedef struct _ChunkLink      ChunkLink;
221 typedef struct _SlabInfo       SlabInfo;
222 typedef struct _CachedMagazine CachedMagazine;
223 struct _ChunkLink {
224   ChunkLink *next;
225   ChunkLink *data;
226 };
227 struct _SlabInfo {
228   ChunkLink *chunks;
229   guint n_allocated;
230   SlabInfo *next, *prev;
231 };
232 typedef struct {
233   ChunkLink *chunks;
234   gsize      count;                     /* approximative chunks list length */
235 } Magazine;
236 typedef struct {
237   Magazine   *magazine1;                /* array of MAX_SLAB_INDEX (allocator) */
238   Magazine   *magazine2;                /* array of MAX_SLAB_INDEX (allocator) */
239 } ThreadMemory;
240 typedef struct {
241   gboolean always_malloc;
242   gboolean bypass_magazines;
243   gboolean debug_blocks;
244   gsize    working_set_msecs;
245   guint    color_increment;
246 } SliceConfig;
247 typedef struct {
248   /* const after initialization */
249   gsize         min_page_size, max_page_size;
250   SliceConfig   config;
251   gsize         max_slab_chunk_size_for_magazine_cache;
252   /* magazine cache */
253   GMutex        magazine_mutex;
254   ChunkLink   **magazines;                /* array of MAX_SLAB_INDEX (allocator) */
255   guint        *contention_counters;      /* array of MAX_SLAB_INDEX (allocator) */
256   gint          mutex_counter;
257   guint         stamp_counter;
258   guint         last_stamp;
259   /* slab allocator */
260   GMutex        slab_mutex;
261   SlabInfo    **slab_stack;                /* array of MAX_SLAB_INDEX (allocator) */
262   guint        color_accu;
263 } Allocator;
264
265 /* --- g-slice prototypes --- */
266 static gpointer     slab_allocator_alloc_chunk       (gsize      chunk_size);
267 static void         slab_allocator_free_chunk        (gsize      chunk_size,
268                                                       gpointer   mem);
269 static void         private_thread_memory_cleanup    (gpointer   data);
270 static gpointer     allocator_memalign               (gsize      alignment,
271                                                       gsize      memsize);
272 static void         allocator_memfree                (gsize      memsize,
273                                                       gpointer   mem);
274 static inline void  magazine_cache_update_stamp      (void);
275 static inline gsize allocator_get_magazine_threshold (Allocator *allocator,
276                                                       guint      ix);
277
278 /* --- g-slice memory checker --- */
279 static void     smc_notify_alloc  (void   *pointer,
280                                    size_t  size);
281 static int      smc_notify_free   (void   *pointer,
282                                    size_t  size);
283
284 /* --- variables --- */
285 static GPrivate    private_thread_memory = G_PRIVATE_INIT (private_thread_memory_cleanup);
286 static gsize       sys_page_size = 0;
287 static Allocator   allocator[1] = { { 0, }, };
288 static SliceConfig slice_config = {
289   FALSE,        /* always_malloc */
290   FALSE,        /* bypass_magazines */
291   FALSE,        /* debug_blocks */
292   15 * 1000,    /* working_set_msecs */
293   1,            /* color increment, alt: 0x7fffffff */
294 };
295 static GMutex      smc_tree_mutex; /* mutex for G_SLICE=debug-blocks */
296
297 /* --- auxiliary funcitons --- */
298 void
299 g_slice_set_config (GSliceConfig ckey,
300                     gint64       value)
301 {
302   g_return_if_fail (sys_page_size == 0);
303   switch (ckey)
304     {
305     case G_SLICE_CONFIG_ALWAYS_MALLOC:
306       slice_config.always_malloc = value != 0;
307       break;
308     case G_SLICE_CONFIG_BYPASS_MAGAZINES:
309       slice_config.bypass_magazines = value != 0;
310       break;
311     case G_SLICE_CONFIG_WORKING_SET_MSECS:
312       slice_config.working_set_msecs = value;
313       break;
314     case G_SLICE_CONFIG_COLOR_INCREMENT:
315       slice_config.color_increment = value;
316     default: ;
317     }
318 }
319
320 gint64
321 g_slice_get_config (GSliceConfig ckey)
322 {
323   switch (ckey)
324     {
325     case G_SLICE_CONFIG_ALWAYS_MALLOC:
326       return slice_config.always_malloc;
327     case G_SLICE_CONFIG_BYPASS_MAGAZINES:
328       return slice_config.bypass_magazines;
329     case G_SLICE_CONFIG_WORKING_SET_MSECS:
330       return slice_config.working_set_msecs;
331     case G_SLICE_CONFIG_CHUNK_SIZES:
332       return MAX_SLAB_INDEX (allocator);
333     case G_SLICE_CONFIG_COLOR_INCREMENT:
334       return slice_config.color_increment;
335     default:
336       return 0;
337     }
338 }
339
340 gint64*
341 g_slice_get_config_state (GSliceConfig ckey,
342                           gint64       address,
343                           guint       *n_values)
344 {
345   guint i = 0;
346   g_return_val_if_fail (n_values != NULL, NULL);
347   *n_values = 0;
348   switch (ckey)
349     {
350       gint64 array[64];
351     case G_SLICE_CONFIG_CONTENTION_COUNTER:
352       array[i++] = SLAB_CHUNK_SIZE (allocator, address);
353       array[i++] = allocator->contention_counters[address];
354       array[i++] = allocator_get_magazine_threshold (allocator, address);
355       *n_values = i;
356       return g_memdup (array, sizeof (array[0]) * *n_values);
357     default:
358       return NULL;
359     }
360 }
361
362 static void
363 slice_config_init (SliceConfig *config)
364 {
365   const gchar *val;
366
367   *config = slice_config;
368
369   val = getenv ("G_SLICE");
370   if (val != NULL)
371     {
372       gint flags;
373       const GDebugKey keys[] = {
374         { "always-malloc", 1 << 0 },
375         { "debug-blocks",  1 << 1 },
376       };
377
378       flags = g_parse_debug_string (val, keys, G_N_ELEMENTS (keys));
379       if (flags & (1 << 0))
380         config->always_malloc = TRUE;
381       if (flags & (1 << 1))
382         config->debug_blocks = TRUE;
383     }
384   else
385     {
386       /* G_SLICE was not specified, so check if valgrind is running and
387        * disable ourselves if it is.
388        *
389        * This way it's possible to force gslice to be enabled under
390        * valgrind just by setting G_SLICE to the empty string.
391        */
392       if (RUNNING_ON_VALGRIND)
393         config->always_malloc = TRUE;
394     }
395 }
396
397 static void
398 g_slice_init_nomessage (void)
399 {
400   /* we may not use g_error() or friends here */
401   mem_assert (sys_page_size == 0);
402   mem_assert (MIN_MAGAZINE_SIZE >= 4);
403
404 #ifdef G_OS_WIN32
405   {
406     SYSTEM_INFO system_info;
407     GetSystemInfo (&system_info);
408     sys_page_size = system_info.dwPageSize;
409   }
410 #else
411   sys_page_size = sysconf (_SC_PAGESIZE); /* = sysconf (_SC_PAGE_SIZE); = getpagesize(); */
412 #endif
413   mem_assert (sys_page_size >= 2 * LARGEALIGNMENT);
414   mem_assert ((sys_page_size & (sys_page_size - 1)) == 0);
415   slice_config_init (&allocator->config);
416   allocator->min_page_size = sys_page_size;
417 #if HAVE_COMPLIANT_POSIX_MEMALIGN || HAVE_MEMALIGN
418   /* allow allocation of pages up to 8KB (with 8KB alignment).
419    * this is useful because many medium to large sized structures
420    * fit less than 8 times (see [4]) into 4KB pages.
421    * we allow very small page sizes here, to reduce wastage in
422    * threads if only small allocations are required (this does
423    * bear the risk of increasing allocation times and fragmentation
424    * though).
425    */
426   allocator->min_page_size = MAX (allocator->min_page_size, 4096);
427   allocator->max_page_size = MAX (allocator->min_page_size, 8192);
428   allocator->min_page_size = MIN (allocator->min_page_size, 128);
429 #else
430   /* we can only align to system page size */
431   allocator->max_page_size = sys_page_size;
432 #endif
433   if (allocator->config.always_malloc)
434     {
435       allocator->contention_counters = NULL;
436       allocator->magazines = NULL;
437       allocator->slab_stack = NULL;
438     }
439   else
440     {
441       allocator->contention_counters = g_new0 (guint, MAX_SLAB_INDEX (allocator));
442       allocator->magazines = g_new0 (ChunkLink*, MAX_SLAB_INDEX (allocator));
443       allocator->slab_stack = g_new0 (SlabInfo*, MAX_SLAB_INDEX (allocator));
444     }
445
446   allocator->mutex_counter = 0;
447   allocator->stamp_counter = MAX_STAMP_COUNTER; /* force initial update */
448   allocator->last_stamp = 0;
449   allocator->color_accu = 0;
450   magazine_cache_update_stamp();
451   /* values cached for performance reasons */
452   allocator->max_slab_chunk_size_for_magazine_cache = MAX_SLAB_CHUNK_SIZE (allocator);
453   if (allocator->config.always_malloc || allocator->config.bypass_magazines)
454     allocator->max_slab_chunk_size_for_magazine_cache = 0;      /* non-optimized cases */
455 }
456
457 static inline guint
458 allocator_categorize (gsize aligned_chunk_size)
459 {
460   /* speed up the likely path */
461   if (G_LIKELY (aligned_chunk_size && aligned_chunk_size <= allocator->max_slab_chunk_size_for_magazine_cache))
462     return 1;           /* use magazine cache */
463
464   if (!allocator->config.always_malloc &&
465       aligned_chunk_size &&
466       aligned_chunk_size <= MAX_SLAB_CHUNK_SIZE (allocator))
467     {
468       if (allocator->config.bypass_magazines)
469         return 2;       /* use slab allocator, see [2] */
470       return 1;         /* use magazine cache */
471     }
472   return 0;             /* use malloc() */
473 }
474
475 static inline void
476 g_mutex_lock_a (GMutex *mutex,
477                 guint  *contention_counter)
478 {
479   gboolean contention = FALSE;
480   if (!g_mutex_trylock (mutex))
481     {
482       g_mutex_lock (mutex);
483       contention = TRUE;
484     }
485   if (contention)
486     {
487       allocator->mutex_counter++;
488       if (allocator->mutex_counter >= 1)        /* quickly adapt to contention */
489         {
490           allocator->mutex_counter = 0;
491           *contention_counter = MIN (*contention_counter + 1, MAX_MAGAZINE_SIZE);
492         }
493     }
494   else /* !contention */
495     {
496       allocator->mutex_counter--;
497       if (allocator->mutex_counter < -11)       /* moderately recover magazine sizes */
498         {
499           allocator->mutex_counter = 0;
500           *contention_counter = MAX (*contention_counter, 1) - 1;
501         }
502     }
503 }
504
505 static inline ThreadMemory*
506 thread_memory_from_self (void)
507 {
508   ThreadMemory *tmem = g_private_get (&private_thread_memory);
509   if (G_UNLIKELY (!tmem))
510     {
511       static GMutex init_mutex;
512       guint n_magazines;
513
514       g_mutex_lock (&init_mutex);
515       if G_UNLIKELY (sys_page_size == 0)
516         g_slice_init_nomessage ();
517       g_mutex_unlock (&init_mutex);
518
519       n_magazines = MAX_SLAB_INDEX (allocator);
520       tmem = g_malloc0 (sizeof (ThreadMemory) + sizeof (Magazine) * 2 * n_magazines);
521       tmem->magazine1 = (Magazine*) (tmem + 1);
522       tmem->magazine2 = &tmem->magazine1[n_magazines];
523       g_private_set (&private_thread_memory, tmem);
524     }
525   return tmem;
526 }
527
528 static inline ChunkLink*
529 magazine_chain_pop_head (ChunkLink **magazine_chunks)
530 {
531   /* magazine chains are linked via ChunkLink->next.
532    * each ChunkLink->data of the toplevel chain may point to a subchain,
533    * linked via ChunkLink->next. ChunkLink->data of the subchains just
534    * contains uninitialized junk.
535    */
536   ChunkLink *chunk = (*magazine_chunks)->data;
537   if (G_UNLIKELY (chunk))
538     {
539       /* allocating from freed list */
540       (*magazine_chunks)->data = chunk->next;
541     }
542   else
543     {
544       chunk = *magazine_chunks;
545       *magazine_chunks = chunk->next;
546     }
547   return chunk;
548 }
549
550 #if 0 /* useful for debugging */
551 static guint
552 magazine_count (ChunkLink *head)
553 {
554   guint count = 0;
555   if (!head)
556     return 0;
557   while (head)
558     {
559       ChunkLink *child = head->data;
560       count += 1;
561       for (child = head->data; child; child = child->next)
562         count += 1;
563       head = head->next;
564     }
565   return count;
566 }
567 #endif
568
569 static inline gsize
570 allocator_get_magazine_threshold (Allocator *allocator,
571                                   guint      ix)
572 {
573   /* the magazine size calculated here has a lower bound of MIN_MAGAZINE_SIZE,
574    * which is required by the implementation. also, for moderately sized chunks
575    * (say >= 64 bytes), magazine sizes shouldn't be much smaller then the number
576    * of chunks available per page/2 to avoid excessive traffic in the magazine
577    * cache for small to medium sized structures.
578    * the upper bound of the magazine size is effectively provided by
579    * MAX_MAGAZINE_SIZE. for larger chunks, this number is scaled down so that
580    * the content of a single magazine doesn't exceed ca. 16KB.
581    */
582   gsize chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
583   guint threshold = MAX (MIN_MAGAZINE_SIZE, allocator->max_page_size / MAX (5 * chunk_size, 5 * 32));
584   guint contention_counter = allocator->contention_counters[ix];
585   if (G_UNLIKELY (contention_counter))  /* single CPU bias */
586     {
587       /* adapt contention counter thresholds to chunk sizes */
588       contention_counter = contention_counter * 64 / chunk_size;
589       threshold = MAX (threshold, contention_counter);
590     }
591   return threshold;
592 }
593
594 /* --- magazine cache --- */
595 static inline void
596 magazine_cache_update_stamp (void)
597 {
598   if (allocator->stamp_counter >= MAX_STAMP_COUNTER)
599     {
600       GTimeVal tv;
601       g_get_current_time (&tv);
602       allocator->last_stamp = tv.tv_sec * 1000 + tv.tv_usec / 1000; /* milli seconds */
603       allocator->stamp_counter = 0;
604     }
605   else
606     allocator->stamp_counter++;
607 }
608
609 static inline ChunkLink*
610 magazine_chain_prepare_fields (ChunkLink *magazine_chunks)
611 {
612   ChunkLink *chunk1;
613   ChunkLink *chunk2;
614   ChunkLink *chunk3;
615   ChunkLink *chunk4;
616   /* checked upon initialization: mem_assert (MIN_MAGAZINE_SIZE >= 4); */
617   /* ensure a magazine with at least 4 unused data pointers */
618   chunk1 = magazine_chain_pop_head (&magazine_chunks);
619   chunk2 = magazine_chain_pop_head (&magazine_chunks);
620   chunk3 = magazine_chain_pop_head (&magazine_chunks);
621   chunk4 = magazine_chain_pop_head (&magazine_chunks);
622   chunk4->next = magazine_chunks;
623   chunk3->next = chunk4;
624   chunk2->next = chunk3;
625   chunk1->next = chunk2;
626   return chunk1;
627 }
628
629 /* access the first 3 fields of a specially prepared magazine chain */
630 #define magazine_chain_prev(mc)         ((mc)->data)
631 #define magazine_chain_stamp(mc)        ((mc)->next->data)
632 #define magazine_chain_uint_stamp(mc)   GPOINTER_TO_UINT ((mc)->next->data)
633 #define magazine_chain_next(mc)         ((mc)->next->next->data)
634 #define magazine_chain_count(mc)        ((mc)->next->next->next->data)
635
636 static void
637 magazine_cache_trim (Allocator *allocator,
638                      guint      ix,
639                      guint      stamp)
640 {
641   /* g_mutex_lock (allocator->mutex); done by caller */
642   /* trim magazine cache from tail */
643   ChunkLink *current = magazine_chain_prev (allocator->magazines[ix]);
644   ChunkLink *trash = NULL;
645   while (ABS (stamp - magazine_chain_uint_stamp (current)) >= allocator->config.working_set_msecs)
646     {
647       /* unlink */
648       ChunkLink *prev = magazine_chain_prev (current);
649       ChunkLink *next = magazine_chain_next (current);
650       magazine_chain_next (prev) = next;
651       magazine_chain_prev (next) = prev;
652       /* clear special fields, put on trash stack */
653       magazine_chain_next (current) = NULL;
654       magazine_chain_count (current) = NULL;
655       magazine_chain_stamp (current) = NULL;
656       magazine_chain_prev (current) = trash;
657       trash = current;
658       /* fixup list head if required */
659       if (current == allocator->magazines[ix])
660         {
661           allocator->magazines[ix] = NULL;
662           break;
663         }
664       current = prev;
665     }
666   g_mutex_unlock (&allocator->magazine_mutex);
667   /* free trash */
668   if (trash)
669     {
670       const gsize chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
671       g_mutex_lock (&allocator->slab_mutex);
672       while (trash)
673         {
674           current = trash;
675           trash = magazine_chain_prev (current);
676           magazine_chain_prev (current) = NULL; /* clear special field */
677           while (current)
678             {
679               ChunkLink *chunk = magazine_chain_pop_head (&current);
680               slab_allocator_free_chunk (chunk_size, chunk);
681             }
682         }
683       g_mutex_unlock (&allocator->slab_mutex);
684     }
685 }
686
687 static void
688 magazine_cache_push_magazine (guint      ix,
689                               ChunkLink *magazine_chunks,
690                               gsize      count) /* must be >= MIN_MAGAZINE_SIZE */
691 {
692   ChunkLink *current = magazine_chain_prepare_fields (magazine_chunks);
693   ChunkLink *next, *prev;
694   g_mutex_lock (&allocator->magazine_mutex);
695   /* add magazine at head */
696   next = allocator->magazines[ix];
697   if (next)
698     prev = magazine_chain_prev (next);
699   else
700     next = prev = current;
701   magazine_chain_next (prev) = current;
702   magazine_chain_prev (next) = current;
703   magazine_chain_prev (current) = prev;
704   magazine_chain_next (current) = next;
705   magazine_chain_count (current) = (gpointer) count;
706   /* stamp magazine */
707   magazine_cache_update_stamp();
708   magazine_chain_stamp (current) = GUINT_TO_POINTER (allocator->last_stamp);
709   allocator->magazines[ix] = current;
710   /* free old magazines beyond a certain threshold */
711   magazine_cache_trim (allocator, ix, allocator->last_stamp);
712   /* g_mutex_unlock (allocator->mutex); was done by magazine_cache_trim() */
713 }
714
715 static ChunkLink*
716 magazine_cache_pop_magazine (guint  ix,
717                              gsize *countp)
718 {
719   g_mutex_lock_a (&allocator->magazine_mutex, &allocator->contention_counters[ix]);
720   if (!allocator->magazines[ix])
721     {
722       guint magazine_threshold = allocator_get_magazine_threshold (allocator, ix);
723       gsize i, chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
724       ChunkLink *chunk, *head;
725       g_mutex_unlock (&allocator->magazine_mutex);
726       g_mutex_lock (&allocator->slab_mutex);
727       head = slab_allocator_alloc_chunk (chunk_size);
728       head->data = NULL;
729       chunk = head;
730       for (i = 1; i < magazine_threshold; i++)
731         {
732           chunk->next = slab_allocator_alloc_chunk (chunk_size);
733           chunk = chunk->next;
734           chunk->data = NULL;
735         }
736       chunk->next = NULL;
737       g_mutex_unlock (&allocator->slab_mutex);
738       *countp = i;
739       return head;
740     }
741   else
742     {
743       ChunkLink *current = allocator->magazines[ix];
744       ChunkLink *prev = magazine_chain_prev (current);
745       ChunkLink *next = magazine_chain_next (current);
746       /* unlink */
747       magazine_chain_next (prev) = next;
748       magazine_chain_prev (next) = prev;
749       allocator->magazines[ix] = next == current ? NULL : next;
750       g_mutex_unlock (&allocator->magazine_mutex);
751       /* clear special fields and hand out */
752       *countp = (gsize) magazine_chain_count (current);
753       magazine_chain_prev (current) = NULL;
754       magazine_chain_next (current) = NULL;
755       magazine_chain_count (current) = NULL;
756       magazine_chain_stamp (current) = NULL;
757       return current;
758     }
759 }
760
761 /* --- thread magazines --- */
762 static void
763 private_thread_memory_cleanup (gpointer data)
764 {
765   ThreadMemory *tmem = data;
766   const guint n_magazines = MAX_SLAB_INDEX (allocator);
767   guint ix;
768   for (ix = 0; ix < n_magazines; ix++)
769     {
770       Magazine *mags[2];
771       guint j;
772       mags[0] = &tmem->magazine1[ix];
773       mags[1] = &tmem->magazine2[ix];
774       for (j = 0; j < 2; j++)
775         {
776           Magazine *mag = mags[j];
777           if (mag->count >= MIN_MAGAZINE_SIZE)
778             magazine_cache_push_magazine (ix, mag->chunks, mag->count);
779           else
780             {
781               const gsize chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
782               g_mutex_lock (&allocator->slab_mutex);
783               while (mag->chunks)
784                 {
785                   ChunkLink *chunk = magazine_chain_pop_head (&mag->chunks);
786                   slab_allocator_free_chunk (chunk_size, chunk);
787                 }
788               g_mutex_unlock (&allocator->slab_mutex);
789             }
790         }
791     }
792   g_free (tmem);
793 }
794
795 static void
796 thread_memory_magazine1_reload (ThreadMemory *tmem,
797                                 guint         ix)
798 {
799   Magazine *mag = &tmem->magazine1[ix];
800   mem_assert (mag->chunks == NULL); /* ensure that we may reset mag->count */
801   mag->count = 0;
802   mag->chunks = magazine_cache_pop_magazine (ix, &mag->count);
803 }
804
805 static void
806 thread_memory_magazine2_unload (ThreadMemory *tmem,
807                                 guint         ix)
808 {
809   Magazine *mag = &tmem->magazine2[ix];
810   magazine_cache_push_magazine (ix, mag->chunks, mag->count);
811   mag->chunks = NULL;
812   mag->count = 0;
813 }
814
815 static inline void
816 thread_memory_swap_magazines (ThreadMemory *tmem,
817                               guint         ix)
818 {
819   Magazine xmag = tmem->magazine1[ix];
820   tmem->magazine1[ix] = tmem->magazine2[ix];
821   tmem->magazine2[ix] = xmag;
822 }
823
824 static inline gboolean
825 thread_memory_magazine1_is_empty (ThreadMemory *tmem,
826                                   guint         ix)
827 {
828   return tmem->magazine1[ix].chunks == NULL;
829 }
830
831 static inline gboolean
832 thread_memory_magazine2_is_full (ThreadMemory *tmem,
833                                  guint         ix)
834 {
835   return tmem->magazine2[ix].count >= allocator_get_magazine_threshold (allocator, ix);
836 }
837
838 static inline gpointer
839 thread_memory_magazine1_alloc (ThreadMemory *tmem,
840                                guint         ix)
841 {
842   Magazine *mag = &tmem->magazine1[ix];
843   ChunkLink *chunk = magazine_chain_pop_head (&mag->chunks);
844   if (G_LIKELY (mag->count > 0))
845     mag->count--;
846   return chunk;
847 }
848
849 static inline void
850 thread_memory_magazine2_free (ThreadMemory *tmem,
851                               guint         ix,
852                               gpointer      mem)
853 {
854   Magazine *mag = &tmem->magazine2[ix];
855   ChunkLink *chunk = mem;
856   chunk->data = NULL;
857   chunk->next = mag->chunks;
858   mag->chunks = chunk;
859   mag->count++;
860 }
861
862 /* --- API functions --- */
863
864 /**
865  * g_slice_new:
866  * @type: the type to allocate, typically a structure name
867  *
868  * A convenience macro to allocate a block of memory from the
869  * slice allocator.
870  *
871  * It calls g_slice_alloc() with <literal>sizeof (@type)</literal>
872  * and casts the returned pointer to a pointer of the given type,
873  * avoiding a type cast in the source code.
874  * Note that the underlying slice allocation mechanism can
875  * be changed with the <link linkend="G_SLICE">G_SLICE=always-malloc</link>
876  * environment variable.
877  *
878  * Returns: a pointer to the allocated block, cast to a pointer to @type
879  *
880  * Since: 2.10
881  */
882
883 /**
884  * g_slice_new0:
885  * @type: the type to allocate, typically a structure name
886  *
887  * A convenience macro to allocate a block of memory from the
888  * slice allocator and set the memory to 0.
889  *
890  * It calls g_slice_alloc0() with <literal>sizeof (@type)</literal>
891  * and casts the returned pointer to a pointer of the given type,
892  * avoiding a type cast in the source code.
893  * Note that the underlying slice allocation mechanism can
894  * be changed with the <link linkend="G_SLICE">G_SLICE=always-malloc</link>
895  * environment variable.
896  *
897  * Since: 2.10
898  */
899
900 /**
901  * g_slice_dup:
902  * @type: the type to duplicate, typically a structure name
903  * @mem: the memory to copy into the allocated block
904  *
905  * A convenience macro to duplicate a block of memory using
906  * the slice allocator.
907  *
908  * It calls g_slice_copy() with <literal>sizeof (@type)</literal>
909  * and casts the returned pointer to a pointer of the given type,
910  * avoiding a type cast in the source code.
911  * Note that the underlying slice allocation mechanism can
912  * be changed with the <link linkend="G_SLICE">G_SLICE=always-malloc</link>
913  * environment variable.
914  *
915  * Returns: a pointer to the allocated block, cast to a pointer to @type
916  *
917  * Since: 2.14
918  */
919
920 /**
921  * g_slice_free:
922  * @type: the type of the block to free, typically a structure name
923  * @mem: a pointer to the block to free
924  *
925  * A convenience macro to free a block of memory that has
926  * been allocated from the slice allocator.
927  *
928  * It calls g_slice_free1() using <literal>sizeof (type)</literal>
929  * as the block size.
930  * Note that the exact release behaviour can be changed with the
931  * <link linkend="G_DEBUG">G_DEBUG=gc-friendly</link> environment
932  * variable, also see <link linkend="G_SLICE">G_SLICE</link> for
933  * related debugging options.
934  *
935  * Since: 2.10
936  */
937
938 /**
939  * g_slice_free_chain:
940  * @type: the type of the @mem_chain blocks
941  * @mem_chain: a pointer to the first block of the chain
942  * @next: the field name of the next pointer in @type
943  *
944  * Frees a linked list of memory blocks of structure type @type.
945  * The memory blocks must be equal-sized, allocated via
946  * g_slice_alloc() or g_slice_alloc0() and linked together by
947  * a @next pointer (similar to #GSList). The name of the
948  * @next field in @type is passed as third argument.
949  * Note that the exact release behaviour can be changed with the
950  * <link linkend="G_DEBUG">G_DEBUG=gc-friendly</link> environment
951  * variable, also see <link linkend="G_SLICE">G_SLICE</link> for
952  * related debugging options.
953  *
954  * Since: 2.10
955  */
956
957 /**
958  * g_slice_alloc:
959  * @block_size: the number of bytes to allocate
960  *
961  * Allocates a block of memory from the slice allocator.
962  * The block adress handed out can be expected to be aligned
963  * to at least <literal>1 * sizeof (void*)</literal>,
964  * though in general slices are 2 * sizeof (void*) bytes aligned,
965  * if a malloc() fallback implementation is used instead,
966  * the alignment may be reduced in a libc dependent fashion.
967  * Note that the underlying slice allocation mechanism can
968  * be changed with the <link linkend="G_SLICE">G_SLICE=always-malloc</link>
969  * environment variable.
970  *
971  * Returns: a pointer to the allocated memory block
972  *
973  * Since: 2.10
974  */
975 gpointer
976 g_slice_alloc (gsize mem_size)
977 {
978   ThreadMemory *tmem;
979   gsize chunk_size;
980   gpointer mem;
981   guint acat;
982
983   /* This gets the private structure for this thread.  If the private
984    * structure does not yet exist, it is created.
985    *
986    * This has a side effect of causing GSlice to be initialised, so it
987    * must come first.
988    */
989   tmem = thread_memory_from_self ();
990
991   chunk_size = P2ALIGN (mem_size);
992   acat = allocator_categorize (chunk_size);
993   if (G_LIKELY (acat == 1))     /* allocate through magazine layer */
994     {
995       guint ix = SLAB_INDEX (allocator, chunk_size);
996       if (G_UNLIKELY (thread_memory_magazine1_is_empty (tmem, ix)))
997         {
998           thread_memory_swap_magazines (tmem, ix);
999           if (G_UNLIKELY (thread_memory_magazine1_is_empty (tmem, ix)))
1000             thread_memory_magazine1_reload (tmem, ix);
1001         }
1002       mem = thread_memory_magazine1_alloc (tmem, ix);
1003     }
1004   else if (acat == 2)           /* allocate through slab allocator */
1005     {
1006       g_mutex_lock (&allocator->slab_mutex);
1007       mem = slab_allocator_alloc_chunk (chunk_size);
1008       g_mutex_unlock (&allocator->slab_mutex);
1009     }
1010   else                          /* delegate to system malloc */
1011     mem = g_malloc (mem_size);
1012   if (G_UNLIKELY (allocator->config.debug_blocks))
1013     smc_notify_alloc (mem, mem_size);
1014
1015   TRACE (GLIB_SLICE_ALLOC((void*)mem, mem_size));
1016
1017   return mem;
1018 }
1019
1020 /**
1021  * g_slice_alloc0:
1022  * @block_size: the number of bytes to allocate
1023  *
1024  * Allocates a block of memory via g_slice_alloc() and initializes
1025  * the returned memory to 0. Note that the underlying slice allocation
1026  * mechanism can be changed with the
1027  * <link linkend="G_SLICE">G_SLICE=always-malloc</link>
1028  * environment variable.
1029  *
1030  * Returns: a pointer to the allocated block
1031  *
1032  * Since: 2.10
1033  */
1034 gpointer
1035 g_slice_alloc0 (gsize mem_size)
1036 {
1037   gpointer mem = g_slice_alloc (mem_size);
1038   if (mem)
1039     memset (mem, 0, mem_size);
1040   return mem;
1041 }
1042
1043 /**
1044  * g_slice_copy:
1045  * @block_size: the number of bytes to allocate
1046  * @mem_block: the memory to copy
1047  *
1048  * Allocates a block of memory from the slice allocator
1049  * and copies @block_size bytes into it from @mem_block.
1050  *
1051  * Returns: a pointer to the allocated memory block
1052  *
1053  * Since: 2.14
1054  */
1055 gpointer
1056 g_slice_copy (gsize         mem_size,
1057               gconstpointer mem_block)
1058 {
1059   gpointer mem = g_slice_alloc (mem_size);
1060   if (mem)
1061     memcpy (mem, mem_block, mem_size);
1062   return mem;
1063 }
1064
1065 /**
1066  * g_slice_free1:
1067  * @block_size: the size of the block
1068  * @mem_block: a pointer to the block to free
1069  *
1070  * Frees a block of memory.
1071  *
1072  * The memory must have been allocated via g_slice_alloc() or
1073  * g_slice_alloc0() and the @block_size has to match the size
1074  * specified upon allocation. Note that the exact release behaviour
1075  * can be changed with the
1076  * <link linkend="G_DEBUG">G_DEBUG=gc-friendly</link> environment
1077  * variable, also see <link linkend="G_SLICE">G_SLICE</link> for
1078  * related debugging options.
1079  *
1080  * Since: 2.10
1081  */
1082 void
1083 g_slice_free1 (gsize    mem_size,
1084                gpointer mem_block)
1085 {
1086   gsize chunk_size = P2ALIGN (mem_size);
1087   guint acat = allocator_categorize (chunk_size);
1088   if (G_UNLIKELY (!mem_block))
1089     return;
1090   if (G_UNLIKELY (allocator->config.debug_blocks) &&
1091       !smc_notify_free (mem_block, mem_size))
1092     abort();
1093   if (G_LIKELY (acat == 1))             /* allocate through magazine layer */
1094     {
1095       ThreadMemory *tmem = thread_memory_from_self();
1096       guint ix = SLAB_INDEX (allocator, chunk_size);
1097       if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
1098         {
1099           thread_memory_swap_magazines (tmem, ix);
1100           if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
1101             thread_memory_magazine2_unload (tmem, ix);
1102         }
1103       if (G_UNLIKELY (g_mem_gc_friendly))
1104         memset (mem_block, 0, chunk_size);
1105       thread_memory_magazine2_free (tmem, ix, mem_block);
1106     }
1107   else if (acat == 2)                   /* allocate through slab allocator */
1108     {
1109       if (G_UNLIKELY (g_mem_gc_friendly))
1110         memset (mem_block, 0, chunk_size);
1111       g_mutex_lock (&allocator->slab_mutex);
1112       slab_allocator_free_chunk (chunk_size, mem_block);
1113       g_mutex_unlock (&allocator->slab_mutex);
1114     }
1115   else                                  /* delegate to system malloc */
1116     {
1117       if (G_UNLIKELY (g_mem_gc_friendly))
1118         memset (mem_block, 0, mem_size);
1119       g_free (mem_block);
1120     }
1121   TRACE (GLIB_SLICE_FREE((void*)mem_block, mem_size));
1122 }
1123
1124 /**
1125  * g_slice_free_chain_with_offset:
1126  * @block_size: the size of the blocks
1127  * @mem_chain:  a pointer to the first block of the chain
1128  * @next_offset: the offset of the @next field in the blocks
1129  *
1130  * Frees a linked list of memory blocks of structure type @type.
1131  *
1132  * The memory blocks must be equal-sized, allocated via
1133  * g_slice_alloc() or g_slice_alloc0() and linked together by a
1134  * @next pointer (similar to #GSList). The offset of the @next
1135  * field in each block is passed as third argument.
1136  * Note that the exact release behaviour can be changed with the
1137  * <link linkend="G_DEBUG">G_DEBUG=gc-friendly</link> environment
1138  * variable, also see <link linkend="G_SLICE">G_SLICE</link> for
1139  * related debugging options.
1140  *
1141  * Since: 2.10
1142  */
1143 void
1144 g_slice_free_chain_with_offset (gsize    mem_size,
1145                                 gpointer mem_chain,
1146                                 gsize    next_offset)
1147 {
1148   gpointer slice = mem_chain;
1149   /* while the thread magazines and the magazine cache are implemented so that
1150    * they can easily be extended to allow for free lists containing more free
1151    * lists for the first level nodes, which would allow O(1) freeing in this
1152    * function, the benefit of such an extension is questionable, because:
1153    * - the magazine size counts will become mere lower bounds which confuses
1154    *   the code adapting to lock contention;
1155    * - freeing a single node to the thread magazines is very fast, so this
1156    *   O(list_length) operation is multiplied by a fairly small factor;
1157    * - memory usage histograms on larger applications seem to indicate that
1158    *   the amount of released multi node lists is negligible in comparison
1159    *   to single node releases.
1160    * - the major performance bottle neck, namely g_private_get() or
1161    *   g_mutex_lock()/g_mutex_unlock() has already been moved out of the
1162    *   inner loop for freeing chained slices.
1163    */
1164   gsize chunk_size = P2ALIGN (mem_size);
1165   guint acat = allocator_categorize (chunk_size);
1166   if (G_LIKELY (acat == 1))             /* allocate through magazine layer */
1167     {
1168       ThreadMemory *tmem = thread_memory_from_self();
1169       guint ix = SLAB_INDEX (allocator, chunk_size);
1170       while (slice)
1171         {
1172           guint8 *current = slice;
1173           slice = *(gpointer*) (current + next_offset);
1174           if (G_UNLIKELY (allocator->config.debug_blocks) &&
1175               !smc_notify_free (current, mem_size))
1176             abort();
1177           if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
1178             {
1179               thread_memory_swap_magazines (tmem, ix);
1180               if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
1181                 thread_memory_magazine2_unload (tmem, ix);
1182             }
1183           if (G_UNLIKELY (g_mem_gc_friendly))
1184             memset (current, 0, chunk_size);
1185           thread_memory_magazine2_free (tmem, ix, current);
1186         }
1187     }
1188   else if (acat == 2)                   /* allocate through slab allocator */
1189     {
1190       g_mutex_lock (&allocator->slab_mutex);
1191       while (slice)
1192         {
1193           guint8 *current = slice;
1194           slice = *(gpointer*) (current + next_offset);
1195           if (G_UNLIKELY (allocator->config.debug_blocks) &&
1196               !smc_notify_free (current, mem_size))
1197             abort();
1198           if (G_UNLIKELY (g_mem_gc_friendly))
1199             memset (current, 0, chunk_size);
1200           slab_allocator_free_chunk (chunk_size, current);
1201         }
1202       g_mutex_unlock (&allocator->slab_mutex);
1203     }
1204   else                                  /* delegate to system malloc */
1205     while (slice)
1206       {
1207         guint8 *current = slice;
1208         slice = *(gpointer*) (current + next_offset);
1209         if (G_UNLIKELY (allocator->config.debug_blocks) &&
1210             !smc_notify_free (current, mem_size))
1211           abort();
1212         if (G_UNLIKELY (g_mem_gc_friendly))
1213           memset (current, 0, mem_size);
1214         g_free (current);
1215       }
1216 }
1217
1218 /* --- single page allocator --- */
1219 static void
1220 allocator_slab_stack_push (Allocator *allocator,
1221                            guint      ix,
1222                            SlabInfo  *sinfo)
1223 {
1224   /* insert slab at slab ring head */
1225   if (!allocator->slab_stack[ix])
1226     {
1227       sinfo->next = sinfo;
1228       sinfo->prev = sinfo;
1229     }
1230   else
1231     {
1232       SlabInfo *next = allocator->slab_stack[ix], *prev = next->prev;
1233       next->prev = sinfo;
1234       prev->next = sinfo;
1235       sinfo->next = next;
1236       sinfo->prev = prev;
1237     }
1238   allocator->slab_stack[ix] = sinfo;
1239 }
1240
1241 static gsize
1242 allocator_aligned_page_size (Allocator *allocator,
1243                              gsize      n_bytes)
1244 {
1245   gsize val = 1 << g_bit_storage (n_bytes - 1);
1246   val = MAX (val, allocator->min_page_size);
1247   return val;
1248 }
1249
1250 static void
1251 allocator_add_slab (Allocator *allocator,
1252                     guint      ix,
1253                     gsize      chunk_size)
1254 {
1255   ChunkLink *chunk;
1256   SlabInfo *sinfo;
1257   gsize addr, padding, n_chunks, color = 0;
1258   gsize page_size = allocator_aligned_page_size (allocator, SLAB_BPAGE_SIZE (allocator, chunk_size));
1259   /* allocate 1 page for the chunks and the slab */
1260   gpointer aligned_memory = allocator_memalign (page_size, page_size - NATIVE_MALLOC_PADDING);
1261   guint8 *mem = aligned_memory;
1262   guint i;
1263   if (!mem)
1264     {
1265       const gchar *syserr = strerror (errno);
1266       mem_error ("failed to allocate %u bytes (alignment: %u): %s\n",
1267                  (guint) (page_size - NATIVE_MALLOC_PADDING), (guint) page_size, syserr);
1268     }
1269   /* mask page address */
1270   addr = ((gsize) mem / page_size) * page_size;
1271   /* assert alignment */
1272   mem_assert (aligned_memory == (gpointer) addr);
1273   /* basic slab info setup */
1274   sinfo = (SlabInfo*) (mem + page_size - SLAB_INFO_SIZE);
1275   sinfo->n_allocated = 0;
1276   sinfo->chunks = NULL;
1277   /* figure cache colorization */
1278   n_chunks = ((guint8*) sinfo - mem) / chunk_size;
1279   padding = ((guint8*) sinfo - mem) - n_chunks * chunk_size;
1280   if (padding)
1281     {
1282       color = (allocator->color_accu * P2ALIGNMENT) % padding;
1283       allocator->color_accu += allocator->config.color_increment;
1284     }
1285   /* add chunks to free list */
1286   chunk = (ChunkLink*) (mem + color);
1287   sinfo->chunks = chunk;
1288   for (i = 0; i < n_chunks - 1; i++)
1289     {
1290       chunk->next = (ChunkLink*) ((guint8*) chunk + chunk_size);
1291       chunk = chunk->next;
1292     }
1293   chunk->next = NULL;   /* last chunk */
1294   /* add slab to slab ring */
1295   allocator_slab_stack_push (allocator, ix, sinfo);
1296 }
1297
1298 static gpointer
1299 slab_allocator_alloc_chunk (gsize chunk_size)
1300 {
1301   ChunkLink *chunk;
1302   guint ix = SLAB_INDEX (allocator, chunk_size);
1303   /* ensure non-empty slab */
1304   if (!allocator->slab_stack[ix] || !allocator->slab_stack[ix]->chunks)
1305     allocator_add_slab (allocator, ix, chunk_size);
1306   /* allocate chunk */
1307   chunk = allocator->slab_stack[ix]->chunks;
1308   allocator->slab_stack[ix]->chunks = chunk->next;
1309   allocator->slab_stack[ix]->n_allocated++;
1310   /* rotate empty slabs */
1311   if (!allocator->slab_stack[ix]->chunks)
1312     allocator->slab_stack[ix] = allocator->slab_stack[ix]->next;
1313   return chunk;
1314 }
1315
1316 static void
1317 slab_allocator_free_chunk (gsize    chunk_size,
1318                            gpointer mem)
1319 {
1320   ChunkLink *chunk;
1321   gboolean was_empty;
1322   guint ix = SLAB_INDEX (allocator, chunk_size);
1323   gsize page_size = allocator_aligned_page_size (allocator, SLAB_BPAGE_SIZE (allocator, chunk_size));
1324   gsize addr = ((gsize) mem / page_size) * page_size;
1325   /* mask page address */
1326   guint8 *page = (guint8*) addr;
1327   SlabInfo *sinfo = (SlabInfo*) (page + page_size - SLAB_INFO_SIZE);
1328   /* assert valid chunk count */
1329   mem_assert (sinfo->n_allocated > 0);
1330   /* add chunk to free list */
1331   was_empty = sinfo->chunks == NULL;
1332   chunk = (ChunkLink*) mem;
1333   chunk->next = sinfo->chunks;
1334   sinfo->chunks = chunk;
1335   sinfo->n_allocated--;
1336   /* keep slab ring partially sorted, empty slabs at end */
1337   if (was_empty)
1338     {
1339       /* unlink slab */
1340       SlabInfo *next = sinfo->next, *prev = sinfo->prev;
1341       next->prev = prev;
1342       prev->next = next;
1343       if (allocator->slab_stack[ix] == sinfo)
1344         allocator->slab_stack[ix] = next == sinfo ? NULL : next;
1345       /* insert slab at head */
1346       allocator_slab_stack_push (allocator, ix, sinfo);
1347     }
1348   /* eagerly free complete unused slabs */
1349   if (!sinfo->n_allocated)
1350     {
1351       /* unlink slab */
1352       SlabInfo *next = sinfo->next, *prev = sinfo->prev;
1353       next->prev = prev;
1354       prev->next = next;
1355       if (allocator->slab_stack[ix] == sinfo)
1356         allocator->slab_stack[ix] = next == sinfo ? NULL : next;
1357       /* free slab */
1358       allocator_memfree (page_size, page);
1359     }
1360 }
1361
1362 /* --- memalign implementation --- */
1363 #ifdef HAVE_MALLOC_H
1364 #include <malloc.h>             /* memalign() */
1365 #endif
1366
1367 /* from config.h:
1368  * define HAVE_POSIX_MEMALIGN           1 // if free(posix_memalign(3)) works, <stdlib.h>
1369  * define HAVE_COMPLIANT_POSIX_MEMALIGN 1 // if free(posix_memalign(3)) works for sizes != 2^n, <stdlib.h>
1370  * define HAVE_MEMALIGN                 1 // if free(memalign(3)) works, <malloc.h>
1371  * define HAVE_VALLOC                   1 // if free(valloc(3)) works, <stdlib.h> or <malloc.h>
1372  * if none is provided, we implement malloc(3)-based alloc-only page alignment
1373  */
1374
1375 #if !(HAVE_COMPLIANT_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_VALLOC)
1376 static GTrashStack *compat_valloc_trash = NULL;
1377 #endif
1378
1379 static gpointer
1380 allocator_memalign (gsize alignment,
1381                     gsize memsize)
1382 {
1383   gpointer aligned_memory = NULL;
1384   gint err = ENOMEM;
1385 #if     HAVE_COMPLIANT_POSIX_MEMALIGN
1386   err = posix_memalign (&aligned_memory, alignment, memsize);
1387 #elif   HAVE_MEMALIGN
1388   errno = 0;
1389   aligned_memory = memalign (alignment, memsize);
1390   err = errno;
1391 #elif   HAVE_VALLOC
1392   errno = 0;
1393   aligned_memory = valloc (memsize);
1394   err = errno;
1395 #else
1396   /* simplistic non-freeing page allocator */
1397   mem_assert (alignment == sys_page_size);
1398   mem_assert (memsize <= sys_page_size);
1399   if (!compat_valloc_trash)
1400     {
1401       const guint n_pages = 16;
1402       guint8 *mem = malloc (n_pages * sys_page_size);
1403       err = errno;
1404       if (mem)
1405         {
1406           gint i = n_pages;
1407           guint8 *amem = (guint8*) ALIGN ((gsize) mem, sys_page_size);
1408           if (amem != mem)
1409             i--;        /* mem wasn't page aligned */
1410           while (--i >= 0)
1411             g_trash_stack_push (&compat_valloc_trash, amem + i * sys_page_size);
1412         }
1413     }
1414   aligned_memory = g_trash_stack_pop (&compat_valloc_trash);
1415 #endif
1416   if (!aligned_memory)
1417     errno = err;
1418   return aligned_memory;
1419 }
1420
1421 static void
1422 allocator_memfree (gsize    memsize,
1423                    gpointer mem)
1424 {
1425 #if     HAVE_COMPLIANT_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_VALLOC
1426   free (mem);
1427 #else
1428   mem_assert (memsize <= sys_page_size);
1429   g_trash_stack_push (&compat_valloc_trash, mem);
1430 #endif
1431 }
1432
1433 static void
1434 mem_error (const char *format,
1435            ...)
1436 {
1437   const char *pname;
1438   va_list args;
1439   /* at least, put out "MEMORY-ERROR", in case we segfault during the rest of the function */
1440   fputs ("\n***MEMORY-ERROR***: ", stderr);
1441   pname = g_get_prgname();
1442   fprintf (stderr, "%s[%ld]: GSlice: ", pname ? pname : "", (long)getpid());
1443   va_start (args, format);
1444   vfprintf (stderr, format, args);
1445   va_end (args);
1446   fputs ("\n", stderr);
1447   abort();
1448   _exit (1);
1449 }
1450
1451 /* --- g-slice memory checker tree --- */
1452 typedef size_t SmcKType;                /* key type */
1453 typedef size_t SmcVType;                /* value type */
1454 typedef struct {
1455   SmcKType key;
1456   SmcVType value;
1457 } SmcEntry;
1458 static void             smc_tree_insert      (SmcKType  key,
1459                                               SmcVType  value);
1460 static gboolean         smc_tree_lookup      (SmcKType  key,
1461                                               SmcVType *value_p);
1462 static gboolean         smc_tree_remove      (SmcKType  key);
1463
1464
1465 /* --- g-slice memory checker implementation --- */
1466 static void
1467 smc_notify_alloc (void   *pointer,
1468                   size_t  size)
1469 {
1470   size_t adress = (size_t) pointer;
1471   if (pointer)
1472     smc_tree_insert (adress, size);
1473 }
1474
1475 #if 0
1476 static void
1477 smc_notify_ignore (void *pointer)
1478 {
1479   size_t adress = (size_t) pointer;
1480   if (pointer)
1481     smc_tree_remove (adress);
1482 }
1483 #endif
1484
1485 static int
1486 smc_notify_free (void   *pointer,
1487                  size_t  size)
1488 {
1489   size_t adress = (size_t) pointer;
1490   SmcVType real_size;
1491   gboolean found_one;
1492
1493   if (!pointer)
1494     return 1; /* ignore */
1495   found_one = smc_tree_lookup (adress, &real_size);
1496   if (!found_one)
1497     {
1498       fprintf (stderr, "GSlice: MemChecker: attempt to release non-allocated block: %p size=%" G_GSIZE_FORMAT "\n", pointer, size);
1499       return 0;
1500     }
1501   if (real_size != size && (real_size || size))
1502     {
1503       fprintf (stderr, "GSlice: MemChecker: attempt to release block with invalid size: %p size=%" G_GSIZE_FORMAT " invalid-size=%" G_GSIZE_FORMAT "\n", pointer, real_size, size);
1504       return 0;
1505     }
1506   if (!smc_tree_remove (adress))
1507     {
1508       fprintf (stderr, "GSlice: MemChecker: attempt to release non-allocated block: %p size=%" G_GSIZE_FORMAT "\n", pointer, size);
1509       return 0;
1510     }
1511   return 1; /* all fine */
1512 }
1513
1514 /* --- g-slice memory checker tree implementation --- */
1515 #define SMC_TRUNK_COUNT     (4093 /* 16381 */)          /* prime, to distribute trunk collisions (big, allocated just once) */
1516 #define SMC_BRANCH_COUNT    (511)                       /* prime, to distribute branch collisions */
1517 #define SMC_TRUNK_EXTENT    (SMC_BRANCH_COUNT * 2039)   /* key address space per trunk, should distribute uniformly across BRANCH_COUNT */
1518 #define SMC_TRUNK_HASH(k)   ((k / SMC_TRUNK_EXTENT) % SMC_TRUNK_COUNT)  /* generate new trunk hash per megabyte (roughly) */
1519 #define SMC_BRANCH_HASH(k)  (k % SMC_BRANCH_COUNT)
1520
1521 typedef struct {
1522   SmcEntry    *entries;
1523   unsigned int n_entries;
1524 } SmcBranch;
1525
1526 static SmcBranch     **smc_tree_root = NULL;
1527
1528 static void
1529 smc_tree_abort (int errval)
1530 {
1531   const char *syserr = strerror (errval);
1532   mem_error ("MemChecker: failure in debugging tree: %s", syserr);
1533 }
1534
1535 static inline SmcEntry*
1536 smc_tree_branch_grow_L (SmcBranch   *branch,
1537                         unsigned int index)
1538 {
1539   unsigned int old_size = branch->n_entries * sizeof (branch->entries[0]);
1540   unsigned int new_size = old_size + sizeof (branch->entries[0]);
1541   SmcEntry *entry;
1542   mem_assert (index <= branch->n_entries);
1543   branch->entries = (SmcEntry*) realloc (branch->entries, new_size);
1544   if (!branch->entries)
1545     smc_tree_abort (errno);
1546   entry = branch->entries + index;
1547   memmove (entry + 1, entry, (branch->n_entries - index) * sizeof (entry[0]));
1548   branch->n_entries += 1;
1549   return entry;
1550 }
1551
1552 static inline SmcEntry*
1553 smc_tree_branch_lookup_nearest_L (SmcBranch *branch,
1554                                   SmcKType   key)
1555 {
1556   unsigned int n_nodes = branch->n_entries, offs = 0;
1557   SmcEntry *check = branch->entries;
1558   int cmp = 0;
1559   while (offs < n_nodes)
1560     {
1561       unsigned int i = (offs + n_nodes) >> 1;
1562       check = branch->entries + i;
1563       cmp = key < check->key ? -1 : key != check->key;
1564       if (cmp == 0)
1565         return check;                   /* return exact match */
1566       else if (cmp < 0)
1567         n_nodes = i;
1568       else /* (cmp > 0) */
1569         offs = i + 1;
1570     }
1571   /* check points at last mismatch, cmp > 0 indicates greater key */
1572   return cmp > 0 ? check + 1 : check;   /* return insertion position for inexact match */
1573 }
1574
1575 static void
1576 smc_tree_insert (SmcKType key,
1577                  SmcVType value)
1578 {
1579   unsigned int ix0, ix1;
1580   SmcEntry *entry;
1581
1582   g_mutex_lock (&smc_tree_mutex);
1583   ix0 = SMC_TRUNK_HASH (key);
1584   ix1 = SMC_BRANCH_HASH (key);
1585   if (!smc_tree_root)
1586     {
1587       smc_tree_root = calloc (SMC_TRUNK_COUNT, sizeof (smc_tree_root[0]));
1588       if (!smc_tree_root)
1589         smc_tree_abort (errno);
1590     }
1591   if (!smc_tree_root[ix0])
1592     {
1593       smc_tree_root[ix0] = calloc (SMC_BRANCH_COUNT, sizeof (smc_tree_root[0][0]));
1594       if (!smc_tree_root[ix0])
1595         smc_tree_abort (errno);
1596     }
1597   entry = smc_tree_branch_lookup_nearest_L (&smc_tree_root[ix0][ix1], key);
1598   if (!entry ||                                                                         /* need create */
1599       entry >= smc_tree_root[ix0][ix1].entries + smc_tree_root[ix0][ix1].n_entries ||   /* need append */
1600       entry->key != key)                                                                /* need insert */
1601     entry = smc_tree_branch_grow_L (&smc_tree_root[ix0][ix1], entry - smc_tree_root[ix0][ix1].entries);
1602   entry->key = key;
1603   entry->value = value;
1604   g_mutex_unlock (&smc_tree_mutex);
1605 }
1606
1607 static gboolean
1608 smc_tree_lookup (SmcKType  key,
1609                  SmcVType *value_p)
1610 {
1611   SmcEntry *entry = NULL;
1612   unsigned int ix0 = SMC_TRUNK_HASH (key), ix1 = SMC_BRANCH_HASH (key);
1613   gboolean found_one = FALSE;
1614   *value_p = 0;
1615   g_mutex_lock (&smc_tree_mutex);
1616   if (smc_tree_root && smc_tree_root[ix0])
1617     {
1618       entry = smc_tree_branch_lookup_nearest_L (&smc_tree_root[ix0][ix1], key);
1619       if (entry &&
1620           entry < smc_tree_root[ix0][ix1].entries + smc_tree_root[ix0][ix1].n_entries &&
1621           entry->key == key)
1622         {
1623           found_one = TRUE;
1624           *value_p = entry->value;
1625         }
1626     }
1627   g_mutex_unlock (&smc_tree_mutex);
1628   return found_one;
1629 }
1630
1631 static gboolean
1632 smc_tree_remove (SmcKType key)
1633 {
1634   unsigned int ix0 = SMC_TRUNK_HASH (key), ix1 = SMC_BRANCH_HASH (key);
1635   gboolean found_one = FALSE;
1636   g_mutex_lock (&smc_tree_mutex);
1637   if (smc_tree_root && smc_tree_root[ix0])
1638     {
1639       SmcEntry *entry = smc_tree_branch_lookup_nearest_L (&smc_tree_root[ix0][ix1], key);
1640       if (entry &&
1641           entry < smc_tree_root[ix0][ix1].entries + smc_tree_root[ix0][ix1].n_entries &&
1642           entry->key == key)
1643         {
1644           unsigned int i = entry - smc_tree_root[ix0][ix1].entries;
1645           smc_tree_root[ix0][ix1].n_entries -= 1;
1646           memmove (entry, entry + 1, (smc_tree_root[ix0][ix1].n_entries - i) * sizeof (entry[0]));
1647           if (!smc_tree_root[ix0][ix1].n_entries)
1648             {
1649               /* avoid useless pressure on the memory system */
1650               free (smc_tree_root[ix0][ix1].entries);
1651               smc_tree_root[ix0][ix1].entries = NULL;
1652             }
1653           found_one = TRUE;
1654         }
1655     }
1656   g_mutex_unlock (&smc_tree_mutex);
1657   return found_one;
1658 }
1659
1660 #ifdef G_ENABLE_DEBUG
1661 void
1662 g_slice_debug_tree_statistics (void)
1663 {
1664   g_mutex_lock (&smc_tree_mutex);
1665   if (smc_tree_root)
1666     {
1667       unsigned int i, j, t = 0, o = 0, b = 0, su = 0, ex = 0, en = 4294967295u;
1668       double tf, bf;
1669       for (i = 0; i < SMC_TRUNK_COUNT; i++)
1670         if (smc_tree_root[i])
1671           {
1672             t++;
1673             for (j = 0; j < SMC_BRANCH_COUNT; j++)
1674               if (smc_tree_root[i][j].n_entries)
1675                 {
1676                   b++;
1677                   su += smc_tree_root[i][j].n_entries;
1678                   en = MIN (en, smc_tree_root[i][j].n_entries);
1679                   ex = MAX (ex, smc_tree_root[i][j].n_entries);
1680                 }
1681               else if (smc_tree_root[i][j].entries)
1682                 o++; /* formerly used, now empty */
1683           }
1684       en = b ? en : 0;
1685       tf = MAX (t, 1.0); /* max(1) to be a valid divisor */
1686       bf = MAX (b, 1.0); /* max(1) to be a valid divisor */
1687       fprintf (stderr, "GSlice: MemChecker: %u trunks, %u branches, %u old branches\n", t, b, o);
1688       fprintf (stderr, "GSlice: MemChecker: %f branches per trunk, %.2f%% utilization\n",
1689                b / tf,
1690                100.0 - (SMC_BRANCH_COUNT - b / tf) / (0.01 * SMC_BRANCH_COUNT));
1691       fprintf (stderr, "GSlice: MemChecker: %f entries per branch, %u minimum, %u maximum\n",
1692                su / bf, en, ex);
1693     }
1694   else
1695     fprintf (stderr, "GSlice: MemChecker: root=NULL\n");
1696   g_mutex_unlock (&smc_tree_mutex);
1697   
1698   /* sample statistics (beast + GSLice + 24h scripted core & GUI activity):
1699    *  PID %CPU %MEM   VSZ  RSS      COMMAND
1700    * 8887 30.3 45.8 456068 414856   beast-0.7.1 empty.bse
1701    * $ cat /proc/8887/statm # total-program-size resident-set-size shared-pages text/code data/stack library dirty-pages
1702    * 114017 103714 2354 344 0 108676 0
1703    * $ cat /proc/8887/status 
1704    * Name:   beast-0.7.1
1705    * VmSize:   456068 kB
1706    * VmLck:         0 kB
1707    * VmRSS:    414856 kB
1708    * VmData:   434620 kB
1709    * VmStk:        84 kB
1710    * VmExe:      1376 kB
1711    * VmLib:     13036 kB
1712    * VmPTE:       456 kB
1713    * Threads:        3
1714    * (gdb) print g_slice_debug_tree_statistics ()
1715    * GSlice: MemChecker: 422 trunks, 213068 branches, 0 old branches
1716    * GSlice: MemChecker: 504.900474 branches per trunk, 98.81% utilization
1717    * GSlice: MemChecker: 4.965039 entries per branch, 1 minimum, 37 maximum
1718    */
1719 }
1720 #endif /* G_ENABLE_DEBUG */