Fix #7513: recursive garbage collection caused stack overflow

pull/251/head
Rubidium 3 years ago committed by Charles Pigott
parent 6c49ae9cd7
commit 47a99bb676

@ -17,7 +17,7 @@ public:
return newarray; return newarray;
} }
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void Mark(SQCollectable **chain); void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue);
#endif #endif
void Finalize(){ void Finalize(){
_values.resize(0); _values.resize(0);

@ -59,7 +59,7 @@ public:
} }
void Finalize(); void Finalize();
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void Mark(SQCollectable ** ); void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue);
#endif #endif
SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval);
SQInstance *CreateInstance(); SQInstance *CreateInstance();
@ -147,7 +147,7 @@ public:
} }
void Finalize(); void Finalize();
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void Mark(SQCollectable ** ); void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue);
#endif #endif
bool InstanceOf(SQClass *trg); bool InstanceOf(SQClass *trg);
bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res); bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res);

@ -32,7 +32,7 @@ public:
bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write); bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write);
static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret); static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret);
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void Mark(SQCollectable **chain); void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue);
void Finalize(){_outervalues.resize(0); } void Finalize(){_outervalues.resize(0); }
#endif #endif
SQObjectPtr _env; SQObjectPtr _env;
@ -66,7 +66,7 @@ public:
bool Yield(SQVM *v); bool Yield(SQVM *v);
bool Resume(SQVM *v,SQInteger target); bool Resume(SQVM *v,SQInteger target);
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void Mark(SQCollectable **chain); void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue);
void Finalize(){_stack.resize(0);_closure=_null_;} void Finalize(){_stack.resize(0);_closure=_null_;}
#endif #endif
SQObjectPtr _closure; SQObjectPtr _closure;
@ -106,7 +106,7 @@ public:
sq_delete(this,SQNativeClosure); sq_delete(this,SQNativeClosure);
} }
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void Mark(SQCollectable **chain); void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue);
void Finalize(){_outervalues.resize(0);} void Finalize(){_outervalues.resize(0);}
#endif #endif
SQInteger _nparamscheck; SQInteger _nparamscheck;

