1 // -*- c-basic-offset: 2 -*-
3 * This file is part of the KDE libraries
4 * Copyright (C) 2003 Apple Computer, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "collector.h"
28 #include <CoreFoundation/CoreFoundation.h>
32 #include <collector.h>
38 #include <mach/mach_port.h>
39 #include <mach/task.h>
40 #include <mach/thread_act.h>
46 const int MINIMUM_CELL_SIZE = 56;
47 const int BLOCK_SIZE = (8 * 4096);
48 const int SPARE_EMPTY_BLOCKS = 2;
49 const int MIN_ARRAY_SIZE = 14;
50 const int GROWTH_FACTOR = 2;
51 const int LOW_WATER_FACTOR = 4;
52 const int ALLOCATIONS_PER_COLLECTION = 1000;
55 const int CELL_ARRAY_LENGTH = (MINIMUM_CELL_SIZE / sizeof(double)) + (MINIMUM_CELL_SIZE % sizeof(double) != 0 ? sizeof(double) : 0);
56 const int CELL_SIZE = CELL_ARRAY_LENGTH * sizeof(double);
57 const int CELLS_PER_BLOCK = ((BLOCK_SIZE * 8 - sizeof(int32_t) * 8 - sizeof(void *) * 8) / (CELL_SIZE * 8));
61 struct CollectorCell {
63 double memory[CELL_ARRAY_LENGTH];
72 struct CollectorBlock {
73 CollectorCell cells[CELLS_PER_BLOCK];
75 CollectorCell *freeList;
78 struct CollectorHeap {
79 CollectorBlock **blocks;
82 int firstBlockWithPossibleSpace;
84 CollectorCell **oversizeCells;
86 int usedOversizeCells;
89 int numAllocationsSinceLastCollect;
92 static CollectorHeap heap = {NULL, 0, 0, 0, NULL, 0, 0, 0, 0};
94 bool Collector::memoryFull = false;
96 void* Collector::allocate(size_t s)
98 assert(Interpreter::lockCount() > 0);
104 if (++heap.numAllocationsSinceLastCollect >= ALLOCATIONS_PER_COLLECTION) {
108 if (s > (unsigned)CELL_SIZE) {
109 // oversize allocator
110 if (heap.usedOversizeCells == heap.numOversizeCells) {
111 heap.numOversizeCells = MAX(MIN_ARRAY_SIZE, heap.numOversizeCells * GROWTH_FACTOR);
112 heap.oversizeCells = (CollectorCell **)realloc(heap.oversizeCells, heap.numOversizeCells * sizeof(CollectorCell *));
115 void *newCell = malloc(s);
116 heap.oversizeCells[heap.usedOversizeCells] = (CollectorCell *)newCell;
117 heap.usedOversizeCells++;
118 heap.numLiveObjects++;
120 #if !USE_CONSERVATIVE_GC
121 ((ValueImp *)(newCell))->_flags = 0;
128 CollectorBlock *targetBlock = NULL;
131 for (i = heap.firstBlockWithPossibleSpace; i < heap.usedBlocks; i++) {
132 if (heap.blocks[i]->usedCells < CELLS_PER_BLOCK) {
133 targetBlock = heap.blocks[i];
138 heap.firstBlockWithPossibleSpace = i;
140 if (targetBlock == NULL) {
141 // didn't find one, need to allocate a new block
143 if (heap.usedBlocks == heap.numBlocks) {
144 heap.numBlocks = MAX(MIN_ARRAY_SIZE, heap.numBlocks * GROWTH_FACTOR);
145 heap.blocks = (CollectorBlock **)realloc(heap.blocks, heap.numBlocks * sizeof(CollectorBlock *));
148 targetBlock = (CollectorBlock *)calloc(1, sizeof(CollectorBlock));
149 targetBlock->freeList = targetBlock->cells;
150 heap.blocks[heap.usedBlocks] = targetBlock;
154 // find a free spot in the block and detach it from the free list
155 CollectorCell *newCell = targetBlock->freeList;
157 if (newCell->u.freeCell.next != NULL) {
158 targetBlock->freeList = newCell->u.freeCell.next;
159 } else if (targetBlock->usedCells == (CELLS_PER_BLOCK - 1)) {
160 // last cell in this block
161 targetBlock->freeList = NULL;
163 // all next pointers are initially 0, meaning "next cell"
164 targetBlock->freeList = newCell + 1;
167 targetBlock->usedCells++;
168 heap.numLiveObjects++;
170 #if !USE_CONSERVATIVE_GC
171 ((ValueImp *)(newCell))->_flags = 0;
173 return (void *)(newCell);
176 #if TEST_CONSERVATIVE_GC || USE_CONSERVATIVE_GC
178 struct Collector::Thread {
179 Thread(pthread_t pthread, mach_port_t mthread) : posixThread(pthread), machThread(mthread) {}
181 pthread_t posixThread;
182 mach_port_t machThread;
185 pthread_key_t registeredThreadKey;
186 pthread_once_t registeredThreadKeyOnce = PTHREAD_ONCE_INIT;
187 Collector::Thread *registeredThreads;
189 static void destroyRegisteredThread(void *data)
191 Collector::Thread *thread = (Collector::Thread *)data;
193 if (registeredThreads == thread) {
194 registeredThreads = registeredThreads->next;
196 Collector::Thread *last = registeredThreads;
197 for (Collector::Thread *t = registeredThreads->next; t != NULL; t = t->next) {
199 last->next = t->next;
209 static void initializeRegisteredThreadKey()
211 pthread_key_create(®isteredThreadKey, destroyRegisteredThread);
214 void Collector::registerThread()
216 pthread_once(®isteredThreadKeyOnce, initializeRegisteredThreadKey);
218 if (!pthread_getspecific(registeredThreadKey)) {
219 pthread_t pthread = pthread_self();
220 Collector::Thread *thread = new Collector::Thread(pthread, pthread_mach_thread_np(pthread));
221 thread->next = registeredThreads;
222 registeredThreads = thread;
223 pthread_setspecific(registeredThreadKey, thread);
228 // cells are 8-byte aligned
229 #define IS_POINTER_ALIGNED(p) (((int)(p) & 7) == 0)
231 void Collector::markStackObjectsConservatively(void *start, void *end)
239 assert(((char *)end - (char *)start) < 0x1000000);
240 assert(IS_POINTER_ALIGNED(start));
241 assert(IS_POINTER_ALIGNED(end));
243 char **p = (char **)start;
244 char **e = (char **)end;
248 if (IS_POINTER_ALIGNED(x) && x) {
250 for (int block = 0; block < heap.usedBlocks; block++) {
251 size_t offset = x - (char *)heap.blocks[block];
252 const size_t lastCellOffset = sizeof(CollectorCell) * (CELLS_PER_BLOCK - 1);
253 if (offset <= lastCellOffset && offset % sizeof(CollectorCell) == 0) {
260 int n = heap.usedOversizeCells;
261 for (int i = 0; i != n; i++) {
262 if (x == (char *)heap.oversizeCells[i]) {
269 if (good && ((CollectorCell *)x)->u.freeCell.zeroIfFree != 0) {
270 ValueImp *imp = (ValueImp *)x;
278 void Collector::markCurrentThreadConservatively()
283 pthread_t thread = pthread_self();
284 void *stackBase = pthread_get_stackaddr_np(thread);
286 void *stackPointer = &dummy;
287 markStackObjectsConservatively(stackPointer, stackBase);
290 typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit
292 void Collector::markOtherThreadConservatively(Thread *thread)
294 thread_suspend(thread->machThread);
296 #if defined(__i386__)
297 i386_thread_state_t regs;
298 unsigned user_count = sizeof(regs)/sizeof(int);
299 thread_state_flavor_t flavor = i386_THREAD_STATE;
300 #elif defined(__ppc__)
301 ppc_thread_state_t regs;
302 unsigned user_count = PPC_THREAD_STATE_COUNT;
303 thread_state_flavor_t flavor = PPC_THREAD_STATE;
304 #elif defined(__ppc64__)
305 ppc_thread_state64_t regs;
306 unsigned user_count = PPC_THREAD_STATE64_COUNT;
307 thread_state_flavor_t flavor = PPC_THREAD_STATE64;
309 #error Unknown Architecture
311 // get the thread register state
312 thread_get_state(thread->machThread, flavor, (thread_state_t)®s, &user_count);
314 // scan the registers
315 markStackObjectsConservatively((void *)®s, (void *)((char *)®s + (user_count * sizeof(usword_t))));
318 #if defined(__i386__)
319 markStackObjectsConservatively((void *)regs.esp, pthread_get_stackaddr_np(thread->posixThread));
320 #elif defined(__ppc__) || defined(__ppc64__)
321 markStackObjectsConservatively((void *)regs.r1, pthread_get_stackaddr_np(thread->posixThread));
323 #error Unknown Architecture
326 thread_resume(thread->machThread);
329 void Collector::markStackObjectsConservatively()
331 markCurrentThreadConservatively();
333 for (Thread *thread = registeredThreads; thread != NULL; thread = thread->next) {
334 if (thread->posixThread != pthread_self()) {
335 markOtherThreadConservatively(thread);
340 void Collector::markProtectedObjects()
342 for (int i = 0; i < ProtectedValues::_tableSize; i++) {
343 ValueImp *val = ProtectedValues::_table[i].key;
344 if (val && !val->marked()) {
352 bool Collector::collect()
354 assert(Interpreter::lockCount() > 0);
356 bool deleted = false;
358 #if TEST_CONSERVATIVE_GC
359 // CONSERVATIVE MARK: mark the root set using conservative GC bit (will compare later)
360 ValueImp::useConservativeMark(true);
363 #if USE_CONSERVATIVE_GC || TEST_CONSERVATIVE_GC
364 if (InterpreterImp::s_hook) {
365 InterpreterImp *scr = InterpreterImp::s_hook;
367 //fprintf( stderr, "Collector marking interpreter %p\n",(void*)scr);
370 } while (scr != InterpreterImp::s_hook);
373 markStackObjectsConservatively();
374 markProtectedObjects();
377 #if TEST_CONSERVATIVE_GC
378 ValueImp::useConservativeMark(false);
381 #if !USE_CONSERVATIVE_GC
382 // MARK: first mark all referenced objects recursively
383 // starting out from the set of root objects
384 if (InterpreterImp::s_hook) {
385 InterpreterImp *scr = InterpreterImp::s_hook;
387 //fprintf( stderr, "Collector marking interpreter %p\n",(void*)scr);
390 } while (scr != InterpreterImp::s_hook);
393 // mark any other objects that we wouldn't delete anyway
394 for (int block = 0; block < heap.usedBlocks; block++) {
396 int minimumCellsToProcess = heap.blocks[block]->usedCells;
397 CollectorBlock *curBlock = heap.blocks[block];
399 for (int cell = 0; cell < CELLS_PER_BLOCK; cell++) {
400 if (minimumCellsToProcess < cell) {
401 goto skip_block_mark;
404 ValueImp *imp = (ValueImp *)(curBlock->cells + cell);
406 if (((CollectorCell *)imp)->u.freeCell.zeroIfFree != 0) {
408 if ((imp->_flags & (ValueImp::VI_CREATED|ValueImp::VI_MARKED)) == ValueImp::VI_CREATED &&
409 ((imp->_flags & ValueImp::VI_GCALLOWED) == 0 || imp->refcount != 0)) {
413 minimumCellsToProcess++;
419 for (int cell = 0; cell < heap.usedOversizeCells; cell++) {
420 ValueImp *imp = (ValueImp *)heap.oversizeCells[cell];
421 if ((imp->_flags & (ValueImp::VI_CREATED|ValueImp::VI_MARKED)) == ValueImp::VI_CREATED &&
422 ((imp->_flags & ValueImp::VI_GCALLOWED) == 0 || imp->refcount != 0)) {
428 // SWEEP: delete everything with a zero refcount (garbage) and unmark everything else
432 for (int block = 0; block < heap.usedBlocks; block++) {
433 CollectorBlock *curBlock = heap.blocks[block];
435 int minimumCellsToProcess = curBlock->usedCells;
437 for (int cell = 0; cell < CELLS_PER_BLOCK; cell++) {
438 if (minimumCellsToProcess < cell) {
439 goto skip_block_sweep;
442 ValueImp *imp = (ValueImp *)(curBlock->cells + cell);
444 if (((CollectorCell *)imp)->u.freeCell.zeroIfFree != 0) {
445 #if USE_CONSERVATIVE_GC
448 if (!imp->refcount && imp->_flags == (ValueImp::VI_GCALLOWED | ValueImp::VI_CREATED))
451 //fprintf( stderr, "Collector::deleting ValueImp %p (%s)\n", (void*)imp, typeid(*imp).name());
452 // emulate destructing part of 'operator delete()'
454 curBlock->usedCells--;
455 heap.numLiveObjects--;
458 // put it on the free list
459 ((CollectorCell *)imp)->u.freeCell.zeroIfFree = 0;
460 ((CollectorCell *)imp)->u.freeCell.next = curBlock->freeList;
461 curBlock->freeList = (CollectorCell *)imp;
464 #if USE_CONSERVATIVE_GC
466 #elif TEST_CONSERVATIVE_GC
467 imp->_flags &= ~(ValueImp::VI_MARKED | ValueImp::VI_CONSERVATIVE_MARKED);
469 imp->_flags &= ~ValueImp::VI_MARKED;
473 minimumCellsToProcess++;
479 if (heap.blocks[block]->usedCells == 0) {
481 if (emptyBlocks > SPARE_EMPTY_BLOCKS) {
483 free(heap.blocks[block]);
485 // swap with the last block so we compact as we go
486 heap.blocks[block] = heap.blocks[heap.usedBlocks - 1];
488 block--; // Don't move forward a step in this case
490 if (heap.numBlocks > MIN_ARRAY_SIZE && heap.usedBlocks < heap.numBlocks / LOW_WATER_FACTOR) {
491 heap.numBlocks = heap.numBlocks / GROWTH_FACTOR;
492 heap.blocks = (CollectorBlock **)realloc(heap.blocks, heap.numBlocks * sizeof(CollectorBlock *));
499 heap.firstBlockWithPossibleSpace = 0;
503 while (cell < heap.usedOversizeCells) {
504 ValueImp *imp = (ValueImp *)heap.oversizeCells[cell];
506 #if USE_CONSERVATIVE_GC
509 if (!imp->refcount &&
510 imp->_flags == (ValueImp::VI_GCALLOWED | ValueImp::VI_CREATED)) {
515 heap.oversizeCells[cell]->u.freeCell.zeroIfFree = 0;
520 // swap with the last oversize cell so we compact as we go
521 heap.oversizeCells[cell] = heap.oversizeCells[heap.usedOversizeCells - 1];
523 heap.usedOversizeCells--;
525 heap.numLiveObjects--;
527 if (heap.numOversizeCells > MIN_ARRAY_SIZE && heap.usedOversizeCells < heap.numOversizeCells / LOW_WATER_FACTOR) {
528 heap.numOversizeCells = heap.numOversizeCells / GROWTH_FACTOR;
529 heap.oversizeCells = (CollectorCell **)realloc(heap.oversizeCells, heap.numOversizeCells * sizeof(CollectorCell *));
533 #if USE_CONSERVATIVE_GC
535 #elif TEST_CONSERVATIVE_GC
536 imp->_flags &= ~(ValueImp::VI_MARKED | ValueImp::VI_CONSERVATIVE_MARKED);
538 imp->_flags &= ~ValueImp::VI_MARKED;
544 heap.numAllocationsSinceLastCollect = 0;
546 memoryFull = (heap.numLiveObjects >= KJS_MEM_LIMIT);
551 int Collector::size()
553 return heap.numLiveObjects;
557 void Collector::finalCheck()
564 int Collector::numInterpreters()
567 if (InterpreterImp::s_hook) {
568 InterpreterImp *scr = InterpreterImp::s_hook;
572 } while (scr != InterpreterImp::s_hook);
577 int Collector::numGCNotAllowedObjects()
580 #if !USE_CONSERVATIVE_GC
581 for (int block = 0; block < heap.usedBlocks; block++) {
582 CollectorBlock *curBlock = heap.blocks[block];
584 for (int cell = 0; cell < CELLS_PER_BLOCK; cell++) {
585 ValueImp *imp = (ValueImp *)(curBlock->cells + cell);
587 if (((CollectorCell *)imp)->u.freeCell.zeroIfFree != 0 &&
588 (imp->_flags & ValueImp::VI_GCALLOWED) == 0) {
594 for (int cell = 0; cell < heap.usedOversizeCells; cell++) {
595 ValueImp *imp = (ValueImp *)heap.oversizeCells[cell];
596 if ((imp->_flags & ValueImp::VI_GCALLOWED) == 0) {
605 int Collector::numReferencedObjects()
609 #if USE_CONSERVATIVE_GC
610 for (int i = 0; i < ProtectedValues::_tableSize; i++) {
611 ValueImp *val = ProtectedValues::_table[i].key;
619 for (int block = 0; block < heap.usedBlocks; block++) {
620 CollectorBlock *curBlock = heap.blocks[block];
622 for (int cell = 0; cell < CELLS_PER_BLOCK; cell++) {
623 ValueImp *imp = (ValueImp *)(curBlock->cells + cell);
625 if (((CollectorCell *)imp)->u.freeCell.zeroIfFree != 0 &&
626 imp->refcount != 0) {
632 for (int cell = 0; cell < heap.usedOversizeCells; cell++) {
633 ValueImp *imp = (ValueImp *)heap.oversizeCells[cell];
634 if (imp->refcount != 0) {
643 const void *Collector::rootObjectClasses()
645 CFMutableSetRef classes = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
647 #if USE_CONSERVATIVE_GC
648 for (int i = 0; i < ProtectedValues::_tableSize; i++) {
649 ValueImp *val = ProtectedValues::_table[i].key;
651 const char *mangled_name = typeid(*val).name();
653 char *demangled_name = __cxxabiv1::__cxa_demangle (mangled_name, NULL, NULL, &status);
655 CFStringRef className = CFStringCreateWithCString(NULL, demangled_name, kCFStringEncodingASCII);
656 free(demangled_name);
657 CFSetAddValue(classes, className);
658 CFRelease(className);
662 for (int block = 0; block < heap.usedBlocks; block++) {
663 CollectorBlock *curBlock = heap.blocks[block];
664 for (int cell = 0; cell < CELLS_PER_BLOCK; cell++) {
665 ValueImp *imp = (ValueImp *)(curBlock->cells + cell);
667 if (((CollectorCell *)imp)->u.freeCell.zeroIfFree != 0 &&
668 ((imp->_flags & ValueImp::VI_GCALLOWED) == 0 || imp->refcount != 0)) {
669 const char *mangled_name = typeid(*imp).name();
671 char *demangled_name = __cxxabiv1::__cxa_demangle (mangled_name, NULL, NULL, &status);
673 CFStringRef className = CFStringCreateWithCString(NULL, demangled_name, kCFStringEncodingASCII);
674 free(demangled_name);
675 CFSetAddValue(classes, className);
676 CFRelease(className);
681 for (int cell = 0; cell < heap.usedOversizeCells; cell++) {
682 ValueImp *imp = (ValueImp *)heap.oversizeCells[cell];
684 if ((imp->_flags & ValueImp::VI_GCALLOWED) == 0 || imp->refcount != 0) {
685 const char *mangled_name = typeid(*imp).name();
687 char *demangled_name = __cxxabiv1::__cxa_demangle (mangled_name, NULL, NULL, &status);
689 CFStringRef className = CFStringCreateWithCString(NULL, demangled_name, kCFStringEncodingASCII);
690 free(demangled_name);
691 CFSetAddValue(classes, className);
692 CFRelease(className);
700 #endif // APPLE_CHANGES