int asCGarbageCollector::DestroyNewGarbage() { for(;;) { switch( destroyNewState ) { case destroyGarbage_init: { // If there are no objects to be freed then don't start if( gcNewObjects.GetLength() == 0 ) return 0; destroyNewIdx = (asUINT)-1; destroyNewState = destroyGarbage_loop; } break; case destroyGarbage_loop: case destroyGarbage_haveMore: { // If the refCount has reached 1, then only the GC still holds a // reference to the object, thus we don't need to worry about the // application touching the objects during collection. // Destroy all objects that have refCount == 1. If any objects are // destroyed, go over the list again, because it may have made more // objects reach refCount == 1. if( ++destroyNewIdx < gcNewObjects.GetLength() ) { asSObjTypePair gcObj = GetNewObjectAtIdx(destroyNewIdx); if( engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount) == 1 ) { // Release the object immediately // Make sure the refCount is really 0, because the // destructor may have increased the refCount again. bool addRef = false; if( gcObj.type->flags & asOBJ_SCRIPT_OBJECT ) { // Script objects may actually be resurrected in the destructor int refCount = ((asCScriptObject*)gcObj.obj)->Release(); if( refCount > 0 ) addRef = true; } else engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.release); // Was the object really destroyed? if( !addRef ) { numDestroyed++; numNewDestroyed++; RemoveNewObjectAtIdx(destroyNewIdx); destroyNewIdx--; } else { // Since the object was resurrected in the // destructor, we must add our reference again engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.addref); } destroyNewState = destroyGarbage_haveMore; } else if( gcObj.count == 3 ) { // We've already verified this object multiple times. It is likely // to live for quite a long time so we'll move it to the list if old objects MoveObjectToOldList(destroyNewIdx); destroyNewIdx--; } else { // Increase the counter for the number of times the object has been verified IncreaseCounterForNewObject(destroyNewIdx); } // Allow the application to work a little return 1; } else { if( destroyNewState == destroyGarbage_haveMore ) { // Restart the cycle destroyNewState = destroyGarbage_init; } else { // Restart the cycle destroyNewState = destroyGarbage_init; // Return 0 to tell the application that there // is no more garbage to destroy at the moment return 0; } } } break; } } // Shouldn't reach this point UNREACHABLE_RETURN; }
int asCGarbageCollector::GarbageCollect(asDWORD flags) { // If the GC is already processing in another thread, then don't enter here again // TODO: What if it is already processing in this thread? if( TRYENTERCRITICALSECTION(gcCollecting) ) { bool doDetect = (flags & asGC_DETECT_GARBAGE) || !(flags & asGC_DESTROY_GARBAGE); bool doDestroy = (flags & asGC_DESTROY_GARBAGE) || !(flags & asGC_DETECT_GARBAGE); if( flags & asGC_FULL_CYCLE ) { // Reset the state if( doDetect ) { // Move all objects to the old list, so we guarantee that all is detected for( asUINT n = (asUINT)gcNewObjects.GetLength(); n-- > 0; ) MoveObjectToOldList(n); detectState = clearCounters_init; } if( doDestroy ) { destroyNewState = destroyGarbage_init; destroyOldState = destroyGarbage_init; } int r = 1; unsigned int count = (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()); for(;;) { // Detect all garbage with cyclic references if( doDetect ) while( (r = IdentifyGarbageWithCyclicRefs()) == 1 ); // Now destroy all known garbage if( doDestroy ) { while( (r = DestroyNewGarbage()) == 1 ); while( (r = DestroyOldGarbage()) == 1 ); } // Run another iteration if any garbage was destroyed if( count != (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()) ) count = (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()); else break; } // Take the opportunity to clear unused types as well engine->ClearUnusedTypes(); LEAVECRITICALSECTION(gcCollecting); return 0; } else { // Destroy the garbage that we know of if( doDestroy ) { DestroyNewGarbage(); DestroyOldGarbage(); } // Run another incremental step of the identification of cyclic references if( doDetect ) IdentifyGarbageWithCyclicRefs(); } LEAVECRITICALSECTION(gcCollecting); } // Return 1 to indicate that the cycle wasn't finished return 1; }
int asCGarbageCollector::GarbageCollect(asDWORD flags) { // If the GC is already processing in another thread, then don't enter here again if( TRYENTERCRITICALSECTION(gcCollecting) ) { // If the GC is already processing in this thread, then don't enter here again if( isProcessing ) { LEAVECRITICALSECTION(gcCollecting); return 1; } isProcessing = true; bool doDetect = (flags & asGC_DETECT_GARBAGE) || !(flags & asGC_DESTROY_GARBAGE); bool doDestroy = (flags & asGC_DESTROY_GARBAGE) || !(flags & asGC_DETECT_GARBAGE); if( flags & asGC_FULL_CYCLE ) { // Reset the state if( doDetect ) { // Move all objects to the old list, so we guarantee that all is detected for( asUINT n = (asUINT)gcNewObjects.GetLength(); n-- > 0; ) MoveObjectToOldList(n); detectState = clearCounters_init; } if( doDestroy ) { destroyNewState = destroyGarbage_init; destroyOldState = destroyGarbage_init; } unsigned int count = (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()); for(;;) { // Detect all garbage with cyclic references if( doDetect ) while( IdentifyGarbageWithCyclicRefs() == 1 ) {} // Now destroy all known garbage if( doDestroy ) { while( DestroyNewGarbage() == 1 ) {} while( DestroyOldGarbage() == 1 ) {} } // Run another iteration if any garbage was destroyed if( count != (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()) ) count = (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()); else { // Let the engine destroy the types that reached refCount 0 // If none were destroyed, then leave the GC // TODO: The asCObjectType should destroy its content when refCount reaches 0 // since no-one is using them. The registered types should have their // refcount increased by the config groups. Doing it like that will allow // me to remove this call to ClearUnusedTypes() that the GC really // shouldn't be calling. if( engine->ClearUnusedTypes() == 0 ) break; } } isProcessing = false; LEAVECRITICALSECTION(gcCollecting); return 0; } else { // Destroy the garbage that we know of if( doDestroy ) { DestroyNewGarbage(); DestroyOldGarbage(); } // Run another incremental step of the identification of cyclic references if( doDetect ) IdentifyGarbageWithCyclicRefs(); } isProcessing = false; LEAVECRITICALSECTION(gcCollecting); } // Return 1 to indicate that the cycle wasn't finished return 1; }
int asCGarbageCollector::DestroyNewGarbage() { // This function will only be called within the critical section gcCollecting asASSERT(isProcessing); for(;;) { switch( destroyNewState ) { case destroyGarbage_init: { // If there are no objects to be freed then don't start if( gcNewObjects.GetLength() == 0 ) return 0; // Update the seqAtSweepStart which is used to determine when // to move an object from the new set to the old set seqAtSweepStart[0] = seqAtSweepStart[1]; seqAtSweepStart[1] = seqAtSweepStart[2]; seqAtSweepStart[2] = numAdded; destroyNewIdx = (asUINT)-1; destroyNewState = destroyGarbage_loop; } break; case destroyGarbage_loop: case destroyGarbage_haveMore: { // If the refCount has reached 1, then only the GC still holds a // reference to the object, thus we don't need to worry about the // application touching the objects during collection. // Destroy all objects that have refCount == 1. If any objects are // destroyed, go over the list again, because it may have made more // objects reach refCount == 1. if( ++destroyNewIdx < gcNewObjects.GetLength() ) { asSObjTypePair gcObj = GetNewObjectAtIdx(destroyNewIdx); if( engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount) == 1 ) { // Release the object immediately // Make sure the refCount is really 0, because the // destructor may have increased the refCount again. bool addRef = false; if( gcObj.type->flags & asOBJ_SCRIPT_OBJECT ) { // Script objects may actually be resurrected in the destructor int refCount = ((asCScriptObject*)gcObj.obj)->Release(); if( refCount > 0 ) addRef = true; } else engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.release); // Was the object really destroyed? if( !addRef ) { numDestroyed++; numNewDestroyed++; RemoveNewObjectAtIdx(destroyNewIdx); destroyNewIdx--; } else { // Since the object was resurrected in the // destructor, we must add our reference again engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.addref); } destroyNewState = destroyGarbage_haveMore; } // Check if this object has been inspected 3 times already, and if so move it to the // set of old objects that are less likely to become garbage in a short time // TODO: Is 3 really a good value? Should the number of times be dynamic? else if( gcObj.seqNbr < seqAtSweepStart[0] ) { // We've already verified this object multiple times. It is likely // to live for quite a long time so we'll move it to the list if old objects MoveObjectToOldList(destroyNewIdx); destroyNewIdx--; } // Allow the application to work a little return 1; } else { if( destroyNewState == destroyGarbage_haveMore ) { // Restart the cycle destroyNewState = destroyGarbage_init; } else { // Restart the cycle destroyNewState = destroyGarbage_init; // Return 0 to tell the application that there // is no more garbage to destroy at the moment return 0; } } } break; } } // Shouldn't reach this point UNREACHABLE_RETURN; }