Home | History | Annotate | Download | only in tests
      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 #include "config.h"
     32 
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <signal.h>
     36 
     37 #include "glib.h"
     38 
     39 /* notes on macros:
     40  * if ENABLE_GC_FRIENDLY is defined, freed memory should be 0-wiped.
     41  */
     42 
     43 #define MEM_PROFILE_TABLE_SIZE 4096
     44 
     45 #define MEM_AREA_SIZE 4L
     46 
     47 static guint mem_chunk_recursion = 0;
     48 #  define MEM_CHUNK_ROUTINE_COUNT()	(mem_chunk_recursion)
     49 #  define ENTER_MEM_CHUNK_ROUTINE()	(mem_chunk_recursion = MEM_CHUNK_ROUTINE_COUNT () + 1)
     50 #  define LEAVE_MEM_CHUNK_ROUTINE()	(mem_chunk_recursion = MEM_CHUNK_ROUTINE_COUNT () - 1)
     51 
     52 /* --- old memchunk prototypes --- */
     53 void            old_mem_chunks_init     (void);
     54 GMemChunk*      old_mem_chunk_new       (const gchar  *name,
     55                                          gint          atom_size,
     56                                          gulong        area_size,
     57                                          gint          type);
     58 void            old_mem_chunk_destroy   (GMemChunk *mem_chunk);
     59 gpointer        old_mem_chunk_alloc     (GMemChunk *mem_chunk);
     60 gpointer        old_mem_chunk_alloc0    (GMemChunk *mem_chunk);
     61 void            old_mem_chunk_free      (GMemChunk *mem_chunk,
     62                                          gpointer   mem);
     63 void            old_mem_chunk_clean     (GMemChunk *mem_chunk);
     64 void            old_mem_chunk_reset     (GMemChunk *mem_chunk);
     65 void            old_mem_chunk_print     (GMemChunk *mem_chunk);
     66 void            old_mem_chunk_info      (void);
     67 
     68 
     69 /* --- MemChunks --- */
     70 #ifndef G_ALLOC_AND_FREE
     71 typedef struct _GAllocator GAllocator;
     72 typedef struct _GMemChunk  GMemChunk;
     73 #define G_ALLOC_ONLY	  1
     74 #define G_ALLOC_AND_FREE  2
     75 #endif
     76 
     77 typedef struct _GFreeAtom      GFreeAtom;
     78 typedef struct _GMemArea       GMemArea;
     79 
     80 struct _GFreeAtom
     81 {
     82   GFreeAtom *next;
     83 };
     84 
     85 struct _GMemArea
     86 {
     87   GMemArea *next;            /* the next mem area */
     88   GMemArea *prev;            /* the previous mem area */
     89   gulong index;              /* the current index into the "mem" array */
     90   gulong free;               /* the number of free bytes in this mem area */
     91   gulong allocated;          /* the number of atoms allocated from this area */
     92   gulong mark;               /* is this mem area marked for deletion */
     93   gchar mem[MEM_AREA_SIZE];  /* the mem array from which atoms get allocated
     94 			      * the actual size of this array is determined by
     95 			      *  the mem chunk "area_size". ANSI says that it
     96 			      *  must be declared to be the maximum size it
     97 			      *  can possibly be (even though the actual size
     98 			      *  may be less).
     99 			      */
    100 };
    101 
    102 struct _GMemChunk
    103 {
    104   const gchar *name;         /* name of this MemChunk...used for debugging output */
    105   gint type;                 /* the type of MemChunk: ALLOC_ONLY or ALLOC_AND_FREE */
    106   gint num_mem_areas;        /* the number of memory areas */
    107   gint num_marked_areas;     /* the number of areas marked for deletion */
    108   guint atom_size;           /* the size of an atom */
    109   gulong area_size;          /* the size of a memory area */
    110   GMemArea *mem_area;        /* the current memory area */
    111   GMemArea *mem_areas;       /* a list of all the mem areas owned by this chunk */
    112   GMemArea *free_mem_area;   /* the free area...which is about to be destroyed */
    113   GFreeAtom *free_atoms;     /* the free atoms list */
    114   GTree *mem_tree;           /* tree of mem areas sorted by memory address */
    115   GMemChunk *next;           /* pointer to the next chunk */
    116   GMemChunk *prev;           /* pointer to the previous chunk */
    117 };
    118 
    119 
    120 static gulong old_mem_chunk_compute_size (gulong    size,
    121                                           gulong    min_size) G_GNUC_CONST;
    122 static gint   old_mem_chunk_area_compare (GMemArea *a,
    123                                           GMemArea *b);
    124 static gint   old_mem_chunk_area_search  (GMemArea *a,
    125                                           gchar    *addr);
    126 
    127 /* here we can't use StaticMutexes, as they depend upon a working
    128  * g_malloc, the same holds true for StaticPrivate
    129  */
    130 static GMutex        *mem_chunks_lock = NULL;
    131 static GMemChunk     *mem_chunks = NULL;
    132 
    133 void
    134 old_mem_chunks_init (void)
    135 {
    136   mem_chunks_lock = g_mutex_new ();
    137 }
    138 
    139 GMemChunk*
    140 old_mem_chunk_new (const gchar  *name,
    141                    gint          atom_size,
    142                    gulong        area_size,
    143                    gint          type)
    144 {
    145   GMemChunk *mem_chunk;
    146   gulong rarea_size;
    147 
    148   g_return_val_if_fail (atom_size > 0, NULL);
    149   g_return_val_if_fail (area_size >= atom_size, NULL);
    150 
    151   ENTER_MEM_CHUNK_ROUTINE ();
    152 
    153   area_size = (area_size + atom_size - 1) / atom_size;
    154   area_size *= atom_size;
    155 
    156   mem_chunk = g_new (GMemChunk, 1);
    157   mem_chunk->name = name;
    158   mem_chunk->type = type;
    159   mem_chunk->num_mem_areas = 0;
    160   mem_chunk->num_marked_areas = 0;
    161   mem_chunk->mem_area = NULL;
    162   mem_chunk->free_mem_area = NULL;
    163   mem_chunk->free_atoms = NULL;
    164   mem_chunk->mem_tree = NULL;
    165   mem_chunk->mem_areas = NULL;
    166   mem_chunk->atom_size = atom_size;
    167 
    168   if (mem_chunk->type == G_ALLOC_AND_FREE)
    169     mem_chunk->mem_tree = g_tree_new ((GCompareFunc) old_mem_chunk_area_compare);
    170 
    171   if (mem_chunk->atom_size % G_MEM_ALIGN)
    172     mem_chunk->atom_size += G_MEM_ALIGN - (mem_chunk->atom_size % G_MEM_ALIGN);
    173 
    174   rarea_size = area_size + sizeof (GMemArea) - MEM_AREA_SIZE;
    175   rarea_size = old_mem_chunk_compute_size (rarea_size, atom_size + sizeof (GMemArea) - MEM_AREA_SIZE);
    176   mem_chunk->area_size = rarea_size - (sizeof (GMemArea) - MEM_AREA_SIZE);
    177 
    178   g_mutex_lock (mem_chunks_lock);
    179   mem_chunk->next = mem_chunks;
    180   mem_chunk->prev = NULL;
    181   if (mem_chunks)
    182     mem_chunks->prev = mem_chunk;
    183   mem_chunks = mem_chunk;
    184   g_mutex_unlock (mem_chunks_lock);
    185 
    186   LEAVE_MEM_CHUNK_ROUTINE ();
    187 
    188   return mem_chunk;
    189 }
    190 
    191 void
    192 old_mem_chunk_destroy (GMemChunk *mem_chunk)
    193 {
    194   GMemArea *mem_areas;
    195   GMemArea *temp_area;
    196 
    197   g_return_if_fail (mem_chunk != NULL);
    198 
    199   ENTER_MEM_CHUNK_ROUTINE ();
    200 
    201   mem_areas = mem_chunk->mem_areas;
    202   while (mem_areas)
    203     {
    204       temp_area = mem_areas;
    205       mem_areas = mem_areas->next;
    206       g_free (temp_area);
    207     }
    208 
    209   g_mutex_lock (mem_chunks_lock);
    210   if (mem_chunk->next)
    211     mem_chunk->next->prev = mem_chunk->prev;
    212   if (mem_chunk->prev)
    213     mem_chunk->prev->next = mem_chunk->next;
    214 
    215   if (mem_chunk == mem_chunks)
    216     mem_chunks = mem_chunks->next;
    217   g_mutex_unlock (mem_chunks_lock);
    218 
    219   if (mem_chunk->type == G_ALLOC_AND_FREE)
    220     g_tree_destroy (mem_chunk->mem_tree);
    221 
    222   g_free (mem_chunk);
    223 
    224   LEAVE_MEM_CHUNK_ROUTINE ();
    225 }
    226 
    227 gpointer
    228 old_mem_chunk_alloc (GMemChunk *mem_chunk)
    229 {
    230   GMemArea *temp_area;
    231   gpointer mem;
    232 
    233   ENTER_MEM_CHUNK_ROUTINE ();
    234 
    235   g_return_val_if_fail (mem_chunk != NULL, NULL);
    236 
    237   while (mem_chunk->free_atoms)
    238     {
    239       /* Get the first piece of memory on the "free_atoms" list.
    240        * We can go ahead and destroy the list node we used to keep
    241        *  track of it with and to update the "free_atoms" list to
    242        *  point to its next element.
    243        */
    244       mem = mem_chunk->free_atoms;
    245       mem_chunk->free_atoms = mem_chunk->free_atoms->next;
    246 
    247       /* Determine which area this piece of memory is allocated from */
    248       temp_area = g_tree_search (mem_chunk->mem_tree,
    249 				 (GCompareFunc) old_mem_chunk_area_search,
    250 				 mem);
    251 
    252       /* If the area has been marked, then it is being destroyed.
    253        *  (ie marked to be destroyed).
    254        * We check to see if all of the segments on the free list that
    255        *  reference this area have been removed. This occurs when
    256        *  the ammount of free memory is less than the allocatable size.
    257        * If the chunk should be freed, then we place it in the "free_mem_area".
    258        * This is so we make sure not to free the mem area here and then
    259        *  allocate it again a few lines down.
    260        * If we don't allocate a chunk a few lines down then the "free_mem_area"
    261        *  will be freed.
    262        * If there is already a "free_mem_area" then we'll just free this mem area.
    263        */
    264       if (temp_area->mark)
    265         {
    266           /* Update the "free" memory available in that area */
    267           temp_area->free += mem_chunk->atom_size;
    268 
    269           if (temp_area->free == mem_chunk->area_size)
    270             {
    271               if (temp_area == mem_chunk->mem_area)
    272                 mem_chunk->mem_area = NULL;
    273 
    274               if (mem_chunk->free_mem_area)
    275                 {
    276                   mem_chunk->num_mem_areas -= 1;
    277 
    278                   if (temp_area->next)
    279                     temp_area->next->prev = temp_area->prev;
    280                   if (temp_area->prev)
    281                     temp_area->prev->next = temp_area->next;
    282                   if (temp_area == mem_chunk->mem_areas)
    283                     mem_chunk->mem_areas = mem_chunk->mem_areas->next;
    284 
    285 		  if (mem_chunk->type == G_ALLOC_AND_FREE)
    286 		    g_tree_remove (mem_chunk->mem_tree, temp_area);
    287                   g_free (temp_area);
    288                 }
    289               else
    290                 mem_chunk->free_mem_area = temp_area;
    291 
    292 	      mem_chunk->num_marked_areas -= 1;
    293 	    }
    294 	}
    295       else
    296         {
    297           /* Update the number of allocated atoms count.
    298 	   */
    299           temp_area->allocated += 1;
    300 
    301           /* The area wasn't marked...return the memory
    302 	   */
    303 	  goto outa_here;
    304         }
    305     }
    306 
    307   /* If there isn't a current mem area or the current mem area is out of space
    308    *  then allocate a new mem area. We'll first check and see if we can use
    309    *  the "free_mem_area". Otherwise we'll just malloc the mem area.
    310    */
    311   if ((!mem_chunk->mem_area) ||
    312       ((mem_chunk->mem_area->index + mem_chunk->atom_size) > mem_chunk->area_size))
    313     {
    314       if (mem_chunk->free_mem_area)
    315         {
    316           mem_chunk->mem_area = mem_chunk->free_mem_area;
    317 	  mem_chunk->free_mem_area = NULL;
    318         }
    319       else
    320         {
    321 #ifdef ENABLE_GC_FRIENDLY
    322 	  mem_chunk->mem_area = (GMemArea*) g_malloc0 (sizeof (GMemArea) -
    323 						       MEM_AREA_SIZE +
    324 						       mem_chunk->area_size);
    325 #else /* !ENABLE_GC_FRIENDLY */
    326 	  mem_chunk->mem_area = (GMemArea*) g_malloc (sizeof (GMemArea) -
    327 						      MEM_AREA_SIZE +
    328 						      mem_chunk->area_size);
    329 #endif /* ENABLE_GC_FRIENDLY */
    330 
    331 	  mem_chunk->num_mem_areas += 1;
    332 	  mem_chunk->mem_area->next = mem_chunk->mem_areas;
    333 	  mem_chunk->mem_area->prev = NULL;
    334 
    335 	  if (mem_chunk->mem_areas)
    336 	    mem_chunk->mem_areas->prev = mem_chunk->mem_area;
    337 	  mem_chunk->mem_areas = mem_chunk->mem_area;
    338 
    339 	  if (mem_chunk->type == G_ALLOC_AND_FREE)
    340 	    g_tree_insert (mem_chunk->mem_tree, mem_chunk->mem_area, mem_chunk->mem_area);
    341         }
    342 
    343       mem_chunk->mem_area->index = 0;
    344       mem_chunk->mem_area->free = mem_chunk->area_size;
    345       mem_chunk->mem_area->allocated = 0;
    346       mem_chunk->mem_area->mark = 0;
    347     }
    348 
    349   /* Get the memory and modify the state variables appropriately.
    350    */
    351   mem = (gpointer) &mem_chunk->mem_area->mem[mem_chunk->mem_area->index];
    352   mem_chunk->mem_area->index += mem_chunk->atom_size;
    353   mem_chunk->mem_area->free -= mem_chunk->atom_size;
    354   mem_chunk->mem_area->allocated += 1;
    355 
    356  outa_here:
    357 
    358   LEAVE_MEM_CHUNK_ROUTINE ();
    359 
    360   return mem;
    361 }
    362 
    363 gpointer
    364 old_mem_chunk_alloc0 (GMemChunk *mem_chunk)
    365 {
    366   gpointer mem;
    367 
    368   mem = old_mem_chunk_alloc (mem_chunk);
    369   if (mem)
    370     {
    371       memset (mem, 0, mem_chunk->atom_size);
    372     }
    373 
    374   return mem;
    375 }
    376 
    377 void
    378 old_mem_chunk_free (GMemChunk *mem_chunk,
    379                     gpointer   mem)
    380 {
    381   GMemArea *temp_area;
    382   GFreeAtom *free_atom;
    383 
    384   g_return_if_fail (mem_chunk != NULL);
    385   g_return_if_fail (mem != NULL);
    386 
    387   ENTER_MEM_CHUNK_ROUTINE ();
    388 
    389 #ifdef ENABLE_GC_FRIENDLY
    390   memset (mem, 0, mem_chunk->atom_size);
    391 #endif /* ENABLE_GC_FRIENDLY */
    392 
    393   /* Don't do anything if this is an ALLOC_ONLY chunk
    394    */
    395   if (mem_chunk->type == G_ALLOC_AND_FREE)
    396     {
    397       /* Place the memory on the "free_atoms" list
    398        */
    399       free_atom = (GFreeAtom*) mem;
    400       free_atom->next = mem_chunk->free_atoms;
    401       mem_chunk->free_atoms = free_atom;
    402 
    403       temp_area = g_tree_search (mem_chunk->mem_tree,
    404 				 (GCompareFunc) old_mem_chunk_area_search,
    405 				 mem);
    406 
    407       temp_area->allocated -= 1;
    408 
    409       if (temp_area->allocated == 0)
    410 	{
    411 	  temp_area->mark = 1;
    412 	  mem_chunk->num_marked_areas += 1;
    413 	}
    414     }
    415 
    416   LEAVE_MEM_CHUNK_ROUTINE ();
    417 }
    418 
    419 /* This doesn't free the free_area if there is one */
    420 void
    421 old_mem_chunk_clean (GMemChunk *mem_chunk)
    422 {
    423   GMemArea *mem_area;
    424   GFreeAtom *prev_free_atom;
    425   GFreeAtom *temp_free_atom;
    426   gpointer mem;
    427 
    428   g_return_if_fail (mem_chunk != NULL);
    429 
    430   ENTER_MEM_CHUNK_ROUTINE ();
    431 
    432   if (mem_chunk->type == G_ALLOC_AND_FREE)
    433     {
    434       prev_free_atom = NULL;
    435       temp_free_atom = mem_chunk->free_atoms;
    436 
    437       while (temp_free_atom)
    438 	{
    439 	  mem = (gpointer) temp_free_atom;
    440 
    441 	  mem_area = g_tree_search (mem_chunk->mem_tree,
    442 				    (GCompareFunc) old_mem_chunk_area_search,
    443 				    mem);
    444 
    445           /* If this mem area is marked for destruction then delete the
    446 	   *  area and list node and decrement the free mem.
    447            */
    448 	  if (mem_area->mark)
    449 	    {
    450 	      if (prev_free_atom)
    451 		prev_free_atom->next = temp_free_atom->next;
    452 	      else
    453 		mem_chunk->free_atoms = temp_free_atom->next;
    454 	      temp_free_atom = temp_free_atom->next;
    455 
    456 	      mem_area->free += mem_chunk->atom_size;
    457 	      if (mem_area->free == mem_chunk->area_size)
    458 		{
    459 		  mem_chunk->num_mem_areas -= 1;
    460 		  mem_chunk->num_marked_areas -= 1;
    461 
    462 		  if (mem_area->next)
    463 		    mem_area->next->prev = mem_area->prev;
    464 		  if (mem_area->prev)
    465 		    mem_area->prev->next = mem_area->next;
    466 		  if (mem_area == mem_chunk->mem_areas)
    467 		    mem_chunk->mem_areas = mem_chunk->mem_areas->next;
    468 		  if (mem_area == mem_chunk->mem_area)
    469 		    mem_chunk->mem_area = NULL;
    470 
    471 		  if (mem_chunk->type == G_ALLOC_AND_FREE)
    472 		    g_tree_remove (mem_chunk->mem_tree, mem_area);
    473 		  g_free (mem_area);
    474 		}
    475 	    }
    476 	  else
    477 	    {
    478 	      prev_free_atom = temp_free_atom;
    479 	      temp_free_atom = temp_free_atom->next;
    480 	    }
    481 	}
    482     }
    483   LEAVE_MEM_CHUNK_ROUTINE ();
    484 }
    485 
    486 void
    487 old_mem_chunk_reset (GMemChunk *mem_chunk)
    488 {
    489   GMemArea *mem_areas;
    490   GMemArea *temp_area;
    491 
    492   g_return_if_fail (mem_chunk != NULL);
    493 
    494   ENTER_MEM_CHUNK_ROUTINE ();
    495 
    496   mem_areas = mem_chunk->mem_areas;
    497   mem_chunk->num_mem_areas = 0;
    498   mem_chunk->mem_areas = NULL;
    499   mem_chunk->mem_area = NULL;
    500 
    501   while (mem_areas)
    502     {
    503       temp_area = mem_areas;
    504       mem_areas = mem_areas->next;
    505       g_free (temp_area);
    506     }
    507 
    508   mem_chunk->free_atoms = NULL;
    509 
    510   if (mem_chunk->mem_tree)
    511     {
    512       g_tree_destroy (mem_chunk->mem_tree);
    513       mem_chunk->mem_tree = g_tree_new ((GCompareFunc) old_mem_chunk_area_compare);
    514     }
    515 
    516   LEAVE_MEM_CHUNK_ROUTINE ();
    517 }
    518 
    519 void
    520 old_mem_chunk_print (GMemChunk *mem_chunk)
    521 {
    522   GMemArea *mem_areas;
    523   gulong mem;
    524 
    525   g_return_if_fail (mem_chunk != NULL);
    526 
    527   mem_areas = mem_chunk->mem_areas;
    528   mem = 0;
    529 
    530   while (mem_areas)
    531     {
    532       mem += mem_chunk->area_size - mem_areas->free;
    533       mem_areas = mem_areas->next;
    534     }
    535 
    536   g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
    537 	 "%s: %ld bytes using %d mem areas",
    538 	 mem_chunk->name, mem, mem_chunk->num_mem_areas);
    539 }
    540 
    541 void
    542 old_mem_chunk_info (void)
    543 {
    544   GMemChunk *mem_chunk;
    545   gint count;
    546 
    547   count = 0;
    548   g_mutex_lock (mem_chunks_lock);
    549   mem_chunk = mem_chunks;
    550   while (mem_chunk)
    551     {
    552       count += 1;
    553       mem_chunk = mem_chunk->next;
    554     }
    555   g_mutex_unlock (mem_chunks_lock);
    556 
    557   g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "%d mem chunks", count);
    558 
    559   g_mutex_lock (mem_chunks_lock);
    560   mem_chunk = mem_chunks;
    561   g_mutex_unlock (mem_chunks_lock);
    562 
    563   while (mem_chunk)
    564     {
    565       old_mem_chunk_print ((GMemChunk*) mem_chunk);
    566       mem_chunk = mem_chunk->next;
    567     }
    568 }
    569 
    570 static gulong
    571 old_mem_chunk_compute_size (gulong size,
    572                             gulong min_size)
    573 {
    574   gulong power_of_2;
    575   gulong lower, upper;
    576 
    577   power_of_2 = 16;
    578   while (power_of_2 < size)
    579     power_of_2 <<= 1;
    580 
    581   lower = power_of_2 >> 1;
    582   upper = power_of_2;
    583 
    584   if (size - lower < upper - size && lower >= min_size)
    585     return lower;
    586   else
    587     return upper;
    588 }
    589 
    590 static gint
    591 old_mem_chunk_area_compare (GMemArea *a,
    592                             GMemArea *b)
    593 {
    594   if (a->mem > b->mem)
    595     return 1;
    596   else if (a->mem < b->mem)
    597     return -1;
    598   return 0;
    599 }
    600 
    601 static gint
    602 old_mem_chunk_area_search (GMemArea *a,
    603                            gchar    *addr)
    604 {
    605   if (a->mem <= addr)
    606     {
    607       if (addr < &a->mem[a->index])
    608 	return 0;
    609       return 1;
    610     }
    611   return -1;
    612 }
    613