Ok, I'm a moron. When I originally implemented ENABLE_GC_FRIENDLY, I
[platform/upstream/glib.git] / glib / gmem.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GLib Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 /* 
28  * MT safe
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include "glib.h"
38
39 /* #define ENABLE_MEM_PROFILE */
40 /* #define ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS */
41 /* #define ENABLE_MEM_CHECK */
42 #define MEM_PROFILE_TABLE_SIZE 8192
43
44 /*
45  * This library can check for some attempts to do illegal things to
46  * memory (ENABLE_MEM_CHECK), and can do profiling
47  * (ENABLE_MEM_PROFILE).  Both features are implemented by storing
48  * words before the start of the memory chunk.
49  *
50  * The first, at offset -2*SIZEOF_LONG, is used only if
51  * ENABLE_MEM_CHECK is set, and stores 0 after the memory has been
52  * allocated and 1 when it has been freed.  The second, at offset
53  * -SIZEOF_LONG, is used if either flag is set and stores the size of
54  * the block.
55  *
56  * The MEM_CHECK flag is checked when memory is realloc'd and free'd,
57  * and it can be explicitly checked before using a block by calling
58  * g_mem_check().
59  */
60
61 #if defined(ENABLE_MEM_PROFILE) && defined(ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS)
62 #define ENTER_MEM_CHUNK_ROUTINE() \
63   g_private_set (allocating_for_mem_chunk, \
64                 g_private_get (allocating_for_mem_chunk) + 1)
65 #define LEAVE_MEM_CHUNK_ROUTINE() \
66   g_private_set (allocating_for_mem_chunk, \
67                 g_private_get (allocating_for_mem_chunk) - 1) 
68 #else
69 #define ENTER_MEM_CHUNK_ROUTINE()
70 #define LEAVE_MEM_CHUNK_ROUTINE()
71 #endif
72
73
74 #define MEM_AREA_SIZE 4L
75
76 #if SIZEOF_VOID_P > SIZEOF_LONG
77 #define MEM_ALIGN     SIZEOF_VOID_P
78 #else
79 #define MEM_ALIGN     SIZEOF_LONG
80 #endif
81
82
83 typedef struct _GFreeAtom      GFreeAtom;
84 typedef struct _GMemArea       GMemArea;
85 typedef struct _GRealMemChunk  GRealMemChunk;
86
87 struct _GFreeAtom
88 {
89   GFreeAtom *next;
90 };
91
92 struct _GMemArea
93 {
94   GMemArea *next;            /* the next mem area */
95   GMemArea *prev;            /* the previous mem area */
96   gulong index;              /* the current index into the "mem" array */
97   gulong free;               /* the number of free bytes in this mem area */
98   gulong allocated;          /* the number of atoms allocated from this area */
99   gulong mark;               /* is this mem area marked for deletion */
100   gchar mem[MEM_AREA_SIZE];  /* the mem array from which atoms get allocated
101                               * the actual size of this array is determined by
102                               *  the mem chunk "area_size". ANSI says that it
103                               *  must be declared to be the maximum size it
104                               *  can possibly be (even though the actual size
105                               *  may be less).
106                               */
107 };
108
109 struct _GRealMemChunk
110 {
111   gchar *name;               /* name of this MemChunk...used for debugging output */
112   gint type;                 /* the type of MemChunk: ALLOC_ONLY or ALLOC_AND_FREE */
113   gint num_mem_areas;        /* the number of memory areas */
114   gint num_marked_areas;     /* the number of areas marked for deletion */
115   guint atom_size;           /* the size of an atom */
116   gulong area_size;          /* the size of a memory area */
117   GMemArea *mem_area;        /* the current memory area */
118   GMemArea *mem_areas;       /* a list of all the mem areas owned by this chunk */
119   GMemArea *free_mem_area;   /* the free area...which is about to be destroyed */
120   GFreeAtom *free_atoms;     /* the free atoms list */
121   GTree *mem_tree;           /* tree of mem areas sorted by memory address */
122   GRealMemChunk *next;       /* pointer to the next chunk */
123   GRealMemChunk *prev;       /* pointer to the previous chunk */
124 };
125
126
127 #ifndef DISABLE_MEM_POOLS
128 static gulong g_mem_chunk_compute_size (gulong    size,
129                                         gulong    min_size) G_GNUC_CONST;
130 static gint   g_mem_chunk_area_compare (GMemArea *a,
131                                         GMemArea *b);
132 static gint   g_mem_chunk_area_search  (GMemArea *a,
133                                         gchar    *addr);
134
135
136 /* here we can't use StaticMutexes, as they depend upon a working
137  * g_malloc, the same holds true for StaticPrivate */
138 static GMutex* mem_chunks_lock = NULL;
139 static GRealMemChunk *mem_chunks = NULL;
140 #endif
141
142 #ifdef ENABLE_MEM_PROFILE
143 static GMutex* mem_profile_lock;
144 static gulong allocations[MEM_PROFILE_TABLE_SIZE] = { 0 };
145 static gulong allocated_mem = 0;
146 static gulong freed_mem = 0;
147 static GPrivate* allocating_for_mem_chunk = NULL;
148 #define IS_IN_MEM_CHUNK_ROUTINE() \
149   GPOINTER_TO_UINT (g_private_get (allocating_for_mem_chunk))
150 #endif /* ENABLE_MEM_PROFILE */
151
152
153 #ifndef USE_DMALLOC
154
155 gpointer
156 g_malloc (gulong size)
157 {
158   gpointer p;
159   
160   
161 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
162   gulong *t;
163 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
164   
165   
166   if (size == 0)
167     return NULL;
168   
169   
170 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
171   size += SIZEOF_LONG;
172 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
173   
174 #ifdef ENABLE_MEM_CHECK
175   size += SIZEOF_LONG;
176 #endif /* ENABLE_MEM_CHECK */
177   
178   
179   p = (gpointer) malloc (size);
180   if (!p)
181     g_error ("could not allocate %ld bytes", size);
182   
183   
184 #ifdef ENABLE_MEM_CHECK
185   size -= SIZEOF_LONG;
186   
187   t = p;
188   p = ((guchar*) p + SIZEOF_LONG);
189   *t = 0;
190 #endif /* ENABLE_MEM_CHECK */
191   
192 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
193   size -= SIZEOF_LONG;
194   
195   t = p;
196   p = ((guchar*) p + SIZEOF_LONG);
197   *t = size;
198   
199 #ifdef ENABLE_MEM_PROFILE
200   g_mutex_lock (mem_profile_lock);
201 #  ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
202   if(!IS_IN_MEM_CHUNK_ROUTINE()) {
203 #  endif
204     if (size <= MEM_PROFILE_TABLE_SIZE - 1)
205       allocations[size-1] += 1;
206     else
207       allocations[MEM_PROFILE_TABLE_SIZE - 1] += 1;
208     allocated_mem += size;
209 #  ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
210   }
211 #  endif
212   g_mutex_unlock (mem_profile_lock);
213 #endif /* ENABLE_MEM_PROFILE */
214 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
215   
216   
217   return p;
218 }
219
220 gpointer
221 g_malloc0 (gulong size)
222 {
223   gpointer p;
224   
225   
226 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
227   gulong *t;
228 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
229   
230   
231   if (size == 0)
232     return NULL;
233   
234   
235 #if defined (ENABLE_MEM_PROFILE) || defined (ENABLE_MEM_CHECK)
236   size += SIZEOF_LONG;
237 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
238   
239 #ifdef ENABLE_MEM_CHECK
240   size += SIZEOF_LONG;
241 #endif /* ENABLE_MEM_CHECK */
242   
243   
244   p = (gpointer) calloc (size, 1);
245   if (!p)
246     g_error ("could not allocate %ld bytes", size);
247   
248   
249 #ifdef ENABLE_MEM_CHECK
250   size -= SIZEOF_LONG;
251   
252   t = p;
253   p = ((guchar*) p + SIZEOF_LONG);
254   *t = 0;
255 #endif /* ENABLE_MEM_CHECK */
256   
257 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
258   size -= SIZEOF_LONG;
259   
260   t = p;
261   p = ((guchar*) p + SIZEOF_LONG);
262   *t = size;
263   
264 #  ifdef ENABLE_MEM_PROFILE
265   g_mutex_lock (mem_profile_lock);
266 #    ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
267   if(!IS_IN_MEM_CHUNK_ROUTINE()) {
268 #    endif
269     if (size <= (MEM_PROFILE_TABLE_SIZE - 1))
270       allocations[size-1] += 1;
271     else
272       allocations[MEM_PROFILE_TABLE_SIZE - 1] += 1;
273     allocated_mem += size;
274 #    ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
275   }
276 #    endif
277   g_mutex_unlock (mem_profile_lock);
278 #  endif /* ENABLE_MEM_PROFILE */
279 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
280   
281   
282   return p;
283 }
284
285 gpointer
286 g_realloc (gpointer mem,
287            gulong   size)
288 {
289   gpointer p;
290   
291 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
292   gulong *t;
293 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
294   
295   
296   if (size == 0)
297     {
298       g_free (mem);
299     
300       return NULL;
301     }
302   
303   
304 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
305   size += SIZEOF_LONG;
306 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
307   
308 #ifdef ENABLE_MEM_CHECK
309   size += SIZEOF_LONG;
310 #endif /* ENABLE_MEM_CHECK */
311   
312   
313   if (!mem)
314     {
315 #ifdef REALLOC_0_WORKS
316       p = (gpointer) realloc (NULL, size);
317 #else /* !REALLOC_0_WORKS */
318       p = (gpointer) malloc (size);
319 #endif /* !REALLOC_0_WORKS */
320     }
321   else
322     {
323 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
324       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
325 #ifdef ENABLE_MEM_PROFILE
326       g_mutex_lock (mem_profile_lock);
327       freed_mem += *t;
328       g_mutex_unlock (mem_profile_lock);
329 #endif /* ENABLE_MEM_PROFILE */
330       mem = t;
331 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
332       
333 #ifdef ENABLE_MEM_CHECK
334       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
335       if (*t >= 1)
336         g_warning ("trying to realloc freed memory\n");
337       mem = t;
338 #endif /* ENABLE_MEM_CHECK */
339       
340       p = (gpointer) realloc (mem, size);
341     }
342   
343   if (!p)
344     g_error ("could not reallocate %lu bytes", (gulong) size);
345   
346   
347 #ifdef ENABLE_MEM_CHECK
348   size -= SIZEOF_LONG;
349   
350   t = p;
351   p = ((guchar*) p + SIZEOF_LONG);
352   *t = 0;
353 #endif /* ENABLE_MEM_CHECK */
354   
355 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
356   size -= SIZEOF_LONG;
357   
358   t = p;
359   p = ((guchar*) p + SIZEOF_LONG);
360   *t = size;
361   
362 #ifdef ENABLE_MEM_PROFILE
363   g_mutex_lock (mem_profile_lock);
364 #ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
365   if(!IS_IN_MEM_CHUNK_ROUTINE()) {
366 #endif
367     if (size <= (MEM_PROFILE_TABLE_SIZE - 1))
368       allocations[size-1] += 1;
369     else
370       allocations[MEM_PROFILE_TABLE_SIZE - 1] += 1;
371     allocated_mem += size;
372 #ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
373   }
374 #endif
375   g_mutex_unlock (mem_profile_lock);
376 #endif /* ENABLE_MEM_PROFILE */
377 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
378   
379   
380   return p;
381 }
382
383 void
384 g_free (gpointer mem)
385 {
386   if (mem)
387     {
388 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
389       gulong *t;
390       gulong size;
391 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
392       
393 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
394       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
395       size = *t;
396 #ifdef ENABLE_MEM_PROFILE     
397       g_mutex_lock (mem_profile_lock);
398       freed_mem += size;
399       g_mutex_unlock (mem_profile_lock);
400 #endif /* ENABLE_MEM_PROFILE */
401       mem = t;
402 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
403       
404 #ifdef ENABLE_MEM_CHECK
405       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
406       if (*t >= 1)
407         g_warning ("freeing previously freed (%lu times) memory\n", *t);
408       *t += 1;
409       mem = t;
410       
411       memset ((guchar*) mem + 2 * SIZEOF_LONG, 0, size);
412 #else /* ENABLE_MEM_CHECK */
413       free (mem);
414 #endif /* ENABLE_MEM_CHECK */
415     }
416 }
417
418 #endif /* ! USE_DMALLOC */
419
420
421 void
422 g_mem_profile (void)
423 {
424 #ifdef ENABLE_MEM_PROFILE
425   gint i;
426   gulong local_allocations[MEM_PROFILE_TABLE_SIZE];
427   gulong local_allocated_mem;
428   gulong local_freed_mem;  
429
430   g_mutex_lock (mem_profile_lock);
431   for (i = 0; i < MEM_PROFILE_TABLE_SIZE; i++)
432     local_allocations[i] = allocations[i];
433   local_allocated_mem = allocated_mem;
434   local_freed_mem = freed_mem;
435   g_mutex_unlock (mem_profile_lock);
436
437   for (i = 0; i < (MEM_PROFILE_TABLE_SIZE - 1); i++)
438     if (local_allocations[i] > 0)
439       g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
440              "%lu allocations of %d bytes", local_allocations[i], i + 1);
441   
442   if (local_allocations[MEM_PROFILE_TABLE_SIZE - 1] > 0)
443     g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
444            "%lu allocations of greater than %d bytes",
445            local_allocations[MEM_PROFILE_TABLE_SIZE - 1], MEM_PROFILE_TABLE_SIZE - 1);
446   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes allocated", local_allocated_mem);
447   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes freed", local_freed_mem);
448   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes in use", local_allocated_mem - local_freed_mem);
449 #endif /* ENABLE_MEM_PROFILE */
450 }
451
452 void
453 g_mem_check (gpointer mem)
454 {
455 #ifdef ENABLE_MEM_CHECK
456   gulong *t;
457   
458   t = (gulong*) ((guchar*) mem - SIZEOF_LONG - SIZEOF_LONG);
459   
460   if (*t >= 1)
461     g_warning ("mem: 0x%08lx has been freed %lu times\n", (gulong) mem, *t);
462 #endif /* ENABLE_MEM_CHECK */
463 }
464
465 #ifndef DISABLE_MEM_POOLS
466 GMemChunk*
467 g_mem_chunk_new (gchar  *name,
468                  gint    atom_size,
469                  gulong  area_size,
470                  gint    type)
471 {
472   GRealMemChunk *mem_chunk;
473   gulong rarea_size;
474
475   g_return_val_if_fail (atom_size > 0, NULL);
476   g_return_val_if_fail (area_size >= atom_size, NULL);
477
478   ENTER_MEM_CHUNK_ROUTINE();
479
480   area_size = (area_size + atom_size - 1) / atom_size;
481   area_size *= atom_size;
482
483   mem_chunk = g_new (struct _GRealMemChunk, 1);
484   mem_chunk->name = name;
485   mem_chunk->type = type;
486   mem_chunk->num_mem_areas = 0;
487   mem_chunk->num_marked_areas = 0;
488   mem_chunk->mem_area = NULL;
489   mem_chunk->free_mem_area = NULL;
490   mem_chunk->free_atoms = NULL;
491   mem_chunk->mem_tree = NULL;
492   mem_chunk->mem_areas = NULL;
493   mem_chunk->atom_size = atom_size;
494   
495   if (mem_chunk->type == G_ALLOC_AND_FREE)
496     mem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare);
497   
498   if (mem_chunk->atom_size % MEM_ALIGN)
499     mem_chunk->atom_size += MEM_ALIGN - (mem_chunk->atom_size % MEM_ALIGN);
500
501   rarea_size = area_size + sizeof (GMemArea) - MEM_AREA_SIZE;
502   rarea_size = g_mem_chunk_compute_size (rarea_size, atom_size + sizeof (GMemArea) - MEM_AREA_SIZE);
503   mem_chunk->area_size = rarea_size - (sizeof (GMemArea) - MEM_AREA_SIZE);
504
505   g_mutex_lock (mem_chunks_lock);
506   mem_chunk->next = mem_chunks;
507   mem_chunk->prev = NULL;
508   if (mem_chunks)
509     mem_chunks->prev = mem_chunk;
510   mem_chunks = mem_chunk;
511   g_mutex_unlock (mem_chunks_lock);
512
513   LEAVE_MEM_CHUNK_ROUTINE();
514
515   return ((GMemChunk*) mem_chunk);
516 }
517
518 void
519 g_mem_chunk_destroy (GMemChunk *mem_chunk)
520 {
521   GRealMemChunk *rmem_chunk;
522   GMemArea *mem_areas;
523   GMemArea *temp_area;
524   
525   g_return_if_fail (mem_chunk != NULL);
526
527   ENTER_MEM_CHUNK_ROUTINE();
528
529   rmem_chunk = (GRealMemChunk*) mem_chunk;
530   
531   mem_areas = rmem_chunk->mem_areas;
532   while (mem_areas)
533     {
534       temp_area = mem_areas;
535       mem_areas = mem_areas->next;
536       g_free (temp_area);
537     }
538   
539   if (rmem_chunk->next)
540     rmem_chunk->next->prev = rmem_chunk->prev;
541   if (rmem_chunk->prev)
542     rmem_chunk->prev->next = rmem_chunk->next;
543   
544   g_mutex_lock (mem_chunks_lock);
545   if (rmem_chunk == mem_chunks)
546     mem_chunks = mem_chunks->next;
547   g_mutex_unlock (mem_chunks_lock);
548   
549   if (rmem_chunk->type == G_ALLOC_AND_FREE)
550     g_tree_destroy (rmem_chunk->mem_tree);
551   
552   g_free (rmem_chunk);
553
554   LEAVE_MEM_CHUNK_ROUTINE();
555 }
556
557 gpointer
558 g_mem_chunk_alloc (GMemChunk *mem_chunk)
559 {
560   GRealMemChunk *rmem_chunk;
561   GMemArea *temp_area;
562   gpointer mem;
563
564   ENTER_MEM_CHUNK_ROUTINE();
565
566   g_return_val_if_fail (mem_chunk != NULL, NULL);
567   
568   rmem_chunk = (GRealMemChunk*) mem_chunk;
569   
570   while (rmem_chunk->free_atoms)
571     {
572       /* Get the first piece of memory on the "free_atoms" list.
573        * We can go ahead and destroy the list node we used to keep
574        *  track of it with and to update the "free_atoms" list to
575        *  point to its next element.
576        */
577       mem = rmem_chunk->free_atoms;
578       rmem_chunk->free_atoms = rmem_chunk->free_atoms->next;
579       
580       /* Determine which area this piece of memory is allocated from */
581       temp_area = g_tree_search (rmem_chunk->mem_tree,
582                                  (GCompareFunc) g_mem_chunk_area_search,
583                                  mem);
584       
585       /* If the area has been marked, then it is being destroyed.
586        *  (ie marked to be destroyed).
587        * We check to see if all of the segments on the free list that
588        *  reference this area have been removed. This occurs when
589        *  the ammount of free memory is less than the allocatable size.
590        * If the chunk should be freed, then we place it in the "free_mem_area".
591        * This is so we make sure not to free the mem area here and then
592        *  allocate it again a few lines down.
593        * If we don't allocate a chunk a few lines down then the "free_mem_area"
594        *  will be freed.
595        * If there is already a "free_mem_area" then we'll just free this mem area.
596        */
597       if (temp_area->mark)
598         {
599           /* Update the "free" memory available in that area */
600           temp_area->free += rmem_chunk->atom_size;
601           
602           if (temp_area->free == rmem_chunk->area_size)
603             {
604               if (temp_area == rmem_chunk->mem_area)
605                 rmem_chunk->mem_area = NULL;
606               
607               if (rmem_chunk->free_mem_area)
608                 {
609                   rmem_chunk->num_mem_areas -= 1;
610                   
611                   if (temp_area->next)
612                     temp_area->next->prev = temp_area->prev;
613                   if (temp_area->prev)
614                     temp_area->prev->next = temp_area->next;
615                   if (temp_area == rmem_chunk->mem_areas)
616                     rmem_chunk->mem_areas = rmem_chunk->mem_areas->next;
617                   
618                   if (rmem_chunk->type == G_ALLOC_AND_FREE)
619                     g_tree_remove (rmem_chunk->mem_tree, temp_area);
620                   g_free (temp_area);
621                 }
622               else
623                 rmem_chunk->free_mem_area = temp_area;
624               
625               rmem_chunk->num_marked_areas -= 1;
626             }
627         }
628       else
629         {
630           /* Update the number of allocated atoms count.
631            */
632           temp_area->allocated += 1;
633           
634           /* The area wasn't marked...return the memory
635            */
636           goto outa_here;
637         }
638     }
639   
640   /* If there isn't a current mem area or the current mem area is out of space
641    *  then allocate a new mem area. We'll first check and see if we can use
642    *  the "free_mem_area". Otherwise we'll just malloc the mem area.
643    */
644   if ((!rmem_chunk->mem_area) ||
645       ((rmem_chunk->mem_area->index + rmem_chunk->atom_size) > rmem_chunk->area_size))
646     {
647       if (rmem_chunk->free_mem_area)
648         {
649           rmem_chunk->mem_area = rmem_chunk->free_mem_area;
650           rmem_chunk->free_mem_area = NULL;
651         }
652       else
653         {
654 #ifdef ENABLE_GC_FRIENDLY
655           rmem_chunk->mem_area = (GMemArea*) g_malloc0 (sizeof (GMemArea) -
656                                                         MEM_AREA_SIZE +
657                                                         rmem_chunk->area_size); 
658 #else /* !ENABLE_GC_FRIENDLY */
659           rmem_chunk->mem_area = (GMemArea*) g_malloc (sizeof (GMemArea) -
660                                                        MEM_AREA_SIZE +
661                                                        rmem_chunk->area_size);
662 #endif /* ENABLE_GC_FRIENDLY */
663           
664           rmem_chunk->num_mem_areas += 1;
665           rmem_chunk->mem_area->next = rmem_chunk->mem_areas;
666           rmem_chunk->mem_area->prev = NULL;
667           
668           if (rmem_chunk->mem_areas)
669             rmem_chunk->mem_areas->prev = rmem_chunk->mem_area;
670           rmem_chunk->mem_areas = rmem_chunk->mem_area;
671           
672           if (rmem_chunk->type == G_ALLOC_AND_FREE)
673             g_tree_insert (rmem_chunk->mem_tree, rmem_chunk->mem_area, rmem_chunk->mem_area);
674         }
675       
676       rmem_chunk->mem_area->index = 0;
677       rmem_chunk->mem_area->free = rmem_chunk->area_size;
678       rmem_chunk->mem_area->allocated = 0;
679       rmem_chunk->mem_area->mark = 0;
680     }
681   
682   /* Get the memory and modify the state variables appropriately.
683    */
684   mem = (gpointer) &rmem_chunk->mem_area->mem[rmem_chunk->mem_area->index];
685   rmem_chunk->mem_area->index += rmem_chunk->atom_size;
686   rmem_chunk->mem_area->free -= rmem_chunk->atom_size;
687   rmem_chunk->mem_area->allocated += 1;
688
689 outa_here:
690
691   LEAVE_MEM_CHUNK_ROUTINE();
692
693   return mem;
694 }
695
696 gpointer
697 g_mem_chunk_alloc0 (GMemChunk *mem_chunk)
698 {
699   gpointer mem;
700
701   mem = g_mem_chunk_alloc (mem_chunk);
702   if (mem)
703     {
704       GRealMemChunk *rmem_chunk = (GRealMemChunk*) mem_chunk;
705
706       memset (mem, 0, rmem_chunk->atom_size);
707     }
708
709   return mem;
710 }
711
712 void
713 g_mem_chunk_free (GMemChunk *mem_chunk,
714                   gpointer   mem)
715 {
716   GRealMemChunk *rmem_chunk;
717   GMemArea *temp_area;
718   GFreeAtom *free_atom;
719   
720   g_return_if_fail (mem_chunk != NULL);
721   g_return_if_fail (mem != NULL);
722
723   ENTER_MEM_CHUNK_ROUTINE();
724
725   rmem_chunk = (GRealMemChunk*) mem_chunk;
726   
727 #ifdef ENABLE_GC_FRIENDLY
728   memset (mem, 0, rmem_chunk->atom_size);
729 #endif /* ENABLE_GC_FRIENDLY */
730
731   /* Don't do anything if this is an ALLOC_ONLY chunk
732    */
733   if (rmem_chunk->type == G_ALLOC_AND_FREE)
734     {
735       /* Place the memory on the "free_atoms" list
736        */
737       free_atom = (GFreeAtom*) mem;
738       free_atom->next = rmem_chunk->free_atoms;
739       rmem_chunk->free_atoms = free_atom;
740       
741       temp_area = g_tree_search (rmem_chunk->mem_tree,
742                                  (GCompareFunc) g_mem_chunk_area_search,
743                                  mem);
744       
745       temp_area->allocated -= 1;
746       
747       if (temp_area->allocated == 0)
748         {
749           temp_area->mark = 1;
750           rmem_chunk->num_marked_areas += 1;
751         }
752     }
753
754   LEAVE_MEM_CHUNK_ROUTINE();
755 }
756
757 /* This doesn't free the free_area if there is one */
758 void
759 g_mem_chunk_clean (GMemChunk *mem_chunk)
760 {
761   GRealMemChunk *rmem_chunk;
762   GMemArea *mem_area;
763   GFreeAtom *prev_free_atom;
764   GFreeAtom *temp_free_atom;
765   gpointer mem;
766   
767   g_return_if_fail (mem_chunk != NULL);
768   
769   rmem_chunk = (GRealMemChunk*) mem_chunk;
770   
771   if (rmem_chunk->type == G_ALLOC_AND_FREE)
772     {
773       prev_free_atom = NULL;
774       temp_free_atom = rmem_chunk->free_atoms;
775       
776       while (temp_free_atom)
777         {
778           mem = (gpointer) temp_free_atom;
779           
780           mem_area = g_tree_search (rmem_chunk->mem_tree,
781                                     (GCompareFunc) g_mem_chunk_area_search,
782                                     mem);
783           
784           /* If this mem area is marked for destruction then delete the
785            *  area and list node and decrement the free mem.
786            */
787           if (mem_area->mark)
788             {
789               if (prev_free_atom)
790                 prev_free_atom->next = temp_free_atom->next;
791               else
792                 rmem_chunk->free_atoms = temp_free_atom->next;
793               temp_free_atom = temp_free_atom->next;
794               
795               mem_area->free += rmem_chunk->atom_size;
796               if (mem_area->free == rmem_chunk->area_size)
797                 {
798                   rmem_chunk->num_mem_areas -= 1;
799                   rmem_chunk->num_marked_areas -= 1;
800                   
801                   if (mem_area->next)
802                     mem_area->next->prev = mem_area->prev;
803                   if (mem_area->prev)
804                     mem_area->prev->next = mem_area->next;
805                   if (mem_area == rmem_chunk->mem_areas)
806                     rmem_chunk->mem_areas = rmem_chunk->mem_areas->next;
807                   if (mem_area == rmem_chunk->mem_area)
808                     rmem_chunk->mem_area = NULL;
809                   
810                   if (rmem_chunk->type == G_ALLOC_AND_FREE)
811                     g_tree_remove (rmem_chunk->mem_tree, mem_area);
812                   g_free (mem_area);
813                 }
814             }
815           else
816             {
817               prev_free_atom = temp_free_atom;
818               temp_free_atom = temp_free_atom->next;
819             }
820         }
821     }
822 }
823
824 void
825 g_mem_chunk_reset (GMemChunk *mem_chunk)
826 {
827   GRealMemChunk *rmem_chunk;
828   GMemArea *mem_areas;
829   GMemArea *temp_area;
830   
831   g_return_if_fail (mem_chunk != NULL);
832   
833   rmem_chunk = (GRealMemChunk*) mem_chunk;
834   
835   mem_areas = rmem_chunk->mem_areas;
836   rmem_chunk->num_mem_areas = 0;
837   rmem_chunk->mem_areas = NULL;
838   rmem_chunk->mem_area = NULL;
839   
840   while (mem_areas)
841     {
842       temp_area = mem_areas;
843       mem_areas = mem_areas->next;
844       g_free (temp_area);
845     }
846   
847   rmem_chunk->free_atoms = NULL;
848   
849   if (rmem_chunk->mem_tree)
850     g_tree_destroy (rmem_chunk->mem_tree);
851   rmem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare);
852 }
853
854 void
855 g_mem_chunk_print (GMemChunk *mem_chunk)
856 {
857   GRealMemChunk *rmem_chunk;
858   GMemArea *mem_areas;
859   gulong mem;
860   
861   g_return_if_fail (mem_chunk != NULL);
862   
863   rmem_chunk = (GRealMemChunk*) mem_chunk;
864   mem_areas = rmem_chunk->mem_areas;
865   mem = 0;
866   
867   while (mem_areas)
868     {
869       mem += rmem_chunk->area_size - mem_areas->free;
870       mem_areas = mem_areas->next;
871     }
872   
873   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
874          "%s: %ld bytes using %d mem areas",
875          rmem_chunk->name, mem, rmem_chunk->num_mem_areas);
876 }
877
878 void
879 g_mem_chunk_info (void)
880 {
881   GRealMemChunk *mem_chunk;
882   gint count;
883   
884   count = 0;
885   g_mutex_lock (mem_chunks_lock);
886   mem_chunk = mem_chunks;
887   while (mem_chunk)
888     {
889       count += 1;
890       mem_chunk = mem_chunk->next;
891     }
892   g_mutex_unlock (mem_chunks_lock);
893   
894   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%d mem chunks", count);
895   
896   g_mutex_lock (mem_chunks_lock);
897   mem_chunk = mem_chunks;
898   g_mutex_unlock (mem_chunks_lock);
899
900   while (mem_chunk)
901     {
902       g_mem_chunk_print ((GMemChunk*) mem_chunk);
903       mem_chunk = mem_chunk->next;
904     }  
905 }
906
907 void
908 g_blow_chunks (void)
909 {
910   GRealMemChunk *mem_chunk;
911   
912   g_mutex_lock (mem_chunks_lock);
913   mem_chunk = mem_chunks;
914   g_mutex_unlock (mem_chunks_lock);
915   while (mem_chunk)
916     {
917       g_mem_chunk_clean ((GMemChunk*) mem_chunk);
918       mem_chunk = mem_chunk->next;
919     }
920 }
921
922
923 static gulong
924 g_mem_chunk_compute_size (gulong size,
925                           gulong min_size)
926 {
927   gulong power_of_2;
928   gulong lower, upper;
929   
930   power_of_2 = 16;
931   while (power_of_2 < size)
932     power_of_2 <<= 1;
933   
934   lower = power_of_2 >> 1;
935   upper = power_of_2;
936   
937   if (size - lower < upper - size && lower >= min_size)
938     return lower;
939   else
940     return upper;
941 }
942
943 static gint
944 g_mem_chunk_area_compare (GMemArea *a,
945                           GMemArea *b)
946 {
947   if (a->mem > b->mem)
948     return 1;
949   else if (a->mem < b->mem)
950     return -1;
951   return 0;
952 }
953
954 static gint
955 g_mem_chunk_area_search (GMemArea *a,
956                          gchar    *addr)
957 {
958   if (a->mem <= addr)
959     {
960       if (addr < &a->mem[a->index])
961         return 0;
962       return 1;
963     }
964   return -1;
965 }
966 #else /* DISABLE_MEM_POOLS */
967
968 typedef struct
969 {
970   guint alloc_size;           /* the size of an atom */
971 }  GMinimalMemChunk;
972
973 GMemChunk*
974 g_mem_chunk_new (gchar  *name,
975                  gint    atom_size,
976                  gulong  area_size,
977                  gint    type)
978 {
979   GMinimalMemChunk *mem_chunk;
980
981   g_return_val_if_fail (atom_size > 0, NULL);
982
983   mem_chunk = g_new (GMinimalMemChunk, 1);
984   mem_chunk->alloc_size = atom_size;
985
986   return ((GMemChunk*) mem_chunk);
987 }
988
989 void
990 g_mem_chunk_destroy (GMemChunk *mem_chunk)
991 {
992   g_return_if_fail (mem_chunk != NULL);
993   
994   g_free (mem_chunk);
995 }
996
997 gpointer
998 g_mem_chunk_alloc (GMemChunk *mem_chunk)
999 {
1000   GMinimalMemChunk *minimal = (GMinimalMemChunk *)mem_chunk;
1001   
1002   g_return_val_if_fail (mem_chunk != NULL, NULL);
1003   
1004   return g_malloc (minimal->alloc_size);
1005 }
1006
1007 gpointer
1008 g_mem_chunk_alloc0 (GMemChunk *mem_chunk)
1009 {
1010   GMinimalMemChunk *minimal = (GMinimalMemChunk *)mem_chunk;
1011   
1012   g_return_val_if_fail (mem_chunk != NULL, NULL);
1013   
1014   return g_malloc0 (minimal->alloc_size);
1015 }
1016
1017 void
1018 g_mem_chunk_free (GMemChunk *mem_chunk,
1019                   gpointer   mem)
1020 {
1021   g_return_if_fail (mem_chunk != NULL);
1022   
1023   g_free (mem);
1024 }
1025
1026 void
1027 g_mem_chunk_clean (GMemChunk *mem_chunk)
1028 {
1029 }
1030
1031 void
1032 g_mem_chunk_reset (GMemChunk *mem_chunk)
1033 {
1034 }
1035
1036 void
1037 g_mem_chunk_print (GMemChunk *mem_chunk)
1038 {
1039 }
1040
1041 void
1042 g_mem_chunk_info (void)
1043 {
1044 }
1045
1046 void
1047 g_blow_chunks (void)
1048 {
1049 }
1050   
1051 #endif /* DISABLE_MEM_POOLS */
1052
1053 /* generic allocators
1054  */
1055 struct _GAllocator /* from gmem.c */
1056 {
1057   gchar         *name;
1058   guint16        n_preallocs;
1059   guint          is_unused : 1;
1060   guint          type : 4;
1061   GAllocator    *last;
1062   GMemChunk     *mem_chunk;
1063   gpointer       dummy; /* implementation specific */
1064 };
1065
1066 GAllocator*
1067 g_allocator_new (const gchar *name,
1068                  guint        n_preallocs)
1069 {
1070   GAllocator *allocator;
1071
1072   g_return_val_if_fail (name != NULL, NULL);
1073
1074   allocator = g_new0 (GAllocator, 1);
1075   allocator->name = g_strdup (name);
1076   allocator->n_preallocs = CLAMP (n_preallocs, 1, 65535);
1077   allocator->is_unused = TRUE;
1078   allocator->type = 0;
1079   allocator->last = NULL;
1080   allocator->mem_chunk = NULL;
1081   allocator->dummy = NULL;
1082
1083   return allocator;
1084 }
1085
1086 void
1087 g_allocator_free (GAllocator *allocator)
1088 {
1089   g_return_if_fail (allocator != NULL);
1090   g_return_if_fail (allocator->is_unused == TRUE);
1091
1092   g_free (allocator->name);
1093   if (allocator->mem_chunk)
1094     g_mem_chunk_destroy (allocator->mem_chunk);
1095
1096   g_free (allocator);
1097 }
1098
1099 void
1100 g_mem_init (void)
1101 {
1102 #ifndef DISABLE_MEM_POOLS
1103   mem_chunks_lock = g_mutex_new();
1104 #endif
1105 #if ENABLE_MEM_PROFILE
1106   mem_profile_lock = g_mutex_new();
1107   allocating_for_mem_chunk = g_private_new(NULL);
1108 #endif
1109 }