diff options
author | Pavel Labath <pavelo@centrum.sk> | 2011-05-02 21:01:24 (GMT) |
---|---|---|
committer | Pavel Labath <pavelo@centrum.sk> | 2011-05-10 11:00:45 (GMT) |
commit | bef2039d2c5a31ab9f974059d991557276647af1 (patch) | |
tree | 88feb5d8d2f7ac1915325d5506aea71e07386d9f | |
parent | 144d716a42072bd59f6c99e95e86be4486285782 (diff) | |
download | fluxbox_pavel-bef2039d2c5a31ab9f974059d991557276647af1.zip fluxbox_pavel-bef2039d2c5a31ab9f974059d991557276647af1.tar.bz2 |
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.
-rw-r--r-- | src/FbTk/Signal.hh | 61 |
1 files 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 @@ | |||
24 | 24 | ||
25 | #include "RefCount.hh" | 25 | #include "RefCount.hh" |
26 | #include "Slot.hh" | 26 | #include "Slot.hh" |
27 | #include <algorithm> | ||
27 | #include <list> | 28 | #include <list> |
28 | #include <map> | 29 | #include <map> |
29 | #include <vector> | ||
30 | #include <set> | 30 | #include <set> |
31 | 31 | ||
32 | namespace FbTk { | 32 | namespace FbTk { |
@@ -40,6 +40,10 @@ namespace SigImpl { | |||
40 | * handled by the child class so it can do the type checking. | 40 | * handled by the child class so it can do the type checking. |
41 | */ | 41 | */ |
42 | class SignalHolder { | 42 | class SignalHolder { |
43 | protected: | ||
44 | typedef RefCount<SlotBase> SlotPtr; | ||
45 | typedef std::list<SlotPtr> SlotList; | ||
46 | |||
43 | public: | 47 | public: |
44 | /// Special tracker interface used by SignalTracker. | 48 | /// Special tracker interface used by SignalTracker. |
45 | class Tracker { | 49 | class Tracker { |
@@ -49,13 +53,12 @@ public: | |||
49 | virtual void disconnect(SignalHolder& signal) = 0; | 53 | virtual void disconnect(SignalHolder& signal) = 0; |
50 | }; | 54 | }; |
51 | 55 | ||
52 | /// Do not use this type outside this class | ||
53 | typedef std::list<RefCount<SlotBase> > SlotList; | ||
54 | |||
55 | typedef SlotList::iterator Iterator; | 56 | typedef SlotList::iterator Iterator; |
56 | typedef Iterator SlotID; | 57 | typedef Iterator SlotID; |
57 | typedef SlotList::const_iterator ConstIterator; | 58 | typedef SlotList::const_iterator ConstIterator; |
58 | 59 | ||
60 | SignalHolder() : m_emitting(0) {} | ||
61 | |||
59 | ~SignalHolder() { | 62 | ~SignalHolder() { |
60 | // Disconnect this holder from all trackers. | 63 | // Disconnect this holder from all trackers. |
61 | for (Trackers::iterator it = m_trackers.begin(), | 64 | for (Trackers::iterator it = m_trackers.begin(), |
@@ -67,13 +70,21 @@ public: | |||
67 | 70 | ||
68 | /// Remove a specific slot \c id from this signal | 71 | /// Remove a specific slot \c id from this signal |
69 | void disconnect(SlotID slotIt) { | 72 | void disconnect(SlotID slotIt) { |
70 | m_slots.erase( slotIt ); | 73 | if(m_emitting) { |
74 | // if we are emitting, we must not erase the actual element, as that would | ||
75 | // invalidate iterators in the emit() function | ||
76 | *slotIt = SlotPtr(); | ||
77 | } else | ||
78 | m_slots.erase( slotIt ); | ||
71 | } | 79 | } |
72 | 80 | ||
73 | 81 | ||
74 | /// Removes all slots connected to this | 82 | /// Removes all slots connected to this |
75 | void clear() { | 83 | void clear() { |
76 | m_slots.clear(); | 84 | if(m_emitting) |
85 | std::fill(m_slots.begin(), m_slots.end(), SlotPtr()); | ||
86 | else | ||
87 | m_slots.clear(); | ||
77 | } | 88 | } |
78 | 89 | ||
79 | void connectTracker(SignalHolder::Tracker& tracker) { | 90 | void connectTracker(SignalHolder::Tracker& tracker) { |
@@ -92,14 +103,22 @@ protected: | |||
92 | Iterator end() { return m_slots.end(); } | 103 | Iterator end() { return m_slots.end(); } |
93 | 104 | ||
94 | /// Connect a slot to this signal. Must only be called by child classes. | 105 | /// Connect a slot to this signal. Must only be called by child classes. |
95 | SlotID connect(const RefCount<SlotBase>& slot) { | 106 | SlotID connect(const SlotPtr& slot) { |
96 | return m_slots.insert(m_slots.end(), slot); | 107 | return m_slots.insert(m_slots.end(), slot); |
97 | } | 108 | } |
98 | 109 | ||
110 | void begin_emitting() { ++m_emitting; } | ||
111 | void end_emitting() { | ||
112 | if(--m_emitting == 0) { | ||
113 | // remove elements which belonged slots that detached themselves | ||
114 | m_slots.erase(std::remove(m_slots.begin(), m_slots.end(), SlotPtr()), m_slots.end()); | ||
115 | } | ||
116 | } | ||
99 | private: | 117 | private: |
100 | typedef std::set<Tracker*> Trackers; | 118 | typedef std::set<Tracker*> Trackers; |
101 | SlotList m_slots; ///< all slots connected to a signal | 119 | SlotList m_slots; ///< all slots connected to a signal |
102 | Trackers m_trackers; ///< all instances that tracks this signal. | 120 | Trackers m_trackers; ///< all instances that tracks this signal. |
121 | unsigned m_emitting; | ||
103 | }; | 122 | }; |
104 | 123 | ||
105 | struct EmptyArg {}; | 124 | struct EmptyArg {}; |
@@ -113,14 +132,17 @@ template <typename ReturnType, | |||
113 | class Signal: public SigImpl::SignalHolder { | 132 | class Signal: public SigImpl::SignalHolder { |
114 | public: | 133 | public: |
115 | void emit(Arg1 arg1, Arg2 arg2, Arg3 arg3) { | 134 | void emit(Arg1 arg1, Arg2 arg2, Arg3 arg3) { |
135 | begin_emitting(); | ||
116 | for ( Iterator it = begin(); it != end(); ++it ) { | 136 | for ( Iterator it = begin(); it != end(); ++it ) { |
117 | static_cast<SigImpl::SlotBase3<ReturnType, Arg1, Arg2, Arg3> &>(**it)(arg1, arg2, arg3); | 137 | if(*it) |
138 | static_cast<SigImpl::SlotBase3<ReturnType, Arg1, Arg2, Arg3> &>(**it)(arg1, arg2, arg3); | ||
118 | } | 139 | } |
140 | end_emitting(); | ||
119 | } | 141 | } |
120 | 142 | ||
121 | template<typename Functor> | 143 | template<typename Functor> |
122 | SlotID connect(const Functor& functor) { | 144 | SlotID connect(const Functor& functor) { |
123 | return SignalHolder::connect(FbTk::RefCount<SigImpl::SlotBase>( | 145 | return SignalHolder::connect(SlotPtr( |
124 | new SigImpl::Slot3<ReturnType, Arg1, Arg2, Arg3, Functor>(functor) | 146 | new SigImpl::Slot3<ReturnType, Arg1, Arg2, Arg3, Functor>(functor) |
125 | )); | 147 | )); |
126 | } | 148 | } |
@@ -131,14 +153,17 @@ template <typename ReturnType, typename Arg1, typename Arg2> | |||
131 | class Signal<ReturnType, Arg1, Arg2, SigImpl::EmptyArg>: public SigImpl::SignalHolder { | 153 | class Signal<ReturnType, Arg1, Arg2, SigImpl::EmptyArg>: public SigImpl::SignalHolder { |
132 | public: | 154 | public: |
133 | void emit(Arg1 arg1, Arg2 arg2) { | 155 | void emit(Arg1 arg1, Arg2 arg2) { |
156 | begin_emitting(); | ||
134 | for ( Iterator it = begin(); it != end(); ++it ) { | 157 | for ( Iterator it = begin(); it != end(); ++it ) { |
135 | static_cast<SigImpl::SlotBase2<ReturnType, Arg1, Arg2> &>(**it)(arg1, arg2); | 158 | if(*it) |
159 | static_cast<SigImpl::SlotBase2<ReturnType, Arg1, Arg2> &>(**it)(arg1, arg2); | ||
136 | } | 160 | } |
161 | end_emitting(); | ||
137 | } | 162 | } |
138 | 163 | ||
139 | template<typename Functor> | 164 | template<typename Functor> |
140 | SlotID connect(const Functor& functor) { | 165 | SlotID connect(const Functor& functor) { |
141 | return SignalHolder::connect(FbTk::RefCount<SigImpl::SlotBase>( | 166 | return SignalHolder::connect(SlotPtr( |
142 | new SigImpl::Slot2<ReturnType, Arg1, Arg2, Functor>(functor) | 167 | new SigImpl::Slot2<ReturnType, Arg1, Arg2, Functor>(functor) |
143 | )); | 168 | )); |
144 | } | 169 | } |
@@ -149,14 +174,17 @@ template <typename ReturnType, typename Arg1> | |||
149 | class Signal<ReturnType, Arg1, SigImpl::EmptyArg, SigImpl::EmptyArg>: public SigImpl::SignalHolder { | 174 | class Signal<ReturnType, Arg1, SigImpl::EmptyArg, SigImpl::EmptyArg>: public SigImpl::SignalHolder { |
150 | public: | 175 | public: |
151 | void emit(Arg1 arg) { | 176 | void emit(Arg1 arg) { |
177 | begin_emitting(); | ||
152 | for ( Iterator it = begin(); it != end(); ++it ) { | 178 | for ( Iterator it = begin(); it != end(); ++it ) { |
153 | static_cast<SigImpl::SlotBase1<ReturnType, Arg1> &>(**it)(arg); | 179 | if(*it) |
180 | static_cast<SigImpl::SlotBase1<ReturnType, Arg1> &>(**it)(arg); | ||
154 | } | 181 | } |
182 | end_emitting(); | ||
155 | } | 183 | } |
156 | 184 | ||
157 | template<typename Functor> | 185 | template<typename Functor> |
158 | SlotID connect(const Functor& functor) { | 186 | SlotID connect(const Functor& functor) { |
159 | return SignalHolder::connect(FbTk::RefCount<SigImpl::SlotBase>( | 187 | return SignalHolder::connect(SlotPtr( |
160 | new SigImpl::Slot1<ReturnType, Arg1, Functor>(functor) | 188 | new SigImpl::Slot1<ReturnType, Arg1, Functor>(functor) |
161 | )); | 189 | )); |
162 | } | 190 | } |
@@ -167,14 +195,17 @@ template <typename ReturnType> | |||
167 | class Signal<ReturnType, SigImpl::EmptyArg, SigImpl::EmptyArg, SigImpl::EmptyArg>: public SigImpl::SignalHolder { | 195 | class Signal<ReturnType, SigImpl::EmptyArg, SigImpl::EmptyArg, SigImpl::EmptyArg>: public SigImpl::SignalHolder { |
168 | public: | 196 | public: |
169 | void emit() { | 197 | void emit() { |
198 | begin_emitting(); | ||
170 | for ( Iterator it = begin(); it != end(); ++it ) { | 199 | for ( Iterator it = begin(); it != end(); ++it ) { |
171 | static_cast<SigImpl::SlotBase0<ReturnType> &>(**it)(); | 200 | if(*it) |
201 | static_cast<SigImpl::SlotBase0<ReturnType> &>(**it)(); | ||
172 | } | 202 | } |
203 | end_emitting(); | ||
173 | } | 204 | } |
174 | 205 | ||
175 | template<typename Functor> | 206 | template<typename Functor> |
176 | SlotID connect(const Functor& functor) { | 207 | SlotID connect(const Functor& functor) { |
177 | return SignalHolder::connect(FbTk::RefCount<SigImpl::SlotBase>( | 208 | return SignalHolder::connect(SlotPtr( |
178 | new SigImpl::Slot0<ReturnType, Functor>(functor) | 209 | new SigImpl::Slot0<ReturnType, Functor>(functor) |
179 | )); | 210 | )); |
180 | } | 211 | } |