Fix memory leak caused by circular prop list chains.

The shapes library has such pointer chains. The leaks were getting pretty heavy because they included pointers to C4AulFuncs, which kept a lot of parts of the script engine and string tables in memory.
lights3
Sven Eberhardt 2015-08-20 20:19:38 -04:00
parent 5ec9999d4c
commit cf940d639b
6 changed files with 62 additions and 5 deletions

View File

@ -33,6 +33,7 @@
C4Set<C4PropList *> C4PropList::PropLists;
#endif
C4Set<C4PropListNumbered *> C4PropListNumbered::PropLists;
C4Set<C4PropListScript *> C4PropListScript::PropLists;
std::vector<C4PropListNumbered *> C4PropListNumbered::ShelvedPropLists;
int32_t C4PropListNumbered::EnumerationIndex = 0;
C4StringTable Strings;

View File

@ -603,6 +603,9 @@ void C4Game::Clear()
C4PropListNumbered::ClearShelve(); // may be nonempty if there was a fatal error during section load
ScriptEngine.Clear();
// delete any remaining prop lists from circular chains
C4PropListNumbered::ClearNumberedPropLists();
C4PropListScript::ClearScriptPropLists();
MainSysLangStringTable.Clear();
ScenarioLangStringTable.Clear();
CloseScenario();

View File

@ -38,6 +38,7 @@
C4Set<C4PropList *> C4PropList::PropLists;
#endif
C4Set<C4PropListNumbered *> C4PropListNumbered::PropLists;
C4Set<C4PropListScript *> C4PropListScript::PropLists;
std::vector<C4PropListNumbered *> C4PropListNumbered::ShelvedPropLists;
int32_t C4PropListNumbered::EnumerationIndex = 0;
C4StringTable Strings;

View File

@ -134,6 +134,20 @@ void C4PropListNumbered::ClearShelve()
ShelvedPropLists.clear();
}
void C4PropListNumbered::ClearNumberedPropLists()
{
// empty all proplists to ensure safe deletion of proplists with circular references
// note that this the call to Clear() might delete some prop lists. So it is assumed that
// PropLists does not shrink its table as the number of values goes down
C4PropListNumbered *const* p_next = PropLists.First(), *const* p;
while ((p = p_next))
{
p_next = PropLists.Next(p);
// check *p since it might have been deleted by clearing the previous prop list
if (*p) (*p)->Clear();
}
}
C4PropListNumbered::C4PropListNumbered(C4PropList * prototype): C4PropList(prototype), Number(-1)
{
}
@ -190,6 +204,28 @@ C4PropListNumbered::~C4PropListNumbered()
Log("removing numbered proplist without number");
}
void C4PropListScript::ClearScriptPropLists()
{
// empty all proplists to ensure safe deletion of proplists with circular references
// note that this the call to Clear() might delete some prop lists. So it is assumed that
// PropLists does not shrink its table as the number of values goes down
// However, some values may be skipped due to table consolidation. Just fix it by iterating over the table until it's empty.
C4PropListScript *const* p_next, *const* p;
while ((p_next = PropLists.First()))
{
while ((p = p_next))
{
p_next = PropLists.Next(p);
// check *p since it might have been deleted by clearing the previous prop list
if (*p)
{
C4Value ref(C4VPropList(*p)); // keep a reference because prop list might delete itself within clearing method otherwise
(*p)->Clear();
}
}
}
}
void C4PropListStatic::RefCompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers) const
{
assert(!pComp->isCompiler());
@ -609,7 +645,7 @@ C4PropertyName C4PropList::GetPropertyP(C4PropertyName n) const
return P_LAST;
}
int32_t C4PropList::GetPropertyInt(C4PropertyName n) const
int32_t C4PropList::GetPropertyInt(C4PropertyName n, int32_t default_val) const
{
C4String * k = &Strings.P[n];
if (Properties.Has(k))
@ -618,9 +654,9 @@ int32_t C4PropList::GetPropertyInt(C4PropertyName n) const
}
if (GetPrototype())
{
return GetPrototype()->GetPropertyInt(n);
return GetPrototype()->GetPropertyInt(n, default_val);
}
return 0;
return default_val;
}
C4PropList *C4PropList::GetPropertyPropList(C4PropertyName n) const
@ -800,3 +836,11 @@ unsigned int C4Set<C4PropList *>::Hash<C4PropList *>(C4PropList * const & e)
{
return C4Set<C4PropListNumbered *>::Hash(static_cast<int>(reinterpret_cast<intptr_t>(e)));
}
template<> template<>
unsigned int C4Set<C4PropListScript *>::Hash<C4PropListScript *>(C4PropListScript * const & e)
{
// since script prop lists are only put in the set for reference keeping, just hash by pointer
// but use only some of the more significant bits because
return reinterpret_cast<unsigned int>(e) / 63;
}

View File

@ -113,7 +113,7 @@ public:
C4Value Call(C4String * k, C4AulParSet *pPars=0, bool fPassErrors=false);
C4Value Call(const char * k, C4AulParSet *pPars=0, bool fPassErrors=false);
C4PropertyName GetPropertyP(C4PropertyName k) const;
int32_t GetPropertyInt(C4PropertyName k) const;
int32_t GetPropertyInt(C4PropertyName k, int32_t default_val = 0) const;
C4PropList *GetPropertyPropList(C4PropertyName k) const;
bool HasProperty(C4String * k) const { return Properties.Has(k); }
// not allowed on frozen proplists
@ -216,6 +216,7 @@ public:
static void ShelveNumberedPropLists(); // unnumber all proplists and put them on the shelve. To be used on remaining objects before a savegame load.
static void UnshelveNumberedPropLists(); // re-insert shelved proplists into main list
static void ClearShelve();
static void ClearNumberedPropLists(); // empty all properties in numbered prop lists. Used on game clear to ensure prop lists with circular references get cleared.
protected:
C4PropListNumbered(C4PropList * prototype = 0);
void AcquireNumber(); // acquire a number and add to internal list
@ -232,8 +233,14 @@ protected:
class C4PropListScript: public C4PropList
{
public:
C4PropListScript(C4PropList * prototype = 0): C4PropList(prototype) { }
C4PropListScript(C4PropList * prototype = 0) : C4PropList(prototype) { PropLists.Add(this); }
virtual ~C4PropListScript() { PropLists.Remove(this); }
bool Delete() { return true; }
static void ClearScriptPropLists(); // empty all properties in script-created prop lists. Used on game clear to ensure prop lists with circular references get cleared.
protected:
static C4Set<C4PropListScript *> PropLists;
};
// PropLists declared in the game data

View File

@ -34,6 +34,7 @@
C4Set<C4PropList *> C4PropList::PropLists;
#endif
C4Set<C4PropListNumbered *> C4PropListNumbered::PropLists;
C4Set<C4PropListScript *> C4PropListScript::PropLists;
std::vector<C4PropListNumbered *> C4PropListNumbered::ShelvedPropLists;
int32_t C4PropListNumbered::EnumerationIndex = 0;
C4StringTable Strings;