int asCGarbageCollector::AddScriptObjectToGC(void *obj, asCObjectType *objType) { if( obj == 0 || objType == 0 ) { engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_GC_RECEIVED_NULL_PTR); return asINVALID_ARG; } engine->CallObjectMethod(obj, objType->beh.addref); asSObjTypePair ot = {obj, objType, 0}; // Invoke the garbage collector to destroy a little garbage as new comes in // This will maintain the number of objects in the GC at a maintainable level without // halting the application, and without burdening the application with manually invoking the // garbage collector. if( engine->ep.autoGarbageCollect && gcNewObjects.GetLength() ) { // If the GC is already processing in another thread, then don't try this again if( TRYENTERCRITICALSECTION(gcCollecting) ) { // Skip this if the GC is already running in this thread if( !isProcessing ) { isProcessing = true; // TODO: The number of iterations should be dynamic, and increase // if the number of objects in the garbage collector grows high // Run one step of DetectGarbage if( gcOldObjects.GetLength() ) { IdentifyGarbageWithCyclicRefs(); DestroyOldGarbage(); } // Run a few steps of DestroyGarbage int iter = (int)gcNewObjects.GetLength(); if( iter > 10 ) iter = 10; while( iter-- > 0 ) DestroyNewGarbage(); isProcessing = false; } LEAVECRITICALSECTION(gcCollecting); } } // Add the data to the gcObjects array in a critical section as // another thread might be calling this method at the same time ENTERCRITICALSECTION(gcCritical); ot.seqNbr = numAdded++; gcNewObjects.PushLast(ot); LEAVECRITICALSECTION(gcCritical); return ot.seqNbr; }
void asCGarbageCollector::AddScriptObjectToGC(void *obj, asCObjectType *objType) { engine->CallObjectMethod(obj, objType->beh.addref); asSObjTypePair ot = {obj, objType, 0}; // Invoke the garbage collector to destroy a little garbage as new comes in // This will maintain the number of objects in the GC at a maintainable level without // halting the application, and without burdening the application with manually invoking the // garbage collector. if( engine->ep.autoGarbageCollect && gcNewObjects.GetLength() ) { // If the GC is already processing in another thread, then don't try this again // TODO: What if it is already processing in this thread? if( TRYENTERCRITICALSECTION(gcCollecting) ) { // TODO: The number of iterations should be dynamic, and increase // if the number of objects in the garbage collector grows high // Run one step of DetectGarbage if( gcOldObjects.GetLength() ) { IdentifyGarbageWithCyclicRefs(); DestroyOldGarbage(); } // Run a few steps of DestroyGarbage int iter = (int)gcNewObjects.GetLength(); if( iter > 10 ) iter = 10; while( iter-- > 0 ) DestroyNewGarbage(); LEAVECRITICALSECTION(gcCollecting); } } // Add the data to the gcObjects array in a critical section as // another thread might be calling this method at the same time ENTERCRITICALSECTION(gcCritical); gcNewObjects.PushLast(ot); LEAVECRITICALSECTION(gcCritical); }
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::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; }
// TODO: Should have a flag to tell the garbage collector to automatically determine how many iterations are needed // It should then gather statistics such as how many objects has been created since last run, and how many objects // are destroyed per iteration, and how many objects are detected as cyclic garbage per iteration. // It should try to reach a stable number of objects, i.e. so that on average the number of objects added to // the garbage collector is the same as the number of objects destroyed. And it should try to minimize the number // of iterations of detections that must be executed per cycle while still identifying the cyclic garbage // These variables should also be available for inspection through the gcstatistics. int asCGarbageCollector::GarbageCollect(asDWORD flags, asUINT iterations) { // 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 new objects to the old list, so we guarantee that all is detected MoveAllObjectsToOldList(); detectState = clearCounters_init; } if( doDestroy ) { destroyNewState = destroyGarbage_init; destroyOldState = destroyGarbage_init; } // The full cycle only works with the objects in the old list so that the // set of objects scanned for garbage is fixed even if new objects are added // by other threads in parallel. unsigned int count = (unsigned int)(gcOldObjects.GetLength()); for(;;) { // Detect all garbage with cyclic references if( doDetect ) while( IdentifyGarbageWithCyclicRefs() == 1 ) {} // Now destroy all known garbage if( doDestroy ) { if( !doDetect ) while( DestroyNewGarbage() == 1 ) {} while( DestroyOldGarbage() == 1 ) {} } // Run another iteration if any garbage was destroyed if( count != (unsigned int)(gcOldObjects.GetLength()) ) count = (unsigned int)(gcOldObjects.GetLength()); else break; } isProcessing = false; LEAVECRITICALSECTION(gcCollecting); return 0; } else { while( iterations-- > 0 ) { // Destroy the garbage that we know of if( doDestroy ) { DestroyNewGarbage(); DestroyOldGarbage(); } // Run another incremental step of the identification of cyclic references if( doDetect && gcOldObjects.GetLength() > 0 ) IdentifyGarbageWithCyclicRefs(); } } isProcessing = false; LEAVECRITICALSECTION(gcCollecting); } // Return 1 to indicate that the cycle wasn't finished return 1; }