Ejemplo n.º 1
0
void asCGarbageCollector::ReportUndestroyedObjects()
{
	for( asUINT n = 0; n < gcOldObjects.GetLength(); n++ )
	{
		asSObjTypePair gcObj = GetOldObjectAtIdx(n);

		asCString msg;
		msg.Format(TXT_GC_CANNOT_FREE_OBJ_OF_TYPE_s, gcObj.type->name.AddressOf());
		engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());
	}
}
Ejemplo n.º 2
0
int asCGarbageCollector::ReportAndReleaseUndestroyedObjects()
{
	// This function will only be called as the engine is shutting down

	int items = 0;
	for( asUINT n = 0; n < gcOldObjects.GetLength(); n++ )
	{
		asSObjTypePair gcObj = GetOldObjectAtIdx(n);

		int refCount = 0;
		if( gcObj.type->beh.gcGetRefCount && engine->scriptFunctions[gcObj.type->beh.gcGetRefCount] )
			refCount = engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount);

		// Report the object as not being properly destroyed
		asCString msg;
		msg.Format(TXT_d_GC_CANNOT_FREE_OBJ_OF_TYPE_s_REF_COUNT_d, gcObj.seqNbr, gcObj.type->name.AddressOf(), refCount - 1);
		engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());

		// Add additional info for builtin types
		if( gcObj.type->name == "_builtin_function_" )
		{
			// Unfortunately we can't show the function declaration here, because the engine may have released the parameter list already so the declaration would only be misleading
			// We need to show the function type too as for example delegates do not have a name
			msg.Format(TXT_PREV_FUNC_IS_NAMED_s_TYPE_IS_d, reinterpret_cast<asCScriptFunction*>(gcObj.obj)->GetName(), reinterpret_cast<asCScriptFunction*>(gcObj.obj)->GetFuncType());
			engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf());
		}
		else if( gcObj.type->name == "_builtin_objecttype_" )
		{
			msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast<asCObjectType*>(gcObj.obj)->GetName());
			engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf());
		}
		else if( gcObj.type->name == "_builtin_globalprop_" )
		{
			msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast<asCGlobalProperty*>(gcObj.obj)->name.AddressOf());
			engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf());
		}

		// Release the reference that the GC holds if the release functions is still available
		if( gcObj.type->beh.release && engine->scriptFunctions[gcObj.type->beh.release] )
			engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.release);

		items++;
	}
	return items;
}
Ejemplo n.º 3
0
int asCGarbageCollector::ReportAndReleaseUndestroyedObjects()
{
	int items = 0;
	for( asUINT n = 0; n < gcOldObjects.GetLength(); n++ )
	{
		asSObjTypePair gcObj = GetOldObjectAtIdx(n);

		// Report the object as not being properly destroyed
		asCString msg;
		msg.Format(TXT_GC_CANNOT_FREE_OBJ_OF_TYPE_s, gcObj.type->name.AddressOf());
		engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());

		// Release the reference that the GC holds if the release functions is still available
		if( gcObj.type->beh.release && engine->scriptFunctions[gcObj.type->beh.release] )
			engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.release);

		items++;
	}
	return items;
}
Ejemplo n.º 4
0
int asCGarbageCollector::ReportAndReleaseUndestroyedObjects()
{
	int items = 0;
	for( asUINT n = 0; n < gcOldObjects.GetLength(); n++ )
	{
		asSObjTypePair gcObj = GetOldObjectAtIdx(n);

		int refCount = 0;
		if( gcObj.type->beh.gcGetRefCount && engine->scriptFunctions[gcObj.type->beh.gcGetRefCount] )
			refCount = engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount);

		// Report the object as not being properly destroyed
		asCString msg;
		msg.Format(TXT_GC_CANNOT_FREE_OBJ_OF_TYPE_s_REF_COUNT_d, gcObj.type->name.AddressOf(), refCount - 1);
		engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());

		// Add additional info for builtin types
		if( gcObj.type->name == "_builtin_function_" )
		{
			msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast<asCScriptFunction*>(gcObj.obj)->GetName());
			engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf());
		}
		else if( gcObj.type->name == "_builtin_objecttype_" )
		{
			msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast<asCObjectType*>(gcObj.obj)->GetName());
			engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf());
		}
		else if( gcObj.type->name == "_builtin_globalprop_" )
		{
			msg.Format(TXT_PREV_TYPE_IS_NAMED_s, reinterpret_cast<asCGlobalProperty*>(gcObj.obj)->name.AddressOf());
			engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, msg.AddressOf());
		}

		// Release the reference that the GC holds if the release functions is still available
		if( gcObj.type->beh.release && engine->scriptFunctions[gcObj.type->beh.release] )
			engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.release);

		items++;
	}
	return items;
}
Ejemplo n.º 5
0
int asCGarbageCollector::IdentifyGarbageWithCyclicRefs()
{
	for(;;)
	{
		switch( detectState )
		{
		case clearCounters_init:
			detectState = clearCounters_loop;
		break;

		case clearCounters_loop:
		{
			// Decrease reference counter for all objects removed from the map
			asSMapNode<void*, asSIntTypePair> *cursor = 0;
			gcMap.MoveFirst(&cursor);
			if( cursor )
			{
				void *obj = gcMap.GetKey(cursor);
				asSIntTypePair it = gcMap.GetValue(cursor);

				engine->CallObjectMethod(obj, it.type->beh.release);

				ReturnNode(gcMap.Remove(cursor));

				return 1;
			}

			detectState = buildMap_init;
		}
		break;

		case buildMap_init:
			detectIdx = 0;
			detectState = buildMap_loop;
		break;

		case buildMap_loop:
		{
			// Build a map of objects that will be checked, the map will
			// hold the object pointer as key, and the gcCount and the
			// object's type as value. As objects are added to the map the
			// gcFlag must be set in the objects, so we can be verify if
			// the object is accessed during the GC cycle.

			// If an object is removed from the gcObjects list during the
			// iteration of this step, it is possible that an object won't
			// be used during the analyzing for cyclic references. This
			// isn't a problem, as the next time the GC cycle starts the
			// object will be verified.
			if( detectIdx < gcOldObjects.GetLength() )
			{
				// Add the gc count for this object
				asSObjTypePair gcObj = GetOldObjectAtIdx(detectIdx);
	
				int refCount = 0;
				if( gcObj.type->beh.gcGetRefCount )
					refCount = engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount);

				if( refCount > 1 )
				{
					asSIntTypePair it = {refCount-1, gcObj.type};

					gcMap.Insert(GetNode(gcObj.obj, it));

					// Increment the object's reference counter when putting it in the map
					engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.addref);

					// Mark the object so that we can
					// see if it has changed since read
					engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.gcSetFlag);
				}

				detectIdx++; 

				// Let the application work a little
				return 1;
			}
			else
				detectState = countReferences_init;
		}
		break;

		case countReferences_init:
		{
			gcMap.MoveFirst(&gcMapCursor);
			detectState = countReferences_loop;
		}
		break;

		case countReferences_loop:
		{
			// Call EnumReferences on all objects in the map to count the number
			// of references reachable from between objects in the map. If all
			// references for an object in the map is reachable from other objects
			// in the map, then we know that no outside references are held for
			// this object, thus it is a potential dead object in a circular reference.

			// If the gcFlag is cleared for an object we consider the object alive
			// and referenced from outside the GC, thus we don't enumerate its references.

			// Any new objects created after this step in the GC cycle won't be
			// in the map, and is thus automatically considered alive.
			if( gcMapCursor )
			{
				void *obj = gcMap.GetKey(gcMapCursor);
				asCObjectType *type = gcMap.GetValue(gcMapCursor).type;
				gcMap.MoveNext(&gcMapCursor, gcMapCursor);

				if( engine->CallObjectMethodRetBool(obj, type->beh.gcGetFlag) )
				{
					engine->CallObjectMethod(obj, engine, type->beh.gcEnumReferences);
				}

				// Allow the application to work a little
				return 1;
			}
			else
				detectState = detectGarbage_init;
		}
		break;

		case detectGarbage_init:
		{
			gcMap.MoveFirst(&gcMapCursor);
			liveObjects.SetLength(0);
			detectState = detectGarbage_loop1;
		}
		break;

		case detectGarbage_loop1:
		{
			// All objects that are known not to be dead must be removed from the map,
			// along with all objects they reference. What remains in the map after
			// this pass is sure to be dead objects in circular references.

			// An object is considered alive if its gcFlag is cleared, or all the
			// references were not found in the map.

			// Add all alive objects from the map to the liveObjects array
			if( gcMapCursor )
			{
				asSMapNode<void*, asSIntTypePair> *cursor = gcMapCursor;
				gcMap.MoveNext(&gcMapCursor, gcMapCursor);

				void *obj = gcMap.GetKey(cursor);
				asSIntTypePair it = gcMap.GetValue(cursor);

				bool gcFlag = engine->CallObjectMethodRetBool(obj, it.type->beh.gcGetFlag);
				if( !gcFlag || it.i > 0 )
				{
					liveObjects.PushLast(obj);
				}

				// Allow the application to work a little
				return 1;
			}
			else
				detectState = detectGarbage_loop2;
		}
		break;

		case detectGarbage_loop2:
		{
			// In this step we are actually removing the alive objects from the map.
			// As the object is removed, all the objects it references are added to the
			// liveObjects list, by calling EnumReferences. Only objects still in the map
			// will be added to the liveObjects list.
			if( liveObjects.GetLength() )
			{
				void *gcObj = liveObjects.PopLast();
				asCObjectType *type = 0;

				// Remove the object from the map to mark it as alive
				asSMapNode<void*, asSIntTypePair> *cursor = 0;
				if( gcMap.MoveTo(&cursor, gcObj) )
				{
					type = gcMap.GetValue(cursor).type;
					ReturnNode(gcMap.Remove(cursor));

					// We need to decrease the reference count again as we remove the object from the map
					engine->CallObjectMethod(gcObj, type->beh.release);

					// Enumerate all the object's references so that they too can be marked as alive
					engine->CallObjectMethod(gcObj, engine, type->beh.gcEnumReferences);
				}

				// Allow the application to work a little
				return 1;
			}
			else
				detectState = verifyUnmarked_init;
		}
		break;

		case verifyUnmarked_init:
			gcMap.MoveFirst(&gcMapCursor);
			detectState = verifyUnmarked_loop;
			break;

		case verifyUnmarked_loop:
		{
			// In this step we must make sure that none of the objects still in the map
			// has been touched by the application. If they have then we must run the
			// detectGarbage loop once more.
			if( gcMapCursor )
			{
				void *gcObj = gcMap.GetKey(gcMapCursor);
				asCObjectType *type = gcMap.GetValue(gcMapCursor).type;

				bool gcFlag = engine->CallObjectMethodRetBool(gcObj, type->beh.gcGetFlag);
				if( !gcFlag )
				{
					// The unmarked object was touched, rerun the detectGarbage loop
					detectState = detectGarbage_init;
				}
				else
					gcMap.MoveNext(&gcMapCursor, gcMapCursor);

				// Allow the application to work a little
				return 1;
			}
			else
			{
				// No unmarked object was touched, we can now be sure
				// that objects that have gcCount == 0 really is garbage
				detectState = breakCircles_init;
			}
		}
		break;

		case breakCircles_init:
		{
			gcMap.MoveFirst(&gcMapCursor);
			detectState = breakCircles_loop;
		}
		break;

		case breakCircles_loop:
		case breakCircles_haveGarbage:
		{
			// All objects in the map are now known to be dead objects
			// kept alive through circular references. To be able to free
			// these objects we need to force the breaking of the circle
			// by having the objects release their references.
			if( gcMapCursor )
			{
				numDetected++;
				void *gcObj = gcMap.GetKey(gcMapCursor);
				asCObjectType *type = gcMap.GetValue(gcMapCursor).type;
				engine->CallObjectMethod(gcObj, engine, type->beh.gcReleaseAllReferences);

				gcMap.MoveNext(&gcMapCursor, gcMapCursor);

				detectState = breakCircles_haveGarbage;

				// Allow the application to work a little
				return 1;
			}
			else
			{
				// If no garbage was detected we can finish now
				if( detectState != breakCircles_haveGarbage )
				{
					// Restart the GC
					detectState = clearCounters_init;
					return 0;
				}
				else
				{
					// Restart the GC
					detectState = clearCounters_init;
					return 1;
				}
			}
		}
		} // switch
	}

	// Shouldn't reach this point
	UNREACHABLE_RETURN;
}
Ejemplo n.º 6
0
int asCGarbageCollector::DestroyOldGarbage()
{
	for(;;)
	{
		switch( destroyOldState )
		{
		case destroyGarbage_init:
		{
			// If there are no objects to be freed then don't start
			if( gcOldObjects.GetLength() == 0 )
				return 0;

			destroyOldIdx = (asUINT)-1;
			destroyOldState = 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( ++destroyOldIdx < gcOldObjects.GetLength() )
			{
				asSObjTypePair gcObj = GetOldObjectAtIdx(destroyOldIdx);

				if( gcObj.type->beh.gcGetRefCount == 0 )
				{
					// If circular references are formed with registered types that hasn't 
					// registered the GC behaviours, then the engine may be forced to free
					// the object type before the actual object instance. In this case we
					// will be forced to skip the destruction of the objects, so as not to 
					// crash the application.
					asCString msg;
					msg.Format(TXT_GC_CANNOT_FREE_OBJ_OF_TYPE_s, gcObj.type->name.AddressOf());
					engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, msg.AddressOf());

					// Just remove the object, as we will not bother to destroy it
					numDestroyed++;
					RemoveOldObjectAtIdx(destroyOldIdx);
					destroyOldIdx--;
				}
				else 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++;
						RemoveOldObjectAtIdx(destroyOldIdx);
						destroyOldIdx--;
					}
					else
					{
						// Since the object was resurrected in the
						// destructor, we must add our reference again
						engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.addref);
					}

					destroyOldState = destroyGarbage_haveMore;
				}

				// Allow the application to work a little
				return 1;
			}
			else
			{
				if( destroyOldState == destroyGarbage_haveMore )
				{
					// Restart the cycle
					destroyOldState = destroyGarbage_init;
				}
				else
				{
					// Restart the cycle
					destroyOldState = 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;
}