1 /** 2 * This module contains the garbage collector implementation. 3 * 4 * Copyright: Copyright (C) 2001-2007 Digital Mars, www.digitalmars.com. 5 * All rights reserved. 6 * License: 7 * This software is provided 'as-is', without any express or implied 8 * warranty. In no event will the authors be held liable for any damages 9 * arising from the use of this software. 10 * 11 * Permission is granted to anyone to use this software for any purpose, 12 * including commercial applications, and to alter it and redistribute it 13 * freely, in both source and binary form, subject to the following 14 * restrictions: 15 * 16 * o The origin of this software must not be misrepresented; you must not 17 * claim that you wrote the original software. If you use this software 18 * in a product, an acknowledgment in the product documentation would be 19 * appreciated but is not required. 20 * o Altered source versions must be plainly marked as such, and must not 21 * be misrepresented as being the original software. 22 * o This notice may not be removed or altered from any source 23 * distribution. 24 * Authors: Walter Bright, David Friedman, Sean Kelly 25 */ 26 27 module rt.gc.cdgc.gc; 28 29 // D Programming Language Garbage Collector implementation 30 31 /************** Debugging ***************************/ 32 33 //debug = COLLECT_PRINTF; // turn on printf's 34 //debug = PTRCHECK; // more pointer checking 35 //debug = PTRCHECK2; // thorough but slow pointer checking 36 37 /*************** Configuration *********************/ 38 39 version = STACKGROWSDOWN; // growing the stack means subtracting from the stack pointer 40 // (use for Intel X86 CPUs) 41 // else growing the stack means adding to the stack pointer 42 43 /***************************************************/ 44 45 import rt.gc.cdgc.bits: GCBits; 46 import rt.gc.cdgc.stats: GCStats, Stats; 47 import dynarray = rt.gc.cdgc.dynarray; 48 import os = rt.gc.cdgc.os; 49 import opts = rt.gc.cdgc.opts; 50 51 import cstdlib = tango.stdc.stdlib; 52 import cstring = tango.stdc.string; 53 import cstdio = tango.stdc.stdio; 54 debug(COLLECT_PRINTF) alias cstdio.printf printf; 55 56 /* 57 * This is a small optimization that proved it's usefulness. For small chunks 58 * or memory memset() seems to be slower (probably because of the call) that 59 * simply doing a simple loop to set the memory. 60 */ 61 void memset(void* dst, int c, size_t n) 62 { 63 // This number (32) has been determined empirically 64 if (n > 32) { 65 cstring.memset(dst, c, n); 66 return; 67 } 68 auto p = cast(ubyte*)(dst); 69 while (n-- > 0) 70 *p++ = c; 71 } 72 73 version (GNU) 74 { 75 // BUG: The following import will likely not work, since the gcc 76 // subdirectory is elsewhere. Instead, perhaps the functions 77 // could be declared directly or some other resolution could 78 // be found. 79 static import gcc.builtins; // for __builtin_unwind_int 80 } 81 82 struct BlkInfo 83 { 84 void* base; 85 size_t size; 86 uint attr; 87 } 88 89 package enum BlkAttr : uint 90 { 91 FINALIZE = 0b0000_0001, 92 NO_SCAN = 0b0000_0010, 93 NO_MOVE = 0b0000_0100, 94 ALL_BITS = 0b1111_1111 95 } 96 97 package bool has_pointermap(uint attrs) 98 { 99 return !opts.options.conservative && !(attrs & BlkAttr.NO_SCAN); 100 } 101 102 private size_t round_up(size_t n, size_t to) 103 { 104 return (n + to - 1) / to; 105 } 106 107 private 108 { 109 alias void delegate(Object) DEvent; 110 alias void delegate( void*, void* ) scanFn; 111 enum { OPFAIL = ~cast(size_t)0 } 112 113 extern (C) 114 { 115 version (DigitalMars) version(OSX) 116 void _d_osx_image_init(); 117 118 void* rt_stackBottom(); 119 void* rt_stackTop(); 120 void rt_finalize( void* p, bool det = true ); 121 void rt_attachDisposeEvent(Object h, DEvent e); 122 bool rt_detachDisposeEventNoLock(Object h, DEvent e); 123 void rt_scanStaticData( scanFn scan ); 124 125 void thread_init(); 126 bool thread_needLock(); 127 void thread_suspendAll(); 128 void thread_resumeAll(); 129 void thread_scanAll( scanFn fn, void* curStackTop = null ); 130 131 void onOutOfMemoryError(); 132 } 133 } 134 135 136 enum 137 { 138 PAGESIZE = 4096, 139 POOLSIZE = (4096*256), 140 } 141 142 143 enum 144 { 145 B_16, 146 B_32, 147 B_64, 148 B_128, 149 B_256, 150 B_512, 151 B_1024, 152 B_2048, 153 B_PAGE, // start of large alloc 154 B_PAGEPLUS, // continuation of large alloc 155 B_FREE, // free page 156 B_MAX 157 } 158 159 160 alias ubyte Bins; 161 162 163 struct List 164 { 165 List* next; 166 Pool* pool; 167 } 168 169 170 struct Range 171 { 172 void *pbot; 173 void *ptop; 174 int opCmp(in Range other) 175 { 176 if (pbot < other.pbot) 177 return -1; 178 else 179 return cast(int)(pbot > other.pbot); 180 } 181 int opEquals(in Range other) 182 { 183 return pbot is other.pbot; 184 } 185 } 186 187 188 const uint binsize[B_MAX] = [ 16,32,64,128,256,512,1024,2048,4096 ]; 189 const uint notbinsize[B_MAX] = [ ~(16u-1),~(32u-1),~(64u-1),~(128u-1),~(256u-1), 190 ~(512u-1),~(1024u-1),~(2048u-1),~(4096u-1) ]; 191 192 193 /* ============================ GC =============================== */ 194 195 196 class GCLock {} // just a dummy so we can get a global lock 197 198 199 struct GC 200 { 201 // global lock 202 ClassInfo lock; 203 204 void* p_cache; 205 size_t size_cache; 206 207 // !=0 means don't scan stack 208 uint no_stack; 209 bool any_changes; 210 void* stack_bottom; 211 uint inited; 212 /// Turn off collections if > 0 213 int disabled; 214 215 // PID of the fork()ed process doing the mark() (0 if is not running) 216 int mark_proc_pid; 217 218 /// min(pool.baseAddr) 219 byte *min_addr; 220 /// max(pool.topAddr) 221 byte *max_addr; 222 223 /// Total heap memory 224 size_t total_mem; 225 /// Free heap memory 226 size_t free_mem; 227 228 /// Free list for each size 229 List*[B_MAX] free_list; 230 231 dynarray.DynArray!(void*) roots; 232 dynarray.DynArray!(Range) ranges; 233 dynarray.DynArray!(Pool*) pools; 234 235 Stats stats; 236 237 // Monitoring callbacks 238 void delegate() collect_begin_cb; 239 void delegate(int freed, int pagebytes) collect_end_cb; 240 } 241 242 // call locked if necessary 243 private T locked(T, alias Code)() 244 { 245 if (thread_needLock()) 246 synchronized (gc.lock) return Code(); 247 else 248 return Code(); 249 } 250 251 private GC* gc; 252 253 254 bool collect_in_progress() 255 { 256 return gc.mark_proc_pid != 0; 257 } 258 259 260 bool Invariant() 261 { 262 assert (gc !is null); 263 if (gc.inited) { 264 size_t total_mem = 0; 265 size_t free_mem = 0; 266 for (size_t i = 0; i < gc.pools.length; i++) { 267 Pool* pool = gc.pools[i]; 268 pool.Invariant(); 269 if (i == 0) 270 assert(gc.min_addr == pool.baseAddr); 271 if (i + 1 < gc.pools.length) 272 assert(*pool < *gc.pools[i + 1]); 273 else if (i + 1 == gc.pools.length) 274 assert(gc.max_addr == pool.topAddr); 275 total_mem += pool.npages * PAGESIZE; 276 for (size_t pn = 0; pn < pool.npages; ++pn) 277 if (pool.pagetable[pn] == B_FREE) 278 free_mem += PAGESIZE; 279 } 280 281 gc.roots.Invariant(); 282 gc.ranges.Invariant(); 283 284 for (size_t i = 0; i < gc.ranges.length; i++) { 285 assert(gc.ranges[i].pbot); 286 assert(gc.ranges[i].ptop); 287 assert(gc.ranges[i].pbot <= gc.ranges[i].ptop); 288 } 289 290 for (size_t i = 0; i < B_PAGE; i++) { 291 for (List *list = gc.free_list[i]; list; list = list.next) { 292 auto pool = list.pool; 293 assert (pool !is null); 294 auto p = cast(byte*) list; 295 assert (p >= pool.baseAddr); 296 assert (p < pool.topAddr); 297 assert (pool.freebits.test((p - pool.baseAddr) / 16)); 298 free_mem += binsize[i]; 299 } 300 } 301 assert (gc.total_mem == total_mem); 302 assert (gc.free_mem == free_mem); 303 } 304 return true; 305 } 306 307 308 /** 309 * Find Pool that pointer is in. 310 * Return null if not in a Pool. 311 * Assume pools is sorted. 312 */ 313 Pool* findPool(void* p) 314 { 315 if (p < gc.min_addr || p >= gc.max_addr) 316 return null; 317 if (gc.pools.length == 0) 318 return null; 319 if (gc.pools.length == 1) 320 return gc.pools[0]; 321 /// The pooltable[] is sorted by address, so do a binary search 322 size_t low = 0; 323 size_t high = gc.pools.length - 1; 324 while (low <= high) { 325 size_t mid = (low + high) / 2; 326 auto pool = gc.pools[mid]; 327 if (p < pool.baseAddr) 328 high = mid - 1; 329 else if (p >= pool.topAddr) 330 low = mid + 1; 331 else 332 return pool; 333 } 334 // Not found 335 return null; 336 } 337 338 339 /** 340 * Determine the base address of the block containing p. If p is not a gc 341 * allocated pointer, return null. 342 */ 343 BlkInfo getInfo(void* p) 344 { 345 assert (p !is null); 346 Pool* pool = findPool(p); 347 if (pool is null) 348 return BlkInfo.init; 349 BlkInfo info; 350 info.base = pool.findBase(p); 351 if (info.base is null) 352 return BlkInfo.init; 353 info.size = pool.findSize(info.base); 354 size_t bit_i = (info.base - pool.baseAddr) / 16; 355 info.attr = getAttr(pool, bit_i); 356 if (has_pointermap(info.attr)) { 357 info.size -= size_t.sizeof; // PointerMap bitmask 358 // Points to the PointerMap bitmask pointer, not user data 359 if (p >= (info.base + info.size)) { 360 return BlkInfo.init; 361 } 362 } 363 if (opts.options.sentinel) { 364 info.base = sentinel_add(info.base); 365 // points to sentinel data, not user data 366 if (p < info.base || p >= sentinel_post(info.base)) 367 return BlkInfo.init; 368 info.size -= SENTINEL_EXTRA; 369 } 370 return info; 371 } 372 373 374 /** 375 * Compute bin for size. 376 */ 377 Bins findBin(size_t size) 378 { 379 Bins bin; 380 if (size <= 256) 381 { 382 if (size <= 64) 383 { 384 if (size <= 16) 385 bin = B_16; 386 else if (size <= 32) 387 bin = B_32; 388 else 389 bin = B_64; 390 } 391 else 392 { 393 if (size <= 128) 394 bin = B_128; 395 else 396 bin = B_256; 397 } 398 } 399 else 400 { 401 if (size <= 1024) 402 { 403 if (size <= 512) 404 bin = B_512; 405 else 406 bin = B_1024; 407 } 408 else 409 { 410 if (size <= 2048) 411 bin = B_2048; 412 else 413 bin = B_PAGE; 414 } 415 } 416 return bin; 417 } 418 419 420 /** 421 * Allocate a new pool of at least size bytes. 422 * Sort it into pools. 423 * Mark all memory in the pool as B_FREE. 424 * Return the actual number of bytes reserved or 0 on error. 425 */ 426 size_t reserve(size_t size) 427 { 428 assert(size != 0); 429 size_t npages = round_up(size, PAGESIZE); 430 Pool* pool = newPool(npages); 431 432 if (!pool) 433 return 0; 434 return pool.npages * PAGESIZE; 435 } 436 437 438 /** 439 * Minimizes physical memory usage by returning free pools to the OS. 440 * 441 * If full is false, keep some pools alive if the resulting free memory would 442 * be too small. 443 */ 444 void minimize(bool full = true) 445 { 446 // The shared mark bits of the freed pool might be used by the mark process 447 if (collect_in_progress()) 448 return; 449 450 if (gc.pools.length == 0) 451 return; 452 453 for (size_t n = 0; n < gc.pools.length; n++) 454 { 455 Pool* pool = gc.pools[n]; 456 size_t pn; 457 for (pn = 0; pn < pool.npages; pn++) 458 { 459 if (cast(Bins)pool.pagetable[pn] != B_FREE) 460 break; 461 } 462 if (pn < pool.npages) 463 continue; 464 // Free pool 465 size_t pool_size = pool.npages * PAGESIZE; 466 if (!full) { 467 double percent_free = (gc.free_mem - pool_size) * 100.0 / 468 (gc.total_mem - pool_size); 469 if (percent_free < opts.options.min_free) 470 continue; // not enough free, don't remove this pool 471 } 472 gc.total_mem -= pool_size; 473 gc.free_mem -= pool_size; 474 pool.Dtor(); 475 cstdlib.free(pool); 476 gc.pools.remove_at(n); 477 n--; 478 } 479 gc.min_addr = gc.pools[0].baseAddr; 480 gc.max_addr = gc.pools[gc.pools.length - 1].topAddr; 481 } 482 483 484 /** 485 * Allocate a chunk of memory that is larger than a page. 486 * Return null if out of memory. 487 */ 488 void* bigAlloc(size_t npages, out Pool* pool, size_t* pn, bool* collected) 489 { 490 *collected = false; 491 // This code could use some refinement when repeatedly 492 // allocating very large arrays. 493 494 void* find_block() 495 { 496 for (size_t n = 0; n < gc.pools.length; n++) 497 { 498 pool = gc.pools[n]; 499 *pn = pool.allocPages(npages); 500 if (*pn != OPFAIL) 501 return pool.baseAddr + *pn * PAGESIZE; 502 } 503 return null; 504 } 505 506 void* alloc_more() 507 { 508 // Allocate new pool 509 pool = newPool(npages); 510 if (!pool) 511 return null; // let malloc handle the error 512 *pn = pool.allocPages(npages); 513 assert(*pn != OPFAIL); 514 return pool.baseAddr + *pn * PAGESIZE; 515 } 516 517 if (void* p = find_block()) 518 return p; 519 520 if (gc.disabled) 521 return alloc_more(); 522 523 // Try collecting 524 size_t freedpages = fullcollectshell(); 525 *collected = true; 526 if (freedpages >= npages) { 527 if (void* p = find_block()) 528 return p; 529 } 530 531 return alloc_more(); 532 } 533 534 535 /** 536 * Allocate a new pool with at least npages in it. 537 * Sort it into pools. 538 * Return null if failed. 539 */ 540 Pool *newPool(size_t npages) 541 { 542 // Minimum of POOLSIZE 543 if (npages < POOLSIZE/PAGESIZE) 544 npages = POOLSIZE/PAGESIZE; 545 else if (npages > POOLSIZE/PAGESIZE) 546 { 547 // Give us 150% of requested size, so there's room to extend 548 auto n = npages + (npages >> 1); 549 if (n < size_t.max/PAGESIZE) 550 npages = n; 551 } 552 553 // Allocate successively larger pools up to 8 megs 554 if (gc.pools.length) 555 { 556 size_t n = gc.pools.length; 557 if (n > 8) 558 n = 8; // cap pool size at 8 megs 559 n *= (POOLSIZE / PAGESIZE); 560 if (npages < n) 561 npages = n; 562 } 563 564 auto pool = cast(Pool*) cstdlib.calloc(1, Pool.sizeof); 565 if (pool is null) 566 return null; 567 pool.initialize(npages); 568 if (!pool.baseAddr) 569 { 570 pool.Dtor(); 571 return null; 572 } 573 574 auto inserted_pool = *gc.pools.insert_sorted!("*a < *b")(pool); 575 if (inserted_pool is null) { 576 pool.Dtor(); 577 return null; 578 } 579 assert (inserted_pool is pool); 580 gc.min_addr = gc.pools[0].baseAddr; 581 gc.max_addr = gc.pools[gc.pools.length - 1].topAddr; 582 size_t pool_size = pool.topAddr - pool.baseAddr; 583 gc.total_mem += pool_size; 584 gc.free_mem += pool_size; 585 return pool; 586 } 587 588 589 /** 590 * Allocate a page of bin's. 591 * Returns: 592 * 0 failed 593 */ 594 int allocPage(Bins bin) 595 { 596 Pool* pool; 597 size_t pn; 598 599 for (size_t n = 0; n < gc.pools.length; n++) 600 { 601 pool = gc.pools[n]; 602 pn = pool.allocPages(1); 603 if (pn != OPFAIL) 604 goto L1; 605 } 606 return 0; // failed 607 608 L1: 609 pool.pagetable[pn] = cast(ubyte)bin; 610 611 // Convert page to free list 612 size_t size = binsize[bin]; 613 auto list_head = &gc.free_list[bin]; 614 615 byte* p = pool.baseAddr + pn * PAGESIZE; 616 byte* ptop = p + PAGESIZE; 617 size_t bit_i = pn * (PAGESIZE / 16); 618 pool.freebits.set_group(bit_i, PAGESIZE / 16); 619 for (; p < ptop; p += size) 620 { 621 List* l = cast(List *) p; 622 l.next = *list_head; 623 l.pool = pool; 624 *list_head = l; 625 } 626 return 1; 627 } 628 629 630 /** 631 * Search a range of memory values and mark any pointers into the GC pool using 632 * type information (bitmask of pointer locations). 633 */ 634 void mark_range(void *pbot, void *ptop, size_t* pm_bitmask) 635 { 636 // TODO: make our own assert because assert uses the GC 637 assert (pbot <= ptop); 638 639 const BITS_PER_WORD = size_t.sizeof * 8; 640 641 void **p1 = cast(void **)pbot; 642 void **p2 = cast(void **)ptop; 643 size_t pcache = 0; 644 bool changes = false; 645 646 size_t type_size = pm_bitmask[0]; 647 size_t* pm_bits = pm_bitmask + 1; 648 bool has_type_info = type_size != 1 || pm_bits[0] != 1 || pm_bits[1] != 0; 649 650 //printf("marking range: %p -> %p\n", pbot, ptop); 651 for (; p1 + type_size <= p2; p1 += type_size) { 652 for (size_t n = 0; n < type_size; n++) { 653 // scan bit set for this word 654 if (has_type_info && 655 !(pm_bits[n / BITS_PER_WORD] & (1 << (n % BITS_PER_WORD)))) 656 continue; 657 658 void* p = *(p1 + n); 659 660 if (p < gc.min_addr || p >= gc.max_addr) 661 continue; 662 663 if ((cast(size_t)p & ~(PAGESIZE-1)) == pcache) 664 continue; 665 666 Pool* pool = findPool(p); 667 if (pool) 668 { 669 size_t offset = cast(size_t)(p - pool.baseAddr); 670 size_t bit_i = void; 671 size_t pn = offset / PAGESIZE; 672 Bins bin = cast(Bins)pool.pagetable[pn]; 673 674 // Cache B_PAGE, B_PAGEPLUS and B_FREE lookups 675 if (bin >= B_PAGE) 676 pcache = cast(size_t)p & ~(PAGESIZE-1); 677 678 // Adjust bit to be at start of allocated memory block 679 if (bin <= B_PAGE) 680 bit_i = (offset & notbinsize[bin]) / 16; 681 else if (bin == B_PAGEPLUS) 682 { 683 do 684 { 685 --pn; 686 } 687 while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS); 688 bit_i = pn * (PAGESIZE / 16); 689 } 690 else // Don't mark bits in B_FREE pages 691 continue; 692 693 if (!pool.mark.test(bit_i)) 694 { 695 pool.mark.set(bit_i); 696 if (!pool.noscan.test(bit_i)) 697 { 698 pool.scan.set(bit_i); 699 changes = true; 700 } 701 } 702 } 703 } 704 } 705 if (changes) 706 gc.any_changes = true; 707 } 708 709 /** 710 * Return number of full pages free'd. 711 */ 712 size_t fullcollectshell(bool early = false, bool force_block = false) 713 { 714 gc.stats.collection_started(); 715 scope (exit) 716 gc.stats.collection_finished(); 717 718 // The purpose of the 'shell' is to ensure all the registers 719 // get put on the stack so they'll be scanned 720 void *sp; 721 size_t result; 722 version (GNU) 723 { 724 gcc.builtins.__builtin_unwind_init(); 725 sp = & sp; 726 } 727 else version(LDC) 728 { 729 version(X86) 730 { 731 uint eax,ecx,edx,ebx,ebp,esi,edi; 732 asm 733 { 734 mov eax[EBP], EAX ; 735 mov ecx[EBP], ECX ; 736 mov edx[EBP], EDX ; 737 mov ebx[EBP], EBX ; 738 mov ebp[EBP], EBP ; 739 mov esi[EBP], ESI ; 740 mov edi[EBP], EDI ; 741 mov sp[EBP], ESP ; 742 } 743 } 744 else version (X86_64) 745 { 746 ulong rax,rbx,rcx,rdx,rbp,rsi,rdi,r8,r9,r10,r11,r12,r13,r14,r15; 747 asm 748 { 749 movq rax[RBP], RAX ; 750 movq rbx[RBP], RBX ; 751 movq rcx[RBP], RCX ; 752 movq rdx[RBP], RDX ; 753 movq rbp[RBP], RBP ; 754 movq rsi[RBP], RSI ; 755 movq rdi[RBP], RDI ; 756 movq r8 [RBP], R8 ; 757 movq r9 [RBP], R9 ; 758 movq r10[RBP], R10 ; 759 movq r11[RBP], R11 ; 760 movq r12[RBP], R12 ; 761 movq r13[RBP], R13 ; 762 movq r14[RBP], R14 ; 763 movq r15[RBP], R15 ; 764 movq sp[RBP], RSP ; 765 } 766 } 767 else 768 { 769 static assert( false, "Architecture not supported." ); 770 } 771 } 772 else 773 { 774 version (D_InlineAsm_X86) 775 { 776 asm 777 { 778 pushad ; 779 mov sp[EBP],ESP ; 780 } 781 } 782 else version (D_InlineAsm_X86_64) 783 { 784 asm 785 { 786 push RAX ; 787 push RBX ; 788 push RCX ; 789 push RDX ; 790 push RSI ; 791 push RDI ; 792 push RBP ; 793 push R8 ; 794 push R9 ; 795 push R10 ; 796 push R11 ; 797 push R12 ; 798 push R13 ; 799 push R14 ; 800 push R15 ; 801 push RAX ; // 16 byte align the stack 802 } 803 } 804 else 805 { 806 static assert( false, "Architecture not supported." ); 807 } 808 } 809 result = fullcollect(sp, early, force_block); 810 version (GNU) 811 { 812 // nothing to do 813 } 814 else version(LDC) 815 { 816 // nothing to do 817 } 818 else 819 { 820 version (D_InlineAsm_X86_64) 821 { 822 asm 823 { 824 pop RAX ; 825 pop R15 ; 826 pop R14 ; 827 pop R13 ; 828 pop R12 ; 829 pop R11 ; 830 pop R10 ; 831 pop R9 ; 832 pop R8 ; 833 pop RBP ; 834 pop RDI ; 835 pop RSI ; 836 pop RDX ; 837 pop RCX ; 838 pop RBX ; 839 pop RAX ; 840 } 841 } 842 else 843 { 844 asm 845 { 846 popad ; 847 } 848 } 849 } 850 return result; 851 } 852 853 854 /** 855 * 856 */ 857 size_t fullcollect(void *stackTop, bool early = false, bool force_block = false) 858 { 859 debug(COLLECT_PRINTF) printf("Gcx.fullcollect(early=%d)\n", 860 cast(int) early); 861 862 // We will block the mutator only if eager allocation is not used and this 863 // is not an early collection. 864 bool block = force_block || !opts.options.eager_alloc && !early; 865 866 // If there is a mark process running, check if it already finished. If 867 // that is the case, we lunch the sweep phase and hope enough memory is 868 // freed. If it's still running, either we block until the mark phase is 869 // done (and then sweep to finish the collection), or we tell the caller 870 // process no memory has been recovered (it will allocated more to fulfill 871 // the current request if eager allocation is used) and let the mark phase 872 // keep running in parallel. 873 if (collect_in_progress()) { 874 os.WRes r = os.wait_pid(gc.mark_proc_pid, block); 875 assert (r != os.WRes.ERROR); 876 switch (r) { 877 case os.WRes.DONE: 878 debug(COLLECT_PRINTF) printf("\t\tmark proc DONE (block=%d)\n", 879 cast(int) block); 880 gc.mark_proc_pid = 0; 881 return sweep(); 882 case os.WRes.RUNNING: 883 debug(COLLECT_PRINTF) printf("\t\tmark proc RUNNING\n"); 884 if (!block) 885 return 0; 886 // Something went wrong, if block is true, wait() should never 887 // returned RUNNING. 888 goto case os.WRes.ERROR; 889 case os.WRes.ERROR: 890 debug(COLLECT_PRINTF) printf("\t\tmark proc ERROR\n"); 891 disable_fork(); // Try to keep going without forking 892 break; 893 } 894 } 895 896 // Notify the GC monitor, if any 897 if (gc.collect_begin_cb.funcptr) { 898 debug(COLLECT_PRINTF) printf("\t\tcalling monitor (begin)\n"); 899 gc.collect_begin_cb(); 900 } 901 902 // We always need to stop the world to make threads save the CPU registers 903 // in the stack and prepare themselves for thread_scanAll() 904 thread_suspendAll(); 905 gc.stats.world_stopped(); 906 907 // If forking is enabled, we fork() and start a new mark phase in the 908 // child. If the collection should not block, the parent process tells the 909 // caller no memory could be recycled immediately (if eager allocation is 910 // used, and this collection was triggered by an allocation, the caller 911 // should allocate more memory to fulfill the request). If the collection 912 // should block, the parent will wait for the mark phase to finish before 913 // returning control to the mutator, but other threads are restarted and 914 // may run in parallel with the mark phase (unless they allocate or use the 915 // GC themselves, in which case the global GC lock will stop them). 916 if (opts.options.fork) { 917 cstdio.fflush(null); // avoid duplicated FILE* output 918 os.pid_t child_pid = os.fork(); 919 assert (child_pid != -1); // don't accept errors in non-release mode 920 switch (child_pid) { 921 case -1: // if fork() fails, fall-back to stop-the-world 922 disable_fork(); 923 break; 924 case 0: // child process (i.e. the collectors mark phase) 925 mark(stackTop); 926 cstdlib.exit(0); 927 break; // bogus, will never reach here 928 default: // parent process (i.e. the mutator) 929 thread_resumeAll(); 930 gc.stats.world_started(); 931 if (!block) { 932 gc.mark_proc_pid = child_pid; 933 return 0; 934 } 935 os.WRes r = os.wait_pid(child_pid); // block until it finishes 936 assert (r == os.WRes.DONE); 937 debug(COLLECT_PRINTF) printf("\t\tmark proc DONE (block=%d)\n", 938 cast(int) block); 939 if (r == os.WRes.DONE) 940 return sweep(); 941 debug(COLLECT_PRINTF) printf("\tmark() proc ERROR\n"); 942 // If there was some error, try to keep going without forking 943 disable_fork(); 944 // Re-suspend the threads to do the marking in this process 945 thread_suspendAll(); 946 gc.stats.world_stopped(); 947 } 948 949 } 950 951 // If we reach here, we are using the standard stop-the-world collection, 952 // either because fork was disabled in the first place, or because it was 953 // disabled because of some error. 954 mark(stackTop); 955 thread_resumeAll(); 956 gc.stats.world_started(); 957 958 return sweep(); 959 } 960 961 962 /** 963 * 964 */ 965 void mark(void *stackTop) 966 { 967 debug(COLLECT_PRINTF) printf("\tmark()\n"); 968 969 gc.any_changes = false; 970 971 for (size_t n = 0; n < gc.pools.length; n++) 972 { 973 Pool* pool = gc.pools[n]; 974 pool.mark.copy(&pool.freebits); 975 pool.scan.zero(); 976 } 977 978 /// Marks a range of memory in conservative mode. 979 void mark_conservative_range(void* pbot, void* ptop) 980 { 981 mark_range(pbot, ptop, PointerMap.init.bits.ptr); 982 } 983 984 rt_scanStaticData(&mark_conservative_range); 985 986 if (!gc.no_stack) 987 { 988 // Scan stacks and registers for each paused thread 989 thread_scanAll(&mark_conservative_range, stackTop); 990 } 991 992 // Scan roots 993 debug(COLLECT_PRINTF) printf("scan roots[]\n"); 994 mark_conservative_range(gc.roots.ptr, gc.roots.ptr + gc.roots.length); 995 996 // Scan ranges 997 debug(COLLECT_PRINTF) printf("scan ranges[]\n"); 998 for (size_t n = 0; n < gc.ranges.length; n++) 999 { 1000 debug(COLLECT_PRINTF) printf("\t%x .. %x\n", gc.ranges[n].pbot, gc.ranges[n].ptop); 1001 mark_conservative_range(gc.ranges[n].pbot, gc.ranges[n].ptop); 1002 } 1003 1004 debug(COLLECT_PRINTF) printf("\tscan heap\n"); 1005 while (gc.any_changes) 1006 { 1007 gc.any_changes = false; 1008 for (size_t n = 0; n < gc.pools.length; n++) 1009 { 1010 uint *bbase; 1011 uint *b; 1012 uint *btop; 1013 1014 Pool* pool = gc.pools[n]; 1015 1016 bbase = pool.scan.base(); 1017 btop = bbase + pool.scan.nwords; 1018 for (b = bbase; b < btop;) 1019 { 1020 Bins bin; 1021 size_t pn; 1022 size_t u; 1023 size_t bitm; 1024 byte* o; 1025 1026 bitm = *b; 1027 if (!bitm) 1028 { 1029 b++; 1030 continue; 1031 } 1032 *b = 0; 1033 1034 o = pool.baseAddr + (b - bbase) * 32 * 16; 1035 if (!(bitm & 0xFFFF)) 1036 { 1037 bitm >>= 16; 1038 o += 16 * 16; 1039 } 1040 for (; bitm; o += 16, bitm >>= 1) 1041 { 1042 if (!(bitm & 1)) 1043 continue; 1044 1045 pn = cast(size_t)(o - pool.baseAddr) / PAGESIZE; 1046 bin = cast(Bins)pool.pagetable[pn]; 1047 if (bin < B_PAGE) { 1048 if (opts.options.conservative) 1049 mark_conservative_range(o, o + binsize[bin]); 1050 else { 1051 auto end_of_blk = cast(size_t**)(o + 1052 binsize[bin] - size_t.sizeof); 1053 size_t* pm_bitmask = *end_of_blk; 1054 mark_range(o, end_of_blk, pm_bitmask); 1055 } 1056 } 1057 else if (bin == B_PAGE || bin == B_PAGEPLUS) 1058 { 1059 if (bin == B_PAGEPLUS) 1060 { 1061 while (pool.pagetable[pn - 1] != B_PAGE) 1062 pn--; 1063 } 1064 u = 1; 1065 while (pn + u < pool.npages && 1066 pool.pagetable[pn + u] == B_PAGEPLUS) 1067 u++; 1068 1069 size_t blk_size = u * PAGESIZE; 1070 if (opts.options.conservative) 1071 mark_conservative_range(o, o + blk_size); 1072 else { 1073 auto end_of_blk = cast(size_t**)(o + blk_size - 1074 size_t.sizeof); 1075 size_t* pm_bitmask = *end_of_blk; 1076 mark_range(o, end_of_blk, pm_bitmask); 1077 } 1078 } 1079 } 1080 } 1081 } 1082 } 1083 } 1084 1085 1086 /** 1087 * 1088 */ 1089 size_t sweep() 1090 { 1091 // Free up everything not marked 1092 debug(COLLECT_PRINTF) printf("\tsweep\n"); 1093 gc.p_cache = null; 1094 gc.size_cache = 0; 1095 gc.free_mem = 0; // will be recalculated 1096 size_t freedpages = 0; 1097 size_t freed = 0; 1098 for (size_t n = 0; n < gc.pools.length; n++) 1099 { 1100 Pool* pool = gc.pools[n]; 1101 pool.clear_cache(); 1102 uint* bbase = pool.mark.base(); 1103 size_t pn; 1104 for (pn = 0; pn < pool.npages; pn++, bbase += PAGESIZE / (32 * 16)) 1105 { 1106 Bins bin = cast(Bins)pool.pagetable[pn]; 1107 1108 if (bin < B_PAGE) 1109 { 1110 auto size = binsize[bin]; 1111 byte* p = pool.baseAddr + pn * PAGESIZE; 1112 byte* ptop = p + PAGESIZE; 1113 size_t bit_i = pn * (PAGESIZE/16); 1114 size_t bit_stride = size / 16; 1115 1116 version(none) // BUG: doesn't work because freebits() must also be cleared 1117 { 1118 // If free'd entire page 1119 if (bbase[0] == 0 && bbase[1] == 0 && bbase[2] == 0 && 1120 bbase[3] == 0 && bbase[4] == 0 && bbase[5] == 0 && 1121 bbase[6] == 0 && bbase[7] == 0) 1122 { 1123 for (; p < ptop; p += size, bit_i += bit_stride) 1124 { 1125 if (pool.finals.testClear(bit_i)) { 1126 if (opts.options.sentinel) 1127 rt_finalize(sentinel_add(p), false/*gc.no_stack > 0*/); 1128 else 1129 rt_finalize(p, false/*gc.no_stack > 0*/); 1130 } 1131 clrAttr(pool, bit_i, BlkAttr.ALL_BITS); 1132 1133 if (opts.options.mem_stomp) 1134 memset(p, 0xF3, size); 1135 } 1136 pool.pagetable[pn] = B_FREE; 1137 freed += PAGESIZE; 1138 continue; 1139 } 1140 } 1141 for (; p < ptop; p += size, bit_i += bit_stride) 1142 { 1143 if (!pool.mark.test(bit_i)) 1144 { 1145 if (opts.options.sentinel) 1146 sentinel_Invariant(sentinel_add(p)); 1147 1148 pool.freebits.set(bit_i); 1149 if (pool.finals.testClear(bit_i)) { 1150 if (opts.options.sentinel) 1151 rt_finalize(sentinel_add(p), false/*gc.no_stack > 0*/); 1152 else 1153 rt_finalize(p, false/*gc.no_stack > 0*/); 1154 } 1155 clrAttr(pool, bit_i, BlkAttr.ALL_BITS); 1156 1157 if (opts.options.mem_stomp) 1158 memset(p, 0xF3, size); 1159 1160 freed += size; 1161 } 1162 } 1163 } 1164 else if (bin == B_PAGE) 1165 { 1166 size_t bit_stride = PAGESIZE / 16; 1167 size_t bit_i = pn * bit_stride; 1168 if (!pool.mark.test(bit_i)) 1169 { 1170 byte *p = pool.baseAddr + pn * PAGESIZE; 1171 if (opts.options.sentinel) 1172 sentinel_Invariant(sentinel_add(p)); 1173 if (pool.finals.testClear(bit_i)) { 1174 if (opts.options.sentinel) 1175 rt_finalize(sentinel_add(p), false/*gc.no_stack > 0*/); 1176 else 1177 rt_finalize(p, false/*gc.no_stack > 0*/); 1178 } 1179 clrAttr(pool, bit_i, BlkAttr.ALL_BITS); 1180 1181 debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p); 1182 pool.pagetable[pn] = B_FREE; 1183 pool.freebits.set_group(bit_i, PAGESIZE / 16); 1184 freedpages++; 1185 gc.free_mem += PAGESIZE; 1186 if (opts.options.mem_stomp) 1187 memset(p, 0xF3, PAGESIZE); 1188 while (pn + 1 < pool.npages && pool.pagetable[pn + 1] == B_PAGEPLUS) 1189 { 1190 pn++; 1191 pool.pagetable[pn] = B_FREE; 1192 bit_i += bit_stride; 1193 pool.freebits.set_group(bit_i, PAGESIZE / 16); 1194 freedpages++; 1195 gc.free_mem += PAGESIZE; 1196 1197 if (opts.options.mem_stomp) 1198 { 1199 p += PAGESIZE; 1200 memset(p, 0xF3, PAGESIZE); 1201 } 1202 } 1203 } 1204 } 1205 else if (bin == B_FREE) { 1206 gc.free_mem += PAGESIZE; 1207 } 1208 } 1209 } 1210 1211 // Zero buckets 1212 gc.free_list[] = null; 1213 1214 // Free complete pages, rebuild free list 1215 debug(COLLECT_PRINTF) printf("\tfree complete pages\n"); 1216 size_t recoveredpages = 0; 1217 for (size_t n = 0; n < gc.pools.length; n++) 1218 { 1219 Pool* pool = gc.pools[n]; 1220 for (size_t pn = 0; pn < pool.npages; pn++) 1221 { 1222 Bins bin = cast(Bins)pool.pagetable[pn]; 1223 size_t bit_i; 1224 size_t u; 1225 1226 if (bin < B_PAGE) 1227 { 1228 size_t size = binsize[bin]; 1229 size_t bit_stride = size / 16; 1230 size_t bit_base = pn * (PAGESIZE / 16); 1231 size_t bit_top = bit_base + (PAGESIZE / 16); 1232 byte* p; 1233 1234 bit_i = bit_base; 1235 for (; bit_i < bit_top; bit_i += bit_stride) 1236 { 1237 if (!pool.freebits.test(bit_i)) 1238 goto Lnotfree; 1239 } 1240 pool.pagetable[pn] = B_FREE; 1241 pool.freebits.set_group(bit_base, PAGESIZE / 16); 1242 recoveredpages++; 1243 gc.free_mem += PAGESIZE; 1244 continue; 1245 1246 Lnotfree: 1247 p = pool.baseAddr + pn * PAGESIZE; 1248 for (u = 0; u < PAGESIZE; u += size) 1249 { 1250 bit_i = bit_base + u / 16; 1251 if (pool.freebits.test(bit_i)) 1252 { 1253 assert ((p+u) >= pool.baseAddr); 1254 assert ((p+u) < pool.topAddr); 1255 List* list = cast(List*) (p + u); 1256 // avoid unnecesary writes (it really saves time) 1257 if (list.next != gc.free_list[bin]) 1258 list.next = gc.free_list[bin]; 1259 if (list.pool != pool) 1260 list.pool = pool; 1261 gc.free_list[bin] = list; 1262 gc.free_mem += binsize[bin]; 1263 } 1264 } 1265 } 1266 } 1267 } 1268 1269 debug(COLLECT_PRINTF) printf("recovered pages = %d\n", recoveredpages); 1270 debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", 1271 freed, freedpages, gc.pools.length); 1272 1273 // Notify the GC monitor, if any 1274 if (gc.collect_end_cb.funcptr) { 1275 debug(COLLECT_PRINTF) printf("\t\tcalling monitor (end)\n"); 1276 gc.collect_end_cb(freed + freedpages * PAGESIZE, 1277 (freedpages + recoveredpages) * PAGESIZE); 1278 } 1279 1280 return freedpages + recoveredpages; 1281 } 1282 1283 1284 /** 1285 * 1286 */ 1287 uint getAttr(Pool* pool, size_t bit_i) 1288 in 1289 { 1290 assert( pool ); 1291 } 1292 body 1293 { 1294 uint attrs; 1295 if (pool.finals.test(bit_i)) 1296 attrs |= BlkAttr.FINALIZE; 1297 if (pool.noscan.test(bit_i)) 1298 attrs |= BlkAttr.NO_SCAN; 1299 // if (pool.nomove.test(bit_i)) 1300 // attrs |= BlkAttr.NO_MOVE; 1301 return attrs; 1302 } 1303 1304 1305 /** 1306 * 1307 */ 1308 void setAttr(Pool* pool, size_t bit_i, uint mask) 1309 in 1310 { 1311 assert( pool ); 1312 } 1313 body 1314 { 1315 if (mask & BlkAttr.FINALIZE) 1316 { 1317 pool.finals.set(bit_i); 1318 } 1319 if (mask & BlkAttr.NO_SCAN) 1320 { 1321 pool.noscan.set(bit_i); 1322 } 1323 // if (mask & BlkAttr.NO_MOVE) 1324 // { 1325 // if (!pool.nomove.nbits) 1326 // pool.nomove.alloc(pool.mark.nbits); 1327 // pool.nomove.set(bit_i); 1328 // } 1329 } 1330 1331 1332 /** 1333 * 1334 */ 1335 void clrAttr(Pool* pool, size_t bit_i, uint mask) 1336 in 1337 { 1338 assert( pool ); 1339 } 1340 body 1341 { 1342 if (mask & BlkAttr.FINALIZE) 1343 pool.finals.clear(bit_i); 1344 if (mask & BlkAttr.NO_SCAN) 1345 pool.noscan.clear(bit_i); 1346 // if (mask & BlkAttr.NO_MOVE && pool.nomove.nbits) 1347 // pool.nomove.clear(bit_i); 1348 } 1349 1350 1351 void disable_fork() 1352 { 1353 // we have to disable all options that assume fork is enabled 1354 opts.options.fork = false; 1355 opts.options.eager_alloc = false; 1356 opts.options.early_collect = false; 1357 } 1358 1359 1360 void initialize() 1361 { 1362 int dummy; 1363 gc.stack_bottom = cast(char*)&dummy; 1364 opts.parse(cstdlib.getenv("D_GC_OPTS")); 1365 // If we are going to fork, make sure we have the needed OS support 1366 if (opts.options.fork) 1367 opts.options.fork = os.HAVE_SHARED && os.HAVE_FORK; 1368 // Disable fork()-related options if we don't have it 1369 if (!opts.options.fork) 1370 disable_fork(); 1371 gc.lock = GCLock.classinfo; 1372 gc.inited = 1; 1373 setStackBottom(rt_stackBottom()); 1374 gc.stats = Stats(gc); 1375 if (opts.options.prealloc_npools) { 1376 size_t pages = round_up(opts.options.prealloc_psize, PAGESIZE); 1377 for (size_t i = 0; i < opts.options.prealloc_npools; ++i) 1378 newPool(pages); 1379 } 1380 } 1381 1382 1383 // Launch a parallel collection if we don't have enough free memory available 1384 // (we have less than min_free% of the total heap free). 1385 void early_collect() 1386 { 1387 if ((!opts.options.early_collect || collect_in_progress()) && !gc.disabled) 1388 return; 1389 double percent_free = gc.free_mem * 100.0 / gc.total_mem; 1390 if (percent_free < opts.options.min_free) 1391 fullcollectshell(true); 1392 } 1393 1394 1395 // 1396 // 1397 // 1398 private void *malloc(size_t size, uint attrs, size_t* pm_bitmask) 1399 { 1400 assert(size != 0); 1401 1402 void *p = null; 1403 Bins bin; 1404 1405 gc.stats.malloc_started(size, attrs, pm_bitmask); 1406 scope (exit) 1407 gc.stats.malloc_finished(p); 1408 1409 if (opts.options.sentinel) 1410 size += SENTINEL_EXTRA; 1411 1412 bool has_pm = has_pointermap(attrs); 1413 if (has_pm) 1414 size += size_t.sizeof; 1415 1416 // Compute size bin 1417 // Cache previous binsize lookup - Dave Fladebo. 1418 static size_t lastsize = -1; 1419 static Bins lastbin; 1420 if (size == lastsize) 1421 bin = lastbin; 1422 else 1423 { 1424 bin = findBin(size); 1425 lastsize = size; 1426 lastbin = bin; 1427 } 1428 1429 Pool* pool = void; 1430 size_t bit_i = void; 1431 size_t capacity = void; // to figure out where to store the bitmask 1432 bool collected = false; 1433 if (bin < B_PAGE) 1434 { 1435 p = gc.free_list[bin]; 1436 if (p is null) 1437 { 1438 if (!allocPage(bin) && !gc.disabled) // try to find a new page 1439 { 1440 if (!thread_needLock()) 1441 { 1442 /* Then we haven't locked it yet. Be sure 1443 * and gc.lock for a collection, since a finalizer 1444 * may start a new thread. 1445 */ 1446 synchronized (gc.lock) 1447 { 1448 fullcollectshell(); 1449 } 1450 } 1451 else if (!fullcollectshell()) // collect to find a new page 1452 { 1453 //newPool(1); 1454 } 1455 collected = true; 1456 } 1457 if (!gc.free_list[bin] && !allocPage(bin)) 1458 { 1459 newPool(1); // allocate new pool to find a new page 1460 // TODO: hint allocPage() to use the pool we just created 1461 int result = allocPage(bin); 1462 if (!result) 1463 onOutOfMemoryError(); 1464 } 1465 p = gc.free_list[bin]; 1466 } 1467 capacity = binsize[bin]; 1468 1469 // Return next item from free list 1470 List* list = cast(List*) p; 1471 assert ((cast(byte*)list) >= list.pool.baseAddr); 1472 assert ((cast(byte*)list) < list.pool.topAddr); 1473 gc.free_list[bin] = list.next; 1474 pool = list.pool; 1475 bit_i = (p - pool.baseAddr) / 16; 1476 assert (pool.freebits.test(bit_i)); 1477 pool.freebits.clear(bit_i); 1478 if (!(attrs & BlkAttr.NO_SCAN)) 1479 memset(p + size, 0, capacity - size); 1480 if (opts.options.mem_stomp) 1481 memset(p, 0xF0, size); 1482 } 1483 else 1484 { 1485 size_t pn; 1486 size_t npages = round_up(size, PAGESIZE); 1487 p = bigAlloc(npages, pool, &pn, &collected); 1488 if (!p) 1489 onOutOfMemoryError(); 1490 assert (pool !is null); 1491 1492 capacity = npages * PAGESIZE; 1493 bit_i = pn * (PAGESIZE / 16); 1494 pool.freebits.clear(bit_i); 1495 pool.pagetable[pn] = B_PAGE; 1496 if (npages > 1) 1497 memset(&pool.pagetable[pn + 1], B_PAGEPLUS, npages - 1); 1498 p = pool.baseAddr + pn * PAGESIZE; 1499 memset(cast(char *)p + size, 0, npages * PAGESIZE - size); 1500 if (opts.options.mem_stomp) 1501 memset(p, 0xF1, size); 1502 1503 } 1504 1505 // Store the bit mask AFTER SENTINEL_POST 1506 // TODO: store it BEFORE, so the bitmask is protected too 1507 if (has_pm) { 1508 auto end_of_blk = cast(size_t**)(p + capacity - size_t.sizeof); 1509 *end_of_blk = pm_bitmask; 1510 size -= size_t.sizeof; 1511 } 1512 1513 if (opts.options.sentinel) { 1514 size -= SENTINEL_EXTRA; 1515 p = sentinel_add(p); 1516 sentinel_init(p, size); 1517 } 1518 1519 if (attrs) { 1520 setAttr(pool, bit_i, attrs); 1521 assert (bin >= B_PAGE || !pool.freebits.test(bit_i)); 1522 } 1523 1524 gc.free_mem -= capacity; 1525 if (collected) { 1526 // If there is not enough free memory (after a collection), allocate 1527 // a new pool big enough to have at least the min_free% of the total 1528 // heap free. If the collection left too much free memory, try to free 1529 // some empty pools. 1530 double percent_free = gc.free_mem * 100.0 / gc.total_mem; 1531 if (percent_free < opts.options.min_free) { 1532 auto pool_size = gc.total_mem * 1.0 / opts.options.min_free 1533 - gc.free_mem; 1534 newPool(round_up(cast(size_t)pool_size, PAGESIZE)); 1535 } 1536 else 1537 minimize(false); 1538 } 1539 else 1540 early_collect(); 1541 1542 return p; 1543 } 1544 1545 1546 // 1547 // 1548 // 1549 private void *calloc(size_t size, uint attrs, size_t* pm_bitmask) 1550 { 1551 assert(size != 0); 1552 1553 void *p = malloc(size, attrs, pm_bitmask); 1554 memset(p, 0, size); 1555 return p; 1556 } 1557 1558 1559 // 1560 // 1561 // 1562 private void *realloc(void *p, size_t size, uint attrs, 1563 size_t* pm_bitmask) 1564 { 1565 if (!size) { 1566 if (p) 1567 free(p); 1568 return null; 1569 } 1570 1571 if (p is null) 1572 return malloc(size, attrs, pm_bitmask); 1573 1574 Pool* pool = findPool(p); 1575 if (pool is null) 1576 return null; 1577 1578 // Set or retrieve attributes as appropriate 1579 auto bit_i = cast(size_t)(p - pool.baseAddr) / 16; 1580 if (attrs) { 1581 clrAttr(pool, bit_i, BlkAttr.ALL_BITS); 1582 setAttr(pool, bit_i, attrs); 1583 } 1584 else 1585 attrs = getAttr(pool, bit_i); 1586 1587 void* blk_base_addr = pool.findBase(p); 1588 size_t blk_size = pool.findSize(p); 1589 bool has_pm = has_pointermap(attrs); 1590 size_t pm_bitmask_size = 0; 1591 if (has_pm) { 1592 pm_bitmask_size = size_t.sizeof; 1593 // Retrieve pointer map bit mask if appropriate 1594 if (pm_bitmask is null) { 1595 auto end_of_blk = cast(size_t**)( 1596 blk_base_addr + blk_size - size_t.sizeof); 1597 pm_bitmask = *end_of_blk; 1598 } 1599 } 1600 1601 if (opts.options.sentinel) { 1602 sentinel_Invariant(p); 1603 size_t sentinel_stored_size = *sentinel_size(p); 1604 if (sentinel_stored_size != size) { 1605 void* p2 = malloc(size, attrs, pm_bitmask); 1606 if (sentinel_stored_size < size) 1607 size = sentinel_stored_size; 1608 cstring.memcpy(p2, p, size); 1609 p = p2; 1610 } 1611 return p; 1612 } 1613 1614 size += pm_bitmask_size; 1615 if (blk_size >= PAGESIZE && size >= PAGESIZE) { 1616 auto psz = blk_size / PAGESIZE; 1617 auto newsz = round_up(size, PAGESIZE); 1618 if (newsz == psz) 1619 return p; 1620 1621 auto pagenum = (p - pool.baseAddr) / PAGESIZE; 1622 1623 if (newsz < psz) { 1624 // Shrink in place 1625 if (opts.options.mem_stomp) 1626 memset(p + size - pm_bitmask_size, 0xF2, 1627 blk_size - size - pm_bitmask_size); 1628 pool.freePages(pagenum + newsz, psz - newsz); 1629 auto new_blk_size = (PAGESIZE * newsz); 1630 gc.free_mem += blk_size - new_blk_size; 1631 // update the size cache, assuming that is very likely the 1632 // size of this block will be queried in the near future 1633 pool.update_cache(p, new_blk_size); 1634 if (has_pm) { 1635 auto end_of_blk = cast(size_t**)(blk_base_addr + 1636 new_blk_size - pm_bitmask_size); 1637 *end_of_blk = pm_bitmask; 1638 } 1639 return p; 1640 } 1641 1642 if (pagenum + newsz <= pool.npages) { 1643 // Attempt to expand in place 1644 for (size_t i = pagenum + psz; 1;) { 1645 if (i == pagenum + newsz) { 1646 if (opts.options.mem_stomp) 1647 memset(p + blk_size - pm_bitmask_size, 0xF0, 1648 size - blk_size - pm_bitmask_size); 1649 memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, 1650 newsz - psz); 1651 auto new_blk_size = (PAGESIZE * newsz); 1652 gc.free_mem -= new_blk_size - blk_size; 1653 // update the size cache, assuming that is very 1654 // likely the size of this block will be queried in 1655 // the near future 1656 pool.update_cache(p, new_blk_size); 1657 if (has_pm) { 1658 auto end_of_blk = cast(size_t**)( 1659 blk_base_addr + new_blk_size - pm_bitmask_size); 1660 *end_of_blk = pm_bitmask; 1661 } 1662 early_collect(); 1663 return p; 1664 } 1665 if (i == pool.npages) 1666 break; 1667 if (pool.pagetable[i] != B_FREE) 1668 break; 1669 i++; 1670 } 1671 } 1672 } 1673 1674 // if new size is bigger or less than half 1675 if (blk_size < size || blk_size > size * 2) { 1676 size -= pm_bitmask_size; 1677 blk_size -= pm_bitmask_size; 1678 void* p2 = malloc(size, attrs, pm_bitmask); 1679 if (blk_size < size) 1680 size = blk_size; 1681 cstring.memcpy(p2, p, size); 1682 p = p2; 1683 } 1684 1685 return p; 1686 } 1687 1688 1689 /** 1690 * Attempt to in-place enlarge the memory block pointed to by p by at least 1691 * min_size beyond its current capacity, up to a maximum of max_size. This 1692 * does not attempt to move the memory block (like realloc() does). 1693 * 1694 * Returns: 1695 * 0 if could not extend p, 1696 * total size of entire memory block if successful. 1697 */ 1698 private size_t extend(void* p, size_t minsize, size_t maxsize) 1699 in 1700 { 1701 assert( minsize <= maxsize ); 1702 } 1703 body 1704 { 1705 if (opts.options.sentinel) 1706 return 0; 1707 1708 Pool* pool = findPool(p); 1709 if (pool is null) 1710 return 0; 1711 1712 // Retrieve attributes 1713 auto bit_i = cast(size_t)(p - pool.baseAddr) / 16; 1714 uint attrs = getAttr(pool, bit_i); 1715 1716 void* blk_base_addr = pool.findBase(p); 1717 size_t blk_size = pool.findSize(p); 1718 bool has_pm = has_pointermap(attrs); 1719 size_t* pm_bitmask = null; 1720 size_t pm_bitmask_size = 0; 1721 if (has_pm) { 1722 pm_bitmask_size = size_t.sizeof; 1723 // Retrieve pointer map bit mask 1724 auto end_of_blk = cast(size_t**)(blk_base_addr + 1725 blk_size - size_t.sizeof); 1726 pm_bitmask = *end_of_blk; 1727 1728 minsize += size_t.sizeof; 1729 maxsize += size_t.sizeof; 1730 } 1731 1732 if (blk_size < PAGESIZE) 1733 return 0; // cannot extend buckets 1734 1735 auto psz = blk_size / PAGESIZE; 1736 auto minsz = round_up(minsize, PAGESIZE); 1737 auto maxsz = round_up(maxsize, PAGESIZE); 1738 1739 auto pagenum = (p - pool.baseAddr) / PAGESIZE; 1740 1741 size_t sz; 1742 for (sz = 0; sz < maxsz; sz++) 1743 { 1744 auto i = pagenum + psz + sz; 1745 if (i == pool.npages) 1746 break; 1747 if (pool.pagetable[i] != B_FREE) 1748 { 1749 if (sz < minsz) 1750 return 0; 1751 break; 1752 } 1753 } 1754 if (sz < minsz) 1755 return 0; 1756 1757 size_t new_size = (psz + sz) * PAGESIZE; 1758 1759 if (opts.options.mem_stomp) 1760 memset(p + blk_size - pm_bitmask_size, 0xF0, 1761 new_size - blk_size - pm_bitmask_size); 1762 memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, sz); 1763 gc.p_cache = null; 1764 gc.size_cache = 0; 1765 gc.free_mem -= new_size - blk_size; 1766 // update the size cache, assuming that is very likely the size of this 1767 // block will be queried in the near future 1768 pool.update_cache(p, new_size); 1769 1770 if (has_pm) { 1771 new_size -= size_t.sizeof; 1772 auto end_of_blk = cast(size_t**)(blk_base_addr + new_size); 1773 *end_of_blk = pm_bitmask; 1774 } 1775 1776 early_collect(); 1777 1778 return new_size; 1779 } 1780 1781 1782 // 1783 // 1784 // 1785 private void free(void *p) 1786 { 1787 assert (p); 1788 1789 Pool* pool; 1790 size_t pagenum; 1791 Bins bin; 1792 size_t bit_i; 1793 1794 // Find which page it is in 1795 pool = findPool(p); 1796 if (!pool) // if not one of ours 1797 return; // ignore 1798 if (opts.options.sentinel) { 1799 sentinel_Invariant(p); 1800 p = sentinel_sub(p); 1801 } 1802 pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE; 1803 bit_i = cast(size_t)(p - pool.baseAddr) / 16; 1804 clrAttr(pool, bit_i, BlkAttr.ALL_BITS); 1805 1806 bin = cast(Bins)pool.pagetable[pagenum]; 1807 if (bin == B_PAGE) // if large alloc 1808 { 1809 // Free pages 1810 size_t npages = 1; 1811 size_t n = pagenum; 1812 pool.freebits.set_group(bit_i, PAGESIZE / 16); 1813 while (++n < pool.npages && pool.pagetable[n] == B_PAGEPLUS) 1814 npages++; 1815 size_t size = npages * PAGESIZE; 1816 if (opts.options.mem_stomp) 1817 memset(p, 0xF2, size); 1818 pool.freePages(pagenum, npages); 1819 gc.free_mem += size; 1820 // just in case we were caching this pointer 1821 pool.clear_cache(p); 1822 } 1823 else 1824 { 1825 // Add to free list 1826 List* list = cast(List*) p; 1827 1828 if (opts.options.mem_stomp) 1829 memset(p, 0xF2, binsize[bin]); 1830 1831 list.next = gc.free_list[bin]; 1832 list.pool = pool; 1833 gc.free_list[bin] = list; 1834 pool.freebits.set(bit_i); 1835 gc.free_mem += binsize[bin]; 1836 } 1837 double percent_free = gc.free_mem * 100.0 / gc.total_mem; 1838 if (percent_free > opts.options.min_free) 1839 minimize(false); 1840 } 1841 1842 1843 /** 1844 * Determine the allocated size of pointer p. If p is an interior pointer 1845 * or not a gc allocated pointer, return 0. 1846 */ 1847 private size_t sizeOf(void *p) 1848 { 1849 assert (p); 1850 1851 if (opts.options.sentinel) 1852 p = sentinel_sub(p); 1853 1854 Pool* pool = findPool(p); 1855 if (pool is null) 1856 return 0; 1857 1858 auto biti = cast(size_t)(p - pool.baseAddr) / 16; 1859 uint attrs = getAttr(pool, biti); 1860 1861 size_t size = pool.findSize(p); 1862 size_t pm_bitmask_size = 0; 1863 if (has_pointermap(attrs)) 1864 pm_bitmask_size = size_t.sizeof; 1865 1866 if (opts.options.sentinel) { 1867 // Check for interior pointer 1868 // This depends on: 1869 // 1) size is a power of 2 for less than PAGESIZE values 1870 // 2) base of memory pool is aligned on PAGESIZE boundary 1871 if (cast(size_t)p & (size - 1) & (PAGESIZE - 1)) 1872 return 0; 1873 return size - SENTINEL_EXTRA - pm_bitmask_size; 1874 } 1875 else { 1876 if (p == gc.p_cache) 1877 return gc.size_cache; 1878 1879 // Check for interior pointer 1880 // This depends on: 1881 // 1) size is a power of 2 for less than PAGESIZE values 1882 // 2) base of memory pool is aligned on PAGESIZE boundary 1883 if (cast(size_t)p & (size - 1) & (PAGESIZE - 1)) 1884 return 0; 1885 1886 gc.p_cache = p; 1887 gc.size_cache = size - pm_bitmask_size; 1888 1889 return gc.size_cache; 1890 } 1891 } 1892 1893 1894 /** 1895 * Verify that pointer p: 1896 * 1) belongs to this memory pool 1897 * 2) points to the start of an allocated piece of memory 1898 * 3) is not on a free list 1899 */ 1900 private void checkNoSync(void *p) 1901 { 1902 assert(p); 1903 1904 if (opts.options.sentinel) 1905 sentinel_Invariant(p); 1906 debug (PTRCHECK) 1907 { 1908 Pool* pool; 1909 size_t pagenum; 1910 Bins bin; 1911 size_t size; 1912 1913 if (opts.options.sentinel) 1914 p = sentinel_sub(p); 1915 pool = findPool(p); 1916 assert(pool); 1917 pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE; 1918 bin = cast(Bins)pool.pagetable[pagenum]; 1919 assert(bin <= B_PAGE); 1920 size = binsize[bin]; 1921 assert((cast(size_t)p & (size - 1)) == 0); 1922 1923 debug (PTRCHECK2) 1924 { 1925 if (bin < B_PAGE) 1926 { 1927 // Check that p is not on a free list 1928 for (List* list = gc.free_list[bin]; list; list = list.next) 1929 { 1930 assert(cast(void*)list != p); 1931 } 1932 } 1933 } 1934 } 1935 } 1936 1937 1938 // 1939 // 1940 // 1941 private void setStackBottom(void *p) 1942 { 1943 version (STACKGROWSDOWN) 1944 { 1945 //p = (void *)((uint *)p + 4); 1946 if (p > gc.stack_bottom) 1947 { 1948 gc.stack_bottom = p; 1949 } 1950 } 1951 else 1952 { 1953 //p = (void *)((uint *)p - 4); 1954 if (p < gc.stack_bottom) 1955 { 1956 gc.stack_bottom = cast(char*)p; 1957 } 1958 } 1959 } 1960 1961 1962 /** 1963 * Retrieve statistics about garbage collection. 1964 * Useful for debugging and tuning. 1965 */ 1966 private GCStats getStats() 1967 { 1968 GCStats stats; 1969 size_t psize = 0; 1970 size_t usize = 0; 1971 size_t flsize = 0; 1972 1973 size_t n; 1974 size_t bsize = 0; 1975 1976 for (n = 0; n < gc.pools.length; n++) 1977 { 1978 Pool* pool = gc.pools[n]; 1979 psize += pool.npages * PAGESIZE; 1980 for (size_t j = 0; j < pool.npages; j++) 1981 { 1982 Bins bin = cast(Bins)pool.pagetable[j]; 1983 if (bin == B_FREE) 1984 stats.freeblocks++; 1985 else if (bin == B_PAGE) 1986 stats.pageblocks++; 1987 else if (bin < B_PAGE) 1988 bsize += PAGESIZE; 1989 } 1990 } 1991 1992 for (n = 0; n < B_PAGE; n++) 1993 { 1994 for (List* list = gc.free_list[n]; list; list = list.next) 1995 flsize += binsize[n]; 1996 } 1997 1998 usize = bsize - flsize; 1999 2000 stats.poolsize = psize; 2001 stats.usedsize = bsize - flsize; 2002 stats.freelistsize = flsize; 2003 return stats; 2004 } 2005 2006 /******************* weak-reference support *********************/ 2007 2008 private struct WeakPointer 2009 { 2010 Object reference; 2011 2012 void ondestroy(Object r) 2013 { 2014 assert(r is reference); 2015 // lock for memory consistency (parallel readers) 2016 // also ensures that weakpointerDestroy can be called while another 2017 // thread is freeing the reference with "delete" 2018 return locked!(void, () { 2019 reference = null; 2020 })(); 2021 } 2022 } 2023 2024 /** 2025 * Create a weak pointer to the given object. 2026 * Returns a pointer to an opaque struct allocated in C memory. 2027 */ 2028 void* weakpointerCreate( Object r ) 2029 { 2030 if (r) 2031 { 2032 // must be allocated in C memory 2033 // 1. to hide the reference from the GC 2034 // 2. the GC doesn't scan delegates added by rt_attachDisposeEvent 2035 // for references 2036 auto wp = cast(WeakPointer*)(cstdlib.malloc(WeakPointer.sizeof)); 2037 if (!wp) 2038 onOutOfMemoryError(); 2039 wp.reference = r; 2040 rt_attachDisposeEvent(r, &wp.ondestroy); 2041 return wp; 2042 } 2043 return null; 2044 } 2045 2046 /** 2047 * Destroy a weak pointer returned by weakpointerCreate(). 2048 * If null is passed, nothing happens. 2049 */ 2050 void weakpointerDestroy( void* p ) 2051 { 2052 if (p) 2053 { 2054 auto wp = cast(WeakPointer*)p; 2055 // must be extra careful about the GC or parallel threads 2056 // finalizing the reference at the same time 2057 return locked!(void, () { 2058 if (wp.reference) 2059 rt_detachDisposeEventNoLock(wp.reference, &wp.ondestroy); 2060 })(); 2061 cstdlib.free(wp); 2062 } 2063 } 2064 2065 /** 2066 * Query a weak pointer and return either the object passed to 2067 * weakpointerCreate, or null if it was free'd in the meantime. 2068 * If null is passed, null is returned. 2069 */ 2070 Object weakpointerGet( void* p ) 2071 { 2072 if (p) 2073 { 2074 // NOTE: could avoid the lock by using Fawzi style GC counters but 2075 // that'd require core.sync.Atomic and lots of care about memory 2076 // consistency it's an optional optimization see 2077 // http://dsource.org/projects/tango/browser/trunk/user/tango/core/Lifetime.d?rev=5100#L158 2078 return locked!(Object, () { 2079 return (cast(WeakPointer*)p).reference; 2080 })(); 2081 } 2082 } 2083 2084 2085 /* ============================ Pool =============================== */ 2086 2087 2088 struct Pool 2089 { 2090 byte* baseAddr; 2091 byte* topAddr; 2092 GCBits mark; // entries already scanned, or should not be scanned 2093 GCBits scan; // entries that need to be scanned 2094 GCBits freebits; // entries that are on the free list 2095 GCBits finals; // entries that need finalizer run on them 2096 GCBits noscan; // entries that should not be scanned 2097 2098 size_t npages; 2099 ubyte* pagetable; 2100 2101 /// Cache for findSize() 2102 size_t cached_size; 2103 void* cached_ptr; 2104 2105 void clear_cache(void* ptr = null) 2106 { 2107 if (ptr is null || ptr is this.cached_ptr) { 2108 this.cached_ptr = null; 2109 this.cached_size = 0; 2110 } 2111 } 2112 2113 void update_cache(void* ptr, size_t size) 2114 { 2115 this.cached_ptr = ptr; 2116 this.cached_size = size; 2117 } 2118 2119 void initialize(size_t npages) 2120 { 2121 size_t poolsize = npages * PAGESIZE; 2122 assert(poolsize >= POOLSIZE); 2123 baseAddr = cast(byte *) os.alloc(poolsize); 2124 2125 // Some of the code depends on page alignment of memory pools 2126 assert((cast(size_t)baseAddr & (PAGESIZE - 1)) == 0); 2127 2128 if (!baseAddr) 2129 { 2130 npages = 0; 2131 poolsize = 0; 2132 } 2133 topAddr = baseAddr + poolsize; 2134 2135 size_t nbits = cast(size_t)poolsize / 16; 2136 2137 // if the GC will run in parallel in a fork()ed process, we need to 2138 // share the mark bits 2139 os.Vis vis = os.Vis.PRIV; 2140 if (opts.options.fork) 2141 vis = os.Vis.SHARED; 2142 mark.alloc(nbits, vis); // shared between mark and sweep 2143 freebits.alloc(nbits); // not used by the mark phase 2144 scan.alloc(nbits); // only used in the mark phase 2145 finals.alloc(nbits); // not used by the mark phase 2146 noscan.alloc(nbits); // mark phase *MUST* have a snapshot 2147 2148 // all is free when we start 2149 freebits.set_all(); 2150 2151 // avoid accidental sweeping of new pools while using eager allocation 2152 if (collect_in_progress()) 2153 mark.set_all(); 2154 2155 pagetable = cast(ubyte*) cstdlib.malloc(npages); 2156 if (!pagetable) 2157 onOutOfMemoryError(); 2158 memset(pagetable, B_FREE, npages); 2159 2160 this.npages = npages; 2161 } 2162 2163 2164 void Dtor() 2165 { 2166 if (baseAddr) 2167 { 2168 int result; 2169 2170 if (npages) 2171 { 2172 result = os.dealloc(baseAddr, npages * PAGESIZE); 2173 assert(result); 2174 npages = 0; 2175 } 2176 2177 baseAddr = null; 2178 topAddr = null; 2179 } 2180 // See Gcx.Dtor() for the rationale of the null check. 2181 if (pagetable) 2182 cstdlib.free(pagetable); 2183 2184 os.Vis vis = os.Vis.PRIV; 2185 if (opts.options.fork) 2186 vis = os.Vis.SHARED; 2187 mark.Dtor(vis); 2188 freebits.Dtor(); 2189 scan.Dtor(); 2190 finals.Dtor(); 2191 noscan.Dtor(); 2192 } 2193 2194 2195 bool Invariant() 2196 { 2197 return true; 2198 } 2199 2200 2201 invariant 2202 { 2203 //mark.Invariant(); 2204 //scan.Invariant(); 2205 //freebits.Invariant(); 2206 //finals.Invariant(); 2207 //noscan.Invariant(); 2208 2209 if (baseAddr) 2210 { 2211 //if (baseAddr + npages * PAGESIZE != topAddr) 2212 //printf("baseAddr = %p, npages = %d, topAddr = %p\n", baseAddr, npages, topAddr); 2213 assert(baseAddr + npages * PAGESIZE == topAddr); 2214 } 2215 2216 for (size_t i = 0; i < npages; i++) 2217 { 2218 Bins bin = cast(Bins)pagetable[i]; 2219 assert(bin < B_MAX); 2220 } 2221 } 2222 2223 2224 /** 2225 * Allocate n pages from Pool. 2226 * Returns OPFAIL on failure. 2227 */ 2228 size_t allocPages(size_t n) 2229 { 2230 size_t i; 2231 size_t n2; 2232 2233 n2 = n; 2234 for (i = 0; i < npages; i++) 2235 { 2236 if (pagetable[i] == B_FREE) 2237 { 2238 if (--n2 == 0) 2239 return i - n + 1; 2240 } 2241 else 2242 n2 = n; 2243 } 2244 return OPFAIL; 2245 } 2246 2247 2248 /** 2249 * Free npages pages starting with pagenum. 2250 */ 2251 void freePages(size_t pagenum, size_t npages) 2252 { 2253 memset(&pagetable[pagenum], B_FREE, npages); 2254 } 2255 2256 2257 /** 2258 * Find base address of block containing pointer p. 2259 * Returns null if the pointer doesn't belong to this pool 2260 */ 2261 void* findBase(void *p) 2262 { 2263 size_t offset = cast(size_t)(p - this.baseAddr); 2264 size_t pagenum = offset / PAGESIZE; 2265 Bins bin = cast(Bins)this.pagetable[pagenum]; 2266 // Adjust bit to be at start of allocated memory block 2267 if (bin <= B_PAGE) 2268 return this.baseAddr + (offset & notbinsize[bin]); 2269 if (bin == B_PAGEPLUS) { 2270 do { 2271 --pagenum, offset -= PAGESIZE; 2272 } while (cast(Bins)this.pagetable[pagenum] == B_PAGEPLUS); 2273 return this.baseAddr + (offset & (offset.max ^ (PAGESIZE-1))); 2274 } 2275 // we are in a B_FREE page 2276 return null; 2277 } 2278 2279 2280 /** 2281 * Find size of pointer p. 2282 * Returns 0 if p doesn't belong to this pool if if it's block size is less 2283 * than a PAGE. 2284 */ 2285 size_t findSize(void *p) 2286 { 2287 size_t pagenum = cast(size_t)(p - this.baseAddr) / PAGESIZE; 2288 Bins bin = cast(Bins)this.pagetable[pagenum]; 2289 if (bin != B_PAGE) 2290 return binsize[bin]; 2291 if (this.cached_ptr == p) 2292 return this.cached_size; 2293 size_t i = pagenum + 1; 2294 for (; i < this.npages; i++) 2295 if (this.pagetable[i] != B_PAGEPLUS) 2296 break; 2297 this.cached_ptr = p; 2298 this.cached_size = (i - pagenum) * PAGESIZE; 2299 return this.cached_size; 2300 } 2301 2302 2303 /** 2304 * Used for sorting pools 2305 */ 2306 int opCmp(in Pool other) 2307 { 2308 if (baseAddr < other.baseAddr) 2309 return -1; 2310 else 2311 return cast(int)(baseAddr > other.baseAddr); 2312 } 2313 } 2314 2315 2316 /* ============================ SENTINEL =============================== */ 2317 2318 2319 const size_t SENTINEL_PRE = cast(size_t) 0xF4F4F4F4F4F4F4F4UL; // 32 or 64 bits 2320 const ubyte SENTINEL_POST = 0xF5; // 8 bits 2321 const uint SENTINEL_EXTRA = 2 * size_t.sizeof + 1; 2322 2323 2324 size_t* sentinel_size(void *p) { return &(cast(size_t *)p)[-2]; } 2325 size_t* sentinel_pre(void *p) { return &(cast(size_t *)p)[-1]; } 2326 ubyte* sentinel_post(void *p) { return &(cast(ubyte *)p)[*sentinel_size(p)]; } 2327 2328 2329 void sentinel_init(void *p, size_t size) 2330 { 2331 *sentinel_size(p) = size; 2332 *sentinel_pre(p) = SENTINEL_PRE; 2333 *sentinel_post(p) = SENTINEL_POST; 2334 } 2335 2336 2337 void sentinel_Invariant(void *p) 2338 { 2339 if (*sentinel_pre(p) != SENTINEL_PRE || 2340 *sentinel_post(p) != SENTINEL_POST) 2341 cstdlib.abort(); 2342 } 2343 2344 2345 void *sentinel_add(void *p) 2346 { 2347 return p + 2 * size_t.sizeof; 2348 } 2349 2350 2351 void *sentinel_sub(void *p) 2352 { 2353 return p - 2 * size_t.sizeof; 2354 } 2355 2356 2357 2358 /* ============================ C Public Interface ======================== */ 2359 2360 2361 private int _termCleanupLevel=1; 2362 2363 extern (C): 2364 2365 /// sets the cleanup level done by gc 2366 /// 0: none 2367 /// 1: fullCollect 2368 /// 2: fullCollect ignoring stack roots (might crash daemonThreads) 2369 /// result !=0 if the value was invalid 2370 int gc_setTermCleanupLevel(int cLevel) 2371 { 2372 if (cLevel<0 || cLevel>2) return cLevel; 2373 _termCleanupLevel=cLevel; 2374 return 0; 2375 } 2376 2377 /// returns the cleanup level done by gc 2378 int gc_getTermCleanupLevel() 2379 { 2380 return _termCleanupLevel; 2381 } 2382 2383 void gc_init() 2384 { 2385 scope (exit) assert (Invariant()); 2386 gc = cast(GC*) cstdlib.calloc(1, GC.sizeof); 2387 *gc = GC.init; 2388 initialize(); 2389 version (DigitalMars) version(OSX) { 2390 _d_osx_image_init(); 2391 } 2392 // NOTE: The GC must initialize the thread library 2393 // before its first collection. 2394 thread_init(); 2395 } 2396 2397 void gc_term() 2398 { 2399 assert (Invariant()); 2400 if (_termCleanupLevel<1) { 2401 // no cleanup 2402 } else if (_termCleanupLevel==2){ 2403 // a more complete cleanup 2404 // NOTE: There may be daemons threads still running when this routine is 2405 // called. If so, cleaning memory out from under then is a good 2406 // way to make them crash horribly. 2407 // Often this probably doesn't matter much since the app is 2408 // supposed to be shutting down anyway, but for example tests might 2409 // crash (and be considerd failed even if the test was ok). 2410 // thus this is not the default and should be enabled by 2411 // I'm disabling cleanup for now until I can think about it some 2412 // more. 2413 // 2414 // not really a 'collect all' -- still scans static data area, roots, 2415 // and ranges. 2416 return locked!(void, () { 2417 gc.no_stack++; 2418 fullcollectshell(false, true); // force block 2419 gc.no_stack--; 2420 })(); 2421 } else { 2422 // default (safe) clenup 2423 return locked!(void, () { 2424 fullcollectshell(false, true); // force block 2425 })(); 2426 } 2427 } 2428 2429 void gc_enable() 2430 { 2431 return locked!(void, () { 2432 assert (Invariant()); scope (exit) assert (Invariant()); 2433 assert (gc.disabled > 0); 2434 gc.disabled--; 2435 })(); 2436 } 2437 2438 void gc_disable() 2439 { 2440 return locked!(void, () { 2441 assert (Invariant()); scope (exit) assert (Invariant()); 2442 gc.disabled++; 2443 })(); 2444 } 2445 2446 void gc_collect() 2447 { 2448 return locked!(void, () { 2449 assert (Invariant()); scope (exit) assert (Invariant()); 2450 fullcollectshell(); 2451 })(); 2452 } 2453 2454 2455 void gc_minimize() 2456 { 2457 return locked!(void, () { 2458 assert (Invariant()); scope (exit) assert (Invariant()); 2459 minimize(); 2460 })(); 2461 } 2462 2463 uint gc_getAttr(void* p) 2464 { 2465 if (p is null) 2466 return 0; 2467 return locked!(uint, () { 2468 assert (Invariant()); scope (exit) assert (Invariant()); 2469 Pool* pool = findPool(p); 2470 if (pool is null) 2471 return 0u; 2472 auto bit_i = cast(size_t)(p - pool.baseAddr) / 16; 2473 return getAttr(pool, bit_i); 2474 })(); 2475 } 2476 2477 uint gc_setAttr(void* p, uint attrs) 2478 { 2479 if (p is null) 2480 return 0; 2481 return locked!(uint, () { 2482 assert (Invariant()); scope (exit) assert (Invariant()); 2483 Pool* pool = findPool(p); 2484 if (pool is null) 2485 return 0u; 2486 auto bit_i = cast(size_t)(p - pool.baseAddr) / 16; 2487 uint old_attrs = getAttr(pool, bit_i); 2488 setAttr(pool, bit_i, attrs); 2489 return old_attrs; 2490 })(); 2491 } 2492 2493 uint gc_clrAttr(void* p, uint attrs) 2494 { 2495 if (p is null) 2496 return 0; 2497 return locked!(uint, () { 2498 assert (Invariant()); scope (exit) assert (Invariant()); 2499 Pool* pool = findPool(p); 2500 if (pool is null) 2501 return 0u; 2502 auto bit_i = cast(size_t)(p - pool.baseAddr) / 16; 2503 uint old_attrs = getAttr(pool, bit_i); 2504 clrAttr(pool, bit_i, attrs); 2505 return old_attrs; 2506 })(); 2507 } 2508 2509 void* gc_malloc(size_t size, uint attrs = 0, 2510 PointerMap ptrmap = PointerMap.init) 2511 { 2512 if (size == 0) 2513 return null; 2514 return locked!(void*, () { 2515 assert (Invariant()); scope (exit) assert (Invariant()); 2516 return malloc(size, attrs, ptrmap.bits.ptr); 2517 })(); 2518 } 2519 2520 void* gc_calloc(size_t size, uint attrs = 0, 2521 PointerMap ptrmap = PointerMap.init) 2522 { 2523 if (size == 0) 2524 return null; 2525 return locked!(void*, () { 2526 assert (Invariant()); scope (exit) assert (Invariant()); 2527 return calloc(size, attrs, ptrmap.bits.ptr); 2528 })(); 2529 } 2530 2531 void* gc_realloc(void* p, size_t size, uint attrs = 0, 2532 PointerMap ptrmap = PointerMap.init) 2533 { 2534 return locked!(void*, () { 2535 assert (Invariant()); scope (exit) assert (Invariant()); 2536 return realloc(p, size, attrs, ptrmap.bits.ptr); 2537 })(); 2538 } 2539 2540 size_t gc_extend(void* p, size_t min_size, size_t max_size) 2541 { 2542 return locked!(size_t, () { 2543 assert (Invariant()); scope (exit) assert (Invariant()); 2544 return extend(p, min_size, max_size); 2545 })(); 2546 } 2547 2548 size_t gc_reserve(size_t size) 2549 { 2550 if (size == 0) 2551 return 0; 2552 return locked!(size_t, () { 2553 assert (Invariant()); scope (exit) assert (Invariant()); 2554 return reserve(size); 2555 })(); 2556 } 2557 2558 void gc_free(void* p) 2559 { 2560 if (p is null) 2561 return; 2562 return locked!(void, () { 2563 assert (Invariant()); scope (exit) assert (Invariant()); 2564 free(p); 2565 })(); 2566 } 2567 2568 void* gc_addrOf(void* p) 2569 { 2570 if (p is null) 2571 return null; 2572 return locked!(void*, () { 2573 assert (Invariant()); scope (exit) assert (Invariant()); 2574 Pool* pool = findPool(p); 2575 if (pool is null) 2576 return null; 2577 return pool.findBase(p); 2578 })(); 2579 } 2580 2581 size_t gc_sizeOf(void* p) 2582 { 2583 if (p is null) 2584 return 0; 2585 return locked!(size_t, () { 2586 assert (Invariant()); scope (exit) assert (Invariant()); 2587 return sizeOf(p); 2588 })(); 2589 } 2590 2591 BlkInfo gc_query(void* p) 2592 { 2593 if (p is null) 2594 return BlkInfo.init; 2595 return locked!(BlkInfo, () { 2596 assert (Invariant()); scope (exit) assert (Invariant()); 2597 return getInfo(p); 2598 })(); 2599 } 2600 2601 // NOTE: This routine is experimental. The stats or function name may change 2602 // before it is made officially available. 2603 GCStats gc_stats() 2604 { 2605 return locked!(GCStats, () { 2606 assert (Invariant()); scope (exit) assert (Invariant()); 2607 return getStats(); 2608 })(); 2609 } 2610 2611 void gc_addRoot(void* p) 2612 { 2613 if (p is null) 2614 return; 2615 return locked!(void, () { 2616 assert (Invariant()); scope (exit) assert (Invariant()); 2617 if (gc.roots.append(p) is null) 2618 onOutOfMemoryError(); 2619 })(); 2620 } 2621 2622 void gc_addRange(void* p, size_t size) 2623 { 2624 if (p is null || size == 0) 2625 return; 2626 return locked!(void, () { 2627 assert (Invariant()); scope (exit) assert (Invariant()); 2628 if (gc.ranges.append(Range(p, p + size)) is null) 2629 onOutOfMemoryError(); 2630 })(); 2631 } 2632 2633 void gc_removeRoot(void* p) 2634 { 2635 if (p is null) 2636 return; 2637 return locked!(void, () { 2638 assert (Invariant()); scope (exit) assert (Invariant()); 2639 bool r = gc.roots.remove(p); 2640 assert (r); 2641 })(); 2642 } 2643 2644 void gc_removeRange(void* p) 2645 { 2646 if (p is null) 2647 return; 2648 return locked!(void, () { 2649 assert (Invariant()); scope (exit) assert (Invariant()); 2650 bool r = gc.ranges.remove(Range(p, null)); 2651 assert (r); 2652 })(); 2653 } 2654 2655 void* gc_weakpointerCreate(Object r) 2656 { 2657 // weakpointers do their own locking 2658 return weakpointerCreate(r); 2659 } 2660 2661 void gc_weakpointerDestroy(void* wp) 2662 { 2663 // weakpointers do their own locking 2664 weakpointerDestroy(wp); 2665 } 2666 2667 Object gc_weakpointerGet(void* wp) 2668 { 2669 // weakpointers do their own locking 2670 return weakpointerGet(wp); 2671 } 2672 2673 private alias extern(D) void delegate() begin_del; 2674 private alias extern(D) void delegate(int, int) end_del; 2675 void gc_monitor(begin_del begin, end_del end) 2676 { 2677 locked!(void, () { 2678 //casts are a workaround for a dmdfe bug present in 1.064, but fixed in 1.066 2679 gc.collect_begin_cb = cast(typeof(gc.collect_begin_cb)) begin; 2680 gc.collect_end_cb = cast(typeof(gc.collect_end_cb)) end; 2681 })(); 2682 } 2683 2684 2685 // vim: set et sw=4 sts=4 :