From bef2039d2c5a31ab9f974059d991557276647af1 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Mon, 2 May 2011 23:01:24 +0200 Subject: Don't crash when a slot is deregistered in the middle of signal processing this was possible (and used) with FbTk::Subject, but the implemetation of FbTk::Signal didn't support it, which made it impossible to continue with conversion. --- src/FbTk/Signal.hh | 61 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/src/FbTk/Signal.hh b/src/FbTk/Signal.hh index 71a4d6e..432bb3f 100644 --- a/src/FbTk/Signal.hh +++ b/src/FbTk/Signal.hh @@ -24,9 +24,9 @@ #include "RefCount.hh" #include "Slot.hh" +#include #include #include -#include #include namespace FbTk { @@ -40,6 +40,10 @@ namespace SigImpl { * handled by the child class so it can do the type checking. */ class SignalHolder { +protected: + typedef RefCount SlotPtr; + typedef std::list SlotList; + public: /// Special tracker interface used by SignalTracker. class Tracker { @@ -49,13 +53,12 @@ public: virtual void disconnect(SignalHolder& signal) = 0; }; - /// Do not use this type outside this class - typedef std::list > SlotList; - typedef SlotList::iterator Iterator; typedef Iterator SlotID; typedef SlotList::const_iterator ConstIterator; + SignalHolder() : m_emitting(0) {} + ~SignalHolder() { // Disconnect this holder from all trackers. for (Trackers::iterator it = m_trackers.begin(), @@ -67,13 +70,21 @@ public: /// Remove a specific slot \c id from this signal void disconnect(SlotID slotIt) { - m_slots.erase( slotIt ); + if(m_emitting) { + // if we are emitting, we must not erase the actual element, as that would + // invalidate iterators in the emit() function + *slotIt = SlotPtr(); + } else + m_slots.erase( slotIt ); } /// Removes all slots connected to this void clear() { - m_slots.clear(); + if(m_emitting) + std::fill(m_slots.begin(), m_slots.end(), SlotPtr()); + else + m_slots.clear(); } void connectTracker(SignalHolder::Tracker& tracker) { @@ -92,14 +103,22 @@ protected: Iterator end() { return m_slots.end(); } /// Connect a slot to this signal. Must only be called by child classes. - SlotID connect(const RefCount& slot) { + SlotID connect(const SlotPtr& slot) { return m_slots.insert(m_slots.end(), slot); } + void begin_emitting() { ++m_emitting; } + void end_emitting() { + if(--m_emitting == 0) { + // remove elements which belonged slots that detached themselves + m_slots.erase(std::remove(m_slots.begin(), m_slots.end(), SlotPtr()), m_slots.end()); + } + } private: typedef std::set Trackers; SlotList m_slots; ///< all slots connected to a signal Trackers m_trackers; ///< all instances that tracks this signal. + unsigned m_emitting; }; struct EmptyArg {}; @@ -113,14 +132,17 @@ template &>(**it)(arg1, arg2, arg3); + if(*it) + static_cast &>(**it)(arg1, arg2, arg3); } + end_emitting(); } template SlotID connect(const Functor& functor) { - return SignalHolder::connect(FbTk::RefCount( + return SignalHolder::connect(SlotPtr( new SigImpl::Slot3(functor) )); } @@ -131,14 +153,17 @@ template class Signal: public SigImpl::SignalHolder { public: void emit(Arg1 arg1, Arg2 arg2) { + begin_emitting(); for ( Iterator it = begin(); it != end(); ++it ) { - static_cast &>(**it)(arg1, arg2); + if(*it) + static_cast &>(**it)(arg1, arg2); } + end_emitting(); } template SlotID connect(const Functor& functor) { - return SignalHolder::connect(FbTk::RefCount( + return SignalHolder::connect(SlotPtr( new SigImpl::Slot2(functor) )); } @@ -149,14 +174,17 @@ template class Signal: public SigImpl::SignalHolder { public: void emit(Arg1 arg) { + begin_emitting(); for ( Iterator it = begin(); it != end(); ++it ) { - static_cast &>(**it)(arg); + if(*it) + static_cast &>(**it)(arg); } + end_emitting(); } template SlotID connect(const Functor& functor) { - return SignalHolder::connect(FbTk::RefCount( + return SignalHolder::connect(SlotPtr( new SigImpl::Slot1(functor) )); } @@ -167,14 +195,17 @@ template class Signal: public SigImpl::SignalHolder { public: void emit() { + begin_emitting(); for ( Iterator it = begin(); it != end(); ++it ) { - static_cast &>(**it)(); + if(*it) + static_cast &>(**it)(); } + end_emitting(); } template SlotID connect(const Functor& functor) { - return SignalHolder::connect(FbTk::RefCount( + return SignalHolder::connect(SlotPtr( new SigImpl::Slot0(functor) )); } -- cgit v0.11.2