- // Try collecting
- freedpages = fullcollectshell();
- if (freedpages >= pools.length * ((POOLSIZE / PAGESIZE) / 4))
- {
- state = 1;
- continue;
- }
- // Release empty pools to prevent bloat
- minimize();
- // Allocate new pool
- pool = newPool(npages);
- if (!pool)
- {
- state = 2;
- continue;
- }
- pn = pool.allocPages(npages);
- assert(pn != OPFAIL);
- goto L1;
- case 1:
- // Release empty pools to prevent bloat
- minimize();
- // Allocate new pool
- pool = newPool(npages);
- if (!pool)
- goto Lnomemory;
- pn = pool.allocPages(npages);
- assert(pn != OPFAIL);
- goto L1;
- case 2:
- goto Lnomemory;
- default:
- assert(false);
- }
- }
-
- L1:
- pool.pagetable[pn] = B_PAGE;
- if (npages > 1)
- memset(&pool.pagetable[pn + 1], B_PAGEPLUS, npages - 1);
- p = pool.baseAddr + pn * PAGESIZE;
- memset(cast(char *)p + size, 0, npages * PAGESIZE - size);
- if (opts.options.mem_stomp)
- memset(p, 0xF1, size);
- return p;
-
- Lnomemory:
- return null; // let mallocNoSync handle the error
- }
-
-
- /**
- * Allocate a new pool with at least npages in it.
- * Sort it into pools.
- * Return null if failed.
- */
- Pool *newPool(size_t npages)
- {
- // Minimum of POOLSIZE
- if (npages < POOLSIZE/PAGESIZE)
- npages = POOLSIZE/PAGESIZE;
- else if (npages > POOLSIZE/PAGESIZE)
- {
- // Give us 150% of requested size, so there's room to extend
- auto n = npages + (npages >> 1);
- if (n < size_t.max/PAGESIZE)
- npages = n;
- }
-
- // Allocate successively larger pools up to 8 megs
- if (pools.length)
- {
- size_t n = pools.length;
- if (n > 8)
- n = 8; // cap pool size at 8 megs
- n *= (POOLSIZE / PAGESIZE);
- if (npages < n)
- npages = n;
- }
-
- Pool p;
- p.initialize(npages);
- if (!p.baseAddr)
- {
- p.Dtor();
- return null;
- }
-
- Pool* pool = pools.insert_sorted(p);
- if (pool)
- {
- minAddr = pools[0].baseAddr;
- maxAddr = pools[pools.length - 1].topAddr;
- }
- return pool;
- }
-
-
- /**
- * Allocate a page of bin's.
- * Returns:
- * 0 failed
- */
- int allocPage(Bins bin)
- {
- Pool* pool;
- size_t n;
- size_t pn;
- byte* p;
- byte* ptop;
-
- for (n = 0; n < pools.length; n++)
- {
- pool = pools[n];
- pn = pool.allocPages(1);
- if (pn != OPFAIL)
- goto L1;
- }
- return 0; // failed
-
- L1:
- pool.pagetable[pn] = cast(ubyte)bin;
-
- // Convert page to free list
- size_t size = binsize[bin];
- List **b = &bucket[bin];
-
- p = pool.baseAddr + pn * PAGESIZE;
- ptop = p + PAGESIZE;
- for (; p < ptop; p += size)
- {
- (cast(List *)p).next = *b;
- *b = cast(List *)p;
- }
- return 1;
- }
-
-
- /**
- * Marks a range of memory using the conservative bit mask. Used for
- * the stack, for the data segment, and additional memory ranges.
- */
- void mark_conservative(void* pbot, void* ptop)
- {
- mark(pbot, ptop, PointerMap.init.bits.ptr);
- }
-
-
- /**
- * Search a range of memory values and mark any pointers into the GC pool.
- */
- void mark(void *pbot, void *ptop, size_t* pm_bitmask)
- {
- const BITS_PER_WORD = size_t.sizeof * 8;
-
- void **p1 = cast(void **)pbot;
- void **p2 = cast(void **)ptop;
- size_t pcache = 0;
- uint changes = 0;
-
- size_t type_size = pm_bitmask[0];
- size_t* pm_bits = pm_bitmask + 1;
-
- //printf("marking range: %p -> %p\n", pbot, ptop);
- for (; p1 + type_size <= p2; p1 += type_size) {
- for (size_t n = 0; n < type_size; n++) {
- // scan bit set for this word
- if (!(pm_bits[n / BITS_PER_WORD] & (1 << (n % BITS_PER_WORD))))
- continue;
-
- void* p = *(p1 + n);
-
- if (p < minAddr || p >= maxAddr)
- continue;
-
- if ((cast(size_t)p & ~(PAGESIZE-1)) == pcache)
- continue;
-
- Pool* pool = findPool(p);
- if (pool)
- {
- size_t offset = cast(size_t)(p - pool.baseAddr);
- size_t bit_i;
- size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pool.pagetable[pn];
-
- // Adjust bit to be at start of allocated memory block
- if (bin <= B_PAGE)
- bit_i = (offset & notbinsize[bin]) >> 4;
- else if (bin == B_PAGEPLUS)
- {
- do
- {
- --pn;
- }
- while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
- bit_i = pn * (PAGESIZE / 16);
- }
- else
- {
- // Don't mark bits in B_FREE pages
- continue;
- }
-
- if (bin >= B_PAGE) // Cache B_PAGE and B_PAGEPLUS lookups
- pcache = cast(size_t)p & ~(PAGESIZE-1);
-
- if (!pool.mark.test(bit_i))
- {
- pool.mark.set(bit_i);
- if (!pool.noscan.test(bit_i))
- {
- pool.scan.set(bit_i);
- changes = 1;
- }
- }
- }
- }
- }
- anychanges |= changes;
- }
-
- /**
- * Return number of full pages free'd.
- */
- size_t fullcollectshell()
- {
- stats.collection_started();
- scope (exit)
- stats.collection_finished();
-
- // The purpose of the 'shell' is to ensure all the registers
- // get put on the stack so they'll be scanned
- void *sp;
- size_t result;
- version (GNU)
- {
- gcc.builtins.__builtin_unwind_init();
- sp = & sp;
- }
- else version(LDC)
- {
- version(X86)
- {
- uint eax,ecx,edx,ebx,ebp,esi,edi;
- asm
- {
- mov eax[EBP], EAX ;
- mov ecx[EBP], ECX ;
- mov edx[EBP], EDX ;
- mov ebx[EBP], EBX ;
- mov ebp[EBP], EBP ;
- mov esi[EBP], ESI ;
- mov edi[EBP], EDI ;
- mov sp[EBP], ESP ;
- }
- }
- else version (X86_64)
- {
- ulong rax,rbx,rcx,rdx,rbp,rsi,rdi,r8,r9,r10,r11,r12,r13,r14,r15;
- asm
- {
- movq rax[RBP], RAX ;
- movq rbx[RBP], RBX ;
- movq rcx[RBP], RCX ;
- movq rdx[RBP], RDX ;
- movq rbp[RBP], RBP ;
- movq rsi[RBP], RSI ;
- movq rdi[RBP], RDI ;
- movq r8 [RBP], R8 ;
- movq r9 [RBP], R9 ;
- movq r10[RBP], R10 ;
- movq r11[RBP], R11 ;
- movq r12[RBP], R12 ;
- movq r13[RBP], R13 ;
- movq r14[RBP], R14 ;
- movq r15[RBP], R15 ;
- movq sp[RBP], RSP ;
- }
- }
- else
- {
- static assert( false, "Architecture not supported." );
- }
- }
- else
- {
- asm
- {
- pushad ;
- mov sp[EBP],ESP ;
- }
- }
- result = fullcollect(sp);
- version (GNU)
- {
- // nothing to do
- }
- else version(LDC)
- {
- // nothing to do
- }
- else
- {
- asm
- {
- popad ;
- }
- }
- return result;
- }
-
-
- /**
- *
- */
- size_t fullcollect(void *stackTop)
- {
- size_t n;
- Pool* pool;
-
- debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n");
-
- thread_suspendAll();
- stats.world_stopped();
-
- p_cache = null;
- size_cache = 0;
-
- anychanges = 0;
- for (n = 0; n < pools.length; n++)
- {
- pool = pools[n];
- pool.mark.zero();
- pool.scan.zero();
- pool.freebits.zero();
- }
-
- // Mark each free entry, so it doesn't get scanned
- for (n = 0; n < B_PAGE; n++)
- {
- for (List *list = bucket[n]; list; list = list.next)
- {
- pool = findPool(list);
- assert(pool);
- pool.freebits.set(cast(size_t)(cast(byte*)list - pool.baseAddr) / 16);
- }
- }
-
- for (n = 0; n < pools.length; n++)
- {
- pool = pools[n];
- pool.mark.copy(&pool.freebits);
- }
-
- rt_scanStaticData( &mark_conservative );
-
- if (!noStack)
- {
- // Scan stacks and registers for each paused thread
- thread_scanAll( &mark_conservative, stackTop );
- }
-
- // Scan roots
- debug(COLLECT_PRINTF) printf("scan roots[]\n");
- mark_conservative(roots.ptr, roots.ptr + roots.length);
-
- // Scan ranges
- debug(COLLECT_PRINTF) printf("scan ranges[]\n");
- for (n = 0; n < ranges.length; n++)
- {
- debug(COLLECT_PRINTF) printf("\t%x .. %x\n", ranges[n].pbot, ranges[n].ptop);
- mark_conservative(ranges[n].pbot, ranges[n].ptop);
- }
-
- debug(COLLECT_PRINTF) printf("\tscan heap\n");
- while (anychanges)
- {
- anychanges = 0;
- for (n = 0; n < pools.length; n++)
- {
- uint *bbase;
- uint *b;
- uint *btop;
-
- pool = pools[n];
-
- bbase = pool.scan.base();
- btop = bbase + pool.scan.nwords;
- for (b = bbase; b < btop;)
- {
- Bins bin;
- size_t pn;
- size_t u;
- size_t bitm;
- byte* o;
-
- bitm = *b;
- if (!bitm)
- {
- b++;
- continue;
- }
- *b = 0;
-
- o = pool.baseAddr + (b - bbase) * 32 * 16;
- if (!(bitm & 0xFFFF))
- {
- bitm >>= 16;
- o += 16 * 16;
- }
- for (; bitm; o += 16, bitm >>= 1)
- {
- if (!(bitm & 1))
- continue;
-
- pn = cast(size_t)(o - pool.baseAddr) / PAGESIZE;
- bin = cast(Bins)pool.pagetable[pn];
- if (bin < B_PAGE) {
- if (opts.options.conservative)
- mark_conservative(o, o + binsize[bin]);
- else {
- auto end_of_blk = cast(size_t**)(o +
- binsize[bin] - size_t.sizeof);
- size_t* pm_bitmask = *end_of_blk;
- mark(o, end_of_blk, pm_bitmask);
- }
- }
- else if (bin == B_PAGE || bin == B_PAGEPLUS)
- {
- if (bin == B_PAGEPLUS)
- {
- while (pool.pagetable[pn - 1] != B_PAGE)
- pn--;
- }
- u = 1;
- while (pn + u < pool.npages &&
- pool.pagetable[pn + u] == B_PAGEPLUS)
- u++;
-
- size_t blk_size = u * PAGESIZE;
- if (opts.options.conservative)
- mark_conservative(o, o + blk_size);
- else {
- auto end_of_blk = cast(size_t**)(o + blk_size -
- size_t.sizeof);
- size_t* pm_bitmask = *end_of_blk;
- mark(o, end_of_blk, pm_bitmask);
- }
- }
- }
- }
- }
- }
-
- thread_resumeAll();
- stats.world_started();
-
- // Free up everything not marked
- debug(COLLECT_PRINTF) printf("\tfree'ing\n");
- size_t freedpages = 0;
- size_t freed = 0;
- for (n = 0; n < pools.length; n++)
- {
- pool = pools[n];
- uint* bbase = pool.mark.base();
- size_t pn;
- for (pn = 0; pn < pool.npages; pn++, bbase += PAGESIZE / (32 * 16))
- {
- Bins bin = cast(Bins)pool.pagetable[pn];
-
- if (bin < B_PAGE)
- {
- auto size = binsize[bin];
- byte* p = pool.baseAddr + pn * PAGESIZE;
- byte* ptop = p + PAGESIZE;
- size_t bit_i = pn * (PAGESIZE/16);
- size_t bit_stride = size / 16;
-
- version(none) // BUG: doesn't work because freebits() must also be cleared
- {
- // If free'd entire page
- if (bbase[0] == 0 && bbase[1] == 0 && bbase[2] == 0 &&
- bbase[3] == 0 && bbase[4] == 0 && bbase[5] == 0 &&
- bbase[6] == 0 && bbase[7] == 0)
- {
- for (; p < ptop; p += size, bit_i += bit_stride)
- {
- if (pool.finals.nbits && pool.finals.testClear(bit_i)) {
- if (opts.options.sentinel)
- rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/);
- else
- rt_finalize(cast(List *)p, false/*noStack > 0*/);
- }
- this.clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
-
- List *list = cast(List *)p;
-
- if (opts.options.mem_stomp)
- memset(p, 0xF3, size);
- }
- pool.pagetable[pn] = B_FREE;
- freed += PAGESIZE;
- continue;
- }
- }
- for (; p < ptop; p += size, bit_i += bit_stride)
- {
- if (!pool.mark.test(bit_i))
- {
- if (opts.options.sentinel)
- sentinel_Invariant(sentinel_add(p));
-
- pool.freebits.set(bit_i);
- if (pool.finals.nbits && pool.finals.testClear(bit_i)) {
- if (opts.options.sentinel)
- rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/);
- else
- rt_finalize(cast(List *)p, false/*noStack > 0*/);
- }
- clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
-
- List *list = cast(List *)p;
-
- if (opts.options.mem_stomp)
- memset(p, 0xF3, size);
-
- freed += size;
- }
- }
- }
- else if (bin == B_PAGE)
- {
- size_t bit_i = pn * (PAGESIZE / 16);
- if (!pool.mark.test(bit_i))
- {
- byte *p = pool.baseAddr + pn * PAGESIZE;
- if (opts.options.sentinel)
- sentinel_Invariant(sentinel_add(p));
- if (pool.finals.nbits && pool.finals.testClear(bit_i)) {
- if (opts.options.sentinel)
- rt_finalize(sentinel_add(p), false/*noStack > 0*/);
- else
- rt_finalize(p, false/*noStack > 0*/);
- }
- clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
-
- debug(COLLECT_PRINTF) printf("\tcollecting big %x\n", p);
- pool.pagetable[pn] = B_FREE;
- freedpages++;
- if (opts.options.mem_stomp)
- memset(p, 0xF3, PAGESIZE);
- while (pn + 1 < pool.npages && pool.pagetable[pn + 1] == B_PAGEPLUS)
- {
- pn++;
- pool.pagetable[pn] = B_FREE;
- freedpages++;
-
- if (opts.options.mem_stomp)
- {
- p += PAGESIZE;
- memset(p, 0xF3, PAGESIZE);
- }
- }
- }
- }
- }
- }
-
- // Zero buckets
- bucket[] = null;
-
- // Free complete pages, rebuild free list
- debug(COLLECT_PRINTF) printf("\tfree complete pages\n");
- size_t recoveredpages = 0;
- for (n = 0; n < pools.length; n++)
- {
- pool = pools[n];
- for (size_t pn = 0; pn < pool.npages; pn++)
- {
- Bins bin = cast(Bins)pool.pagetable[pn];
- size_t bit_i;
- size_t u;
-
- if (bin < B_PAGE)
- {
- size_t size = binsize[bin];
- size_t bit_stride = size / 16;
- size_t bit_base = pn * (PAGESIZE / 16);
- size_t bit_top = bit_base + (PAGESIZE / 16);
- byte* p;
-
- bit_i = bit_base;
- for (; bit_i < bit_top; bit_i += bit_stride)
- {
- if (!pool.freebits.test(bit_i))
- goto Lnotfree;
- }
- pool.pagetable[pn] = B_FREE;
- recoveredpages++;
- continue;
-
- Lnotfree:
- p = pool.baseAddr + pn * PAGESIZE;
- for (u = 0; u < PAGESIZE; u += size)
- {
- bit_i = bit_base + u / 16;
- if (pool.freebits.test(bit_i))
- {
- List *list = cast(List *)(p + u);
- // avoid unnecessary writes
- if (list.next != bucket[bin])
- list.next = bucket[bin];
- bucket[bin] = list;
- }
- }
- }
- }
- }
-
- debug(COLLECT_PRINTF) printf("recovered pages = %d\n", recoveredpages);
- debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, pools.length);
-
- return freedpages + recoveredpages;
- }
-
-
- /**
- *
- */
- uint getAttr(Pool* pool, size_t bit_i)
- in
- {
- assert( pool );
- }
- body
- {
- uint attrs;
-
- if (pool.finals.nbits &&
- pool.finals.test(bit_i))
- attrs |= BlkAttr.FINALIZE;
- if (pool.noscan.test(bit_i))
- attrs |= BlkAttr.NO_SCAN;
-// if (pool.nomove.nbits &&
-// pool.nomove.test(bit_i))
-// attrs |= BlkAttr.NO_MOVE;
- return attrs;
- }
-
-
- /**
- *
- */
- void setAttr(Pool* pool, size_t bit_i, uint mask)
- in
- {
- assert( pool );
- }
- body
- {
- if (mask & BlkAttr.FINALIZE)
- {
- if (!pool.finals.nbits)
- pool.finals.alloc(pool.mark.nbits);
- pool.finals.set(bit_i);
- }
- if (mask & BlkAttr.NO_SCAN)
- {
- pool.noscan.set(bit_i);
- }
-// if (mask & BlkAttr.NO_MOVE)
-// {
-// if (!pool.nomove.nbits)
-// pool.nomove.alloc(pool.mark.nbits);
-// pool.nomove.set(bit_i);
-// }
- }
-
-
- /**
- *
- */
- void clrAttr(Pool* pool, size_t bit_i, uint mask)
- in
- {
- assert( pool );
- }
- body
- {
- if (mask & BlkAttr.FINALIZE && pool.finals.nbits)
- pool.finals.clear(bit_i);
- if (mask & BlkAttr.NO_SCAN)
- pool.noscan.clear(bit_i);
-// if (mask & BlkAttr.NO_MOVE && pool.nomove.nbits)
-// pool.nomove.clear(bit_i);
- }
-
-
-
- void initialize()
- {
- int dummy;
- stackBottom = cast(char*)&dummy;
- opts.parse(cstdlib.getenv("D_GC_OPTS"));
- lock = GCLock.classinfo;
- inited = 1;
- setStackBottom(rt_stackBottom());
- stats = Stats(this);
- }
-
-
- /**
- *
- */
- void enable()
- {
- if (!thread_needLock())
- {
- assert(this.disabled > 0);
- this.disabled--;
- }
- else synchronized (lock)
- {
- assert(this.disabled > 0);
- this.disabled--;
- }
- }
-
-
- /**
- *
- */
- void disable()
- {
- if (!thread_needLock())
- {
- this.disabled++;
- }
- else synchronized (lock)
- {
- this.disabled++;
- }
- }
-
-
- /**
- *
- */
- uint getAttr(void* p)
- {
- if (!p)
- {
- return 0;
- }
-
- uint go()
- {
- Pool* pool = this.findPool(p);
- uint old_attrs = 0;
-
- if (pool)
- {
- auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
-
- old_attrs = this.getAttr(pool, bit_i);
- }
- return old_attrs;
- }
-
- if (!thread_needLock())
- {
- return go();
- }
- else synchronized (lock)
- {
- return go();
- }
- }
-
-
- /**
- *
- */
- uint setAttr(void* p, uint mask)
- {
- if (!p)
- {
- return 0;
- }
-
- uint go()
- {
- Pool* pool = this.findPool(p);
- uint old_attrs = 0;
-
- if (pool)
- {
- auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
-
- old_attrs = this.getAttr(pool, bit_i);
- this.setAttr(pool, bit_i, mask);
- }
- return old_attrs;
- }
-
- if (!thread_needLock())
- {
- return go();
- }
- else synchronized (lock)
- {
- return go();
- }
- }
-
-
- /**
- *
- */
- uint clrAttr(void* p, uint mask)
- {
- if (!p)
- {
- return 0;
- }
-
- uint go()
- {
- Pool* pool = this.findPool(p);
- uint old_attrs = 0;
-
- if (pool)
- {
- auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
-
- old_attrs = this.getAttr(pool, bit_i);
- this.clrAttr(pool, bit_i, mask);
- }
- return old_attrs;
- }
-
- if (!thread_needLock())
- {
- return go();
- }
- else synchronized (lock)
- {
- return go();
- }
- }
-
-
- /**
- *
- */
- void *malloc(size_t size, uint attrs, PointerMap ptrmap)
- {
- if (!size)
- {
- return null;
- }
-
- if (!thread_needLock())
- {
- return mallocNoSync(size, attrs, ptrmap.bits.ptr);
- }
- else synchronized (lock)
- {
- return mallocNoSync(size, attrs, ptrmap.bits.ptr);
- }
- }
-
-
- //
- //
- //
- private void *mallocNoSync(size_t size, uint attrs, size_t* pm_bitmask)
- {
- assert(size != 0);
-
- stats.malloc_started(size, attrs, pm_bitmask);
- scope (exit)
- stats.malloc_finished(p);
-
- void *p = null;
- Bins bin;