@ -486,104 +486,81 @@ bool SQFunctionProto::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
#define START_MARK() if(!(_uiRef&MARK_FLAG)){ \ void SQVM::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue)
_uiRef|=MARK_FLAG;
#define END_MARK() RemoveFromChain(&_sharedstate->_gc_chain, this); \
AddToChain(chain, this); }
void SQVM::Mark(SQCollectable **chain)
{ {
START_MARK() SQSharedState::EnqueueMarkObject(_lasterror,queue);
SQSharedState::MarkObject(_lasterror,chain); SQSharedState::EnqueueMarkObject(_errorhandler,queue);
SQSharedState::MarkObject(_errorhandler,chain); SQSharedState::EnqueueMarkObject(_debughook,queue);
SQSharedState::MarkObject(_debughook,chain); SQSharedState::EnqueueMarkObject(_roottable, queue);
SQSharedState::MarkObject(_roottable, chain); SQSharedState::EnqueueMarkObject(temp_reg, queue);
SQSharedState::MarkObject(temp_reg, chain); for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::EnqueueMarkObject(_stack[i], queue);
for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain); for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::EnqueueMarkObject(_vargsstack[j], queue);
for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::MarkObject(_vargsstack[j], chain); for(SQInteger k = 0; k < _callsstacksize; k++) SQSharedState::EnqueueMarkObject(_callsstack[k]._closure, queue);
for(SQInteger k = 0; k < _callsstacksize; k++) SQSharedState::MarkObject(_callsstack[k]._closure, chain); }
END_MARK()
} void SQArray::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue)
void SQArray::Mark(SQCollectable **chain)
{ {
START_MARK() SQInteger len = _values.size();
SQInteger len = _values.size(); for(SQInteger i = 0;i < len; i++) SQSharedState::EnqueueMarkObject(_values[i], queue);
for(SQInteger i = 0;i < len; i++) SQSharedState::MarkObject(_values[i], chain);
END_MARK()
} }
void SQTable::Mark(SQCollectable **chain)
void SQTable::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue)
{ {
START_MARK() if(_delegate) queue.Enqueue(_delegate);
if(_delegate) _delegate->Mark(chain); SQInteger len = _numofnodes;
SQInteger len = _numofnodes; for(SQInteger i = 0; i < len; i++){
for(SQInteger i = 0; i < len; i++){ SQSharedState::EnqueueMarkObject(_nodes[i].key, queue);
SQSharedState::MarkObject(_nodes[i].key, chain); SQSharedState::EnqueueMarkObject(_nodes[i].val, queue);
SQSharedState::MarkObject(_nodes[i].val, chain); }
}
END_MARK()
} }
void SQClass::Mark(SQCollectable **chain) void SQClass::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue)
{ {
START_MARK() queue.Enqueue(_members);
_members->Mark(chain); if(_base) queue.Enqueue(_base);
if(_base) _base->Mark(chain); SQSharedState::EnqueueMarkObject(_attributes, queue);
SQSharedState::MarkObject(_attributes, chain); for(SQUnsignedInteger i =0; i< _defaultvalues.size(); i++) {
for(SQUnsignedInteger i =0; i< _defaultvalues.size(); i++) { SQSharedState::EnqueueMarkObject(_defaultvalues[i].val, queue);
SQSharedState::MarkObject(_defaultvalues[i].val, chain); SQSharedState::EnqueueMarkObject(_defaultvalues[i].attrs, queue);
SQSharedState::MarkObject(_defaultvalues[i].attrs, chain); }
} for(SQUnsignedInteger j =0; j< _methods.size(); j++) {
for(SQUnsignedInteger j =0; j< _methods.size(); j++) { SQSharedState::EnqueueMarkObject(_methods[j].val, queue);
SQSharedState::MarkObject(_methods[j].val, chain); SQSharedState::EnqueueMarkObject(_methods[j].attrs, queue);
SQSharedState::MarkObject(_methods[j].attrs, chain); }
} for(SQUnsignedInteger k =0; k< _metamethods.size(); k++) {
for(SQUnsignedInteger k =0; k< _metamethods.size(); k++) { SQSharedState::EnqueueMarkObject(_metamethods[k], queue);
SQSharedState::MarkObject(_metamethods[k], chain); }
}
END_MARK()
} }
void SQInstance::Mark(SQCollectable **chain) void SQInstance::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue)
{ {
START_MARK() queue.Enqueue(_class);
_class->Mark(chain); SQUnsignedInteger nvalues = _class->_defaultvalues.size();
SQUnsignedInteger nvalues = _class->_defaultvalues.size(); for(SQUnsignedInteger i =0; i< nvalues; i++) {
for(SQUnsignedInteger i =0; i< nvalues; i++) { SQSharedState::EnqueueMarkObject(_values[i], queue);
SQSharedState::MarkObject(_values[i], chain); }
}
END_MARK()
} }
void SQGenerator::Mark(SQCollectable **chain) void SQGenerator::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue)
{ {
START_MARK() for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::EnqueueMarkObject(_stack[i], queue);
for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain); for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::EnqueueMarkObject(_vargsstack[j], queue);
for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::MarkObject(_vargsstack[j], chain); SQSharedState::EnqueueMarkObject(_closure, queue);
SQSharedState::MarkObject(_closure, chain);
END_MARK()
} }
void SQClosure::Mark(SQCollectable **chain) void SQClosure::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue)
{ {
START_MARK() for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::EnqueueMarkObject(_outervalues[i], queue);
for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::MarkObject(_outervalues[i], chain); for(SQUnsignedInteger i = 0; i < _defaultparams.size(); i++) SQSharedState::EnqueueMarkObject(_defaultparams[i], queue);
for(SQUnsignedInteger i = 0; i < _defaultparams.size(); i++) SQSharedState::MarkObject(_defaultparams[i], chain);
END_MARK()
} }
void SQNativeClosure::Mark(SQCollectable **chain) void SQNativeClosure::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue)
{ {
START_MARK() for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::EnqueueMarkObject(_outervalues[i], queue);
for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::MarkObject(_outervalues[i], chain);
END_MARK()
} }
void SQUserData::Mark(SQCollectable **chain){ void SQUserData::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue){
START_MARK() if(_delegate) queue.Enqueue(_delegate);
if(_delegate) _delegate->Mark(chain);
END_MARK()
} }
void SQCollectable::UnMark() { _uiRef&=~MARK_FLAG; } void SQCollectable::UnMark() { _uiRef&=~MARK_FLAG; }

