all repos — fluxbox @ bef2039d2c5a31ab9f974059d991557276647af1

custom fork of the fluxbox windowmanager

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.
Pavel Labath pavelo@centrum.sk
commit

bef2039d2c5a31ab9f974059d991557276647af1

parent

144d716a42072bd59f6c99e95e86be4486285782

1 files changed, 46 insertions(+), 15 deletions(-)

jump to
M src/FbTk/Signal.hhsrc/FbTk/Signal.hh

@@ -24,9 +24,9 @@ #define FBTK_SIGNAL_HH

#include "RefCount.hh" #include "Slot.hh" +#include <algorithm> #include <list> #include <map> -#include <vector> #include <set> namespace FbTk {

@@ -40,6 +40,10 @@ * It handles the disconnect and holds all the slots. The connect must be

* handled by the child class so it can do the type checking. */ class SignalHolder { +protected: + typedef RefCount<SlotBase> SlotPtr; + typedef std::list<SlotPtr> SlotList; + public: /// Special tracker interface used by SignalTracker. class Tracker {

@@ -49,12 +53,11 @@ /// Disconnect this holder.

virtual void disconnect(SignalHolder& signal) = 0; }; - /// Do not use this type outside this class - typedef std::list<RefCount<SlotBase> > SlotList; - typedef SlotList::iterator Iterator; typedef Iterator SlotID; typedef SlotList::const_iterator ConstIterator; + + SignalHolder() : m_emitting(0) {} ~SignalHolder() { // Disconnect this holder from all trackers.

@@ -67,13 +70,21 @@ }

/// 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 @@ Iterator begin() { return m_slots.begin(); }

Iterator end() { return m_slots.end(); } /// Connect a slot to this signal. Must only be called by child classes. - SlotID connect(const RefCount<SlotBase>& 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<Tracker*> 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 @@ typename Arg1 = SigImpl::EmptyArg, typename Arg2 = SigImpl::EmptyArg, typename Arg3 = SigImpl::EmptyArg >

class Signal: public SigImpl::SignalHolder { public: void emit(Arg1 arg1, Arg2 arg2, Arg3 arg3) { + begin_emitting(); for ( Iterator it = begin(); it != end(); ++it ) { - static_cast<SigImpl::SlotBase3<ReturnType, Arg1, Arg2, Arg3> &>(**it)(arg1, arg2, arg3); + if(*it) + static_cast<SigImpl::SlotBase3<ReturnType, Arg1, Arg2, Arg3> &>(**it)(arg1, arg2, arg3); } + end_emitting(); } template<typename Functor> SlotID connect(const Functor& functor) { - return SignalHolder::connect(FbTk::RefCount<SigImpl::SlotBase>( + return SignalHolder::connect(SlotPtr( new SigImpl::Slot3<ReturnType, Arg1, Arg2, Arg3, Functor>(functor) )); }

@@ -131,14 +153,17 @@ template <typename ReturnType, typename Arg1, typename Arg2>

class Signal<ReturnType, Arg1, Arg2, SigImpl::EmptyArg>: public SigImpl::SignalHolder { public: void emit(Arg1 arg1, Arg2 arg2) { + begin_emitting(); for ( Iterator it = begin(); it != end(); ++it ) { - static_cast<SigImpl::SlotBase2<ReturnType, Arg1, Arg2> &>(**it)(arg1, arg2); + if(*it) + static_cast<SigImpl::SlotBase2<ReturnType, Arg1, Arg2> &>(**it)(arg1, arg2); } + end_emitting(); } template<typename Functor> SlotID connect(const Functor& functor) { - return SignalHolder::connect(FbTk::RefCount<SigImpl::SlotBase>( + return SignalHolder::connect(SlotPtr( new SigImpl::Slot2<ReturnType, Arg1, Arg2, Functor>(functor) )); }

@@ -149,14 +174,17 @@ template <typename ReturnType, typename Arg1>

class Signal<ReturnType, Arg1, SigImpl::EmptyArg, SigImpl::EmptyArg>: public SigImpl::SignalHolder { public: void emit(Arg1 arg) { + begin_emitting(); for ( Iterator it = begin(); it != end(); ++it ) { - static_cast<SigImpl::SlotBase1<ReturnType, Arg1> &>(**it)(arg); + if(*it) + static_cast<SigImpl::SlotBase1<ReturnType, Arg1> &>(**it)(arg); } + end_emitting(); } template<typename Functor> SlotID connect(const Functor& functor) { - return SignalHolder::connect(FbTk::RefCount<SigImpl::SlotBase>( + return SignalHolder::connect(SlotPtr( new SigImpl::Slot1<ReturnType, Arg1, Functor>(functor) )); }

@@ -167,14 +195,17 @@ template <typename ReturnType>

class Signal<ReturnType, SigImpl::EmptyArg, SigImpl::EmptyArg, SigImpl::EmptyArg>: public SigImpl::SignalHolder { public: void emit() { + begin_emitting(); for ( Iterator it = begin(); it != end(); ++it ) { - static_cast<SigImpl::SlotBase0<ReturnType> &>(**it)(); + if(*it) + static_cast<SigImpl::SlotBase0<ReturnType> &>(**it)(); } + end_emitting(); } template<typename Functor> SlotID connect(const Functor& functor) { - return SignalHolder::connect(FbTk::RefCount<SigImpl::SlotBase>( + return SignalHolder::connect(SlotPtr( new SigImpl::Slot0<ReturnType, Functor>(functor) )); }