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