@ -2,6 +2,7 @@
#ifndef _SQOBJECT_H_ #ifndef _SQOBJECT_H_
#define _SQOBJECT_H_ #define _SQOBJECT_H_
#include <forward_list>
#include "squtils.h" #include "squtils.h"
#define SQ_CLOSURESTREAM_HEAD (('S'<<24)|('Q'<<16)|('I'<<8)|('R')) #define SQ_CLOSURESTREAM_HEAD (('S'<<24)|('Q'<<16)|('I'<<8)|('R'))
@ -344,13 +345,48 @@ struct SQCollectable : public SQRefCounted {
SQCollectable *_prev; SQCollectable *_prev;
SQSharedState *_sharedstate; SQSharedState *_sharedstate;
virtual void Release()=0; virtual void Release()=0;
virtual void Mark(SQCollectable **chain)=0; virtual void EnqueueMarkObjectForChildren(class SQGCMarkerQueue &queue)=0;
void UnMark(); void UnMark();
virtual void Finalize()=0; virtual void Finalize()=0;
static void AddToChain(SQCollectable **chain,SQCollectable *c); static void AddToChain(SQCollectable **chain,SQCollectable *c);
static void RemoveFromChain(SQCollectable **chain,SQCollectable *c); static void RemoveFromChain(SQCollectable **chain,SQCollectable *c);
}; };
/**
* Helper container for state to change the garbage collection from a recursive to an iterative approach.
* The iterative approach provides effectively a depth first search approach.
*/
class SQGCMarkerQueue {
std::forward_list<SQCollectable*> queue; ///< The queue of elements to still process.
public:
/** Whether there are any elements left to process. */
bool IsEmpty() { return this->queue.empty(); }
/**
* Remove the first element from the queue.
* Removal when the queue is empty results in undefined behaviour.
*/
SQCollectable *Pop()
{
SQCollectable *collectable = this->queue.front();
this->queue.pop_front();
return collectable;
}
/**
* Add a collectable to the queue, but only when it has not been marked yet.
* When adding it to the queue, the collectable will be marked, so subsequent calls
* will not add it again.
*/
void Enqueue(SQCollectable *collectable)
{
if ((collectable->_uiRef & MARK_FLAG) == 0) {
collectable->_uiRef |= MARK_FLAG;
this->queue.push_front(collectable);
}
}
};
#define ADD_TO_CHAIN(chain,obj) AddToChain(chain,obj) #define ADD_TO_CHAIN(chain,obj) AddToChain(chain,obj)
#define REMOVE_FROM_CHAIN(chain,obj) {if(!(_uiRef&MARK_FLAG))RemoveFromChain(chain,obj);} #define REMOVE_FROM_CHAIN(chain,obj) {if(!(_uiRef&MARK_FLAG))RemoveFromChain(chain,obj);}

