forked from Mirrors/openclonk
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
parent
5ec9999d4c
commit
cf940d639b
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue