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