@ -228,18 +228,18 @@ SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name)
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain) void SQSharedState::EnqueueMarkObject(SQObjectPtr &o,SQGCMarkerQueue &queue)
{ {
switch(type(o)){ switch(type(o)){
case OT_TABLE:_table(o)->Mark(chain);break; case OT_TABLE:queue.Enqueue(_table(o));break;
case OT_ARRAY:_array(o)->Mark(chain);break; case OT_ARRAY:queue.Enqueue(_array(o));break;
case OT_USERDATA:_userdata(o)->Mark(chain);break; case OT_USERDATA:queue.Enqueue(_userdata(o));break;
case OT_CLOSURE:_closure(o)->Mark(chain);break; case OT_CLOSURE:queue.Enqueue(_closure(o));break;
case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break; case OT_NATIVECLOSURE:queue.Enqueue(_nativeclosure(o));break;
case OT_GENERATOR:_generator(o)->Mark(chain);break; case OT_GENERATOR:queue.Enqueue(_generator(o));break;
case OT_THREAD:_thread(o)->Mark(chain);break; case OT_THREAD:queue.Enqueue(_thread(o));break;
case OT_CLASS:_class(o)->Mark(chain);break; case OT_CLASS:queue.Enqueue(_class(o));break;
case OT_INSTANCE:_instance(o)->Mark(chain);break; case OT_INSTANCE:queue.Enqueue(_instance(o));break;
default: break; //shutup compiler default: break; //shutup compiler
} }
} }
@ -248,27 +248,36 @@ void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain)
SQInteger SQSharedState::CollectGarbage(SQVM *vm) SQInteger SQSharedState::CollectGarbage(SQVM *vm)
{ {
SQInteger n=0; SQInteger n=0;
SQCollectable *tchain=NULL;
SQVM *vms = _thread(_root_vm); SQVM *vms = _thread(_root_vm);
vms->Mark(&tchain); SQGCMarkerQueue queue;
queue.Enqueue(vms);
#ifdef WITH_ASSERT #ifdef WITH_ASSERT
SQInteger x = _table(_thread(_root_vm)->_roottable)->CountUsed(); SQInteger x = _table(_thread(_root_vm)->_roottable)->CountUsed();
#endif #endif
_refs_table.Mark(&tchain); _refs_table.EnqueueMarkObject(queue);
MarkObject(_registry,&tchain); EnqueueMarkObject(_registry,queue);
MarkObject(_consts,&tchain); EnqueueMarkObject(_consts,queue);
MarkObject(_metamethodsmap,&tchain); EnqueueMarkObject(_metamethodsmap,queue);
MarkObject(_table_default_delegate,&tchain); EnqueueMarkObject(_table_default_delegate,queue);
MarkObject(_array_default_delegate,&tchain); EnqueueMarkObject(_array_default_delegate,queue);
MarkObject(_string_default_delegate,&tchain); EnqueueMarkObject(_string_default_delegate,queue);
MarkObject(_number_default_delegate,&tchain); EnqueueMarkObject(_number_default_delegate,queue);
MarkObject(_generator_default_delegate,&tchain); EnqueueMarkObject(_generator_default_delegate,queue);
MarkObject(_thread_default_delegate,&tchain); EnqueueMarkObject(_thread_default_delegate,queue);
MarkObject(_closure_default_delegate,&tchain); EnqueueMarkObject(_closure_default_delegate,queue);
MarkObject(_class_default_delegate,&tchain); EnqueueMarkObject(_class_default_delegate,queue);
MarkObject(_instance_default_delegate,&tchain); EnqueueMarkObject(_instance_default_delegate,queue);
MarkObject(_weakref_default_delegate,&tchain); EnqueueMarkObject(_weakref_default_delegate,queue);
SQCollectable *tchain=NULL;
while (!queue.IsEmpty()) {
SQCollectable *q = queue.Pop();
q->EnqueueMarkObjectForChildren(queue);
SQCollectable::RemoveFromChain(&_gc_chain, q);
SQCollectable::AddToChain(&tchain, q);
}
SQCollectable *t = _gc_chain; SQCollectable *t = _gc_chain;
SQCollectable *nx = NULL; SQCollectable *nx = NULL;
@ -357,12 +366,12 @@ RefTable::~RefTable()
} }
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void RefTable::Mark(SQCollectable **chain) void RefTable::EnqueueMarkObject(SQGCMarkerQueue &queue)
{ {
RefNode *nodes = (RefNode *)_nodes; RefNode *nodes = (RefNode *)_nodes;
for(SQUnsignedInteger n = 0; n < _numofslots; n++) { for(SQUnsignedInteger n = 0; n < _numofslots; n++) {
if(type(nodes->obj) != OT_NULL) { if(type(nodes->obj) != OT_NULL) {
SQSharedState::MarkObject(nodes->obj,chain); SQSharedState::EnqueueMarkObject(nodes->obj,queue);
} }
nodes++; nodes++;
} }

@ -34,7 +34,7 @@ struct RefTable {
void AddRef(SQObject &obj); void AddRef(SQObject &obj);
SQBool Release(SQObject &obj); SQBool Release(SQObject &obj);
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void Mark(SQCollectable **chain); void EnqueueMarkObject(SQGCMarkerQueue &queue);
#endif #endif
void Finalize(); void Finalize();
private: private:
@ -63,7 +63,7 @@ public:
SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name); SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name);
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
SQInteger CollectGarbage(SQVM *vm); SQInteger CollectGarbage(SQVM *vm);
static void MarkObject(SQObjectPtr &o,SQCollectable **chain); static void EnqueueMarkObject(SQObjectPtr &o,SQGCMarkerQueue &queue);
#endif #endif
SQObjectPtrVec *_metamethods; SQObjectPtrVec *_metamethods;
SQObjectPtr _metamethodsmap; SQObjectPtr _metamethodsmap;

@ -60,7 +60,7 @@ public:
SQ_FREE(_nodes, _numofnodes * sizeof(_HashNode)); SQ_FREE(_nodes, _numofnodes * sizeof(_HashNode));
} }
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void Mark(SQCollectable **chain); void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue);
#endif #endif
inline _HashNode *_Get(const SQObjectPtr &key,SQHash hash) inline _HashNode *_Get(const SQObjectPtr &key,SQHash hash)
{ {

@ -18,7 +18,7 @@ struct SQUserData : SQDelegable
return ud; return ud;
} }
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void Mark(SQCollectable **chain); void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue);
void Finalize(){SetDelegate(NULL);} void Finalize(){SetDelegate(NULL);}
#endif #endif
void Release() { void Release() {

@ -113,7 +113,7 @@ public:
#endif #endif
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
void Mark(SQCollectable **chain); void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue);
#endif #endif
void Finalize(); void Finalize();
void GrowCallStack() { void GrowCallStack() {

Loading…
Cancel
Save