2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #ifndef __pbd_signals_h__
21 #define __pbd_signals_h__
24 #include <glibmm/thread.h>
26 #include <boost/noncopyable.hpp>
27 #include <boost/bind.hpp>
28 #include <boost/bind/protect.hpp>
30 #include "pbd/event_loop.h"
31 #include "pbd/signal.h"
35 typedef boost::shared_ptr<Connection> UnscopedConnection;
36 class ScopedConnection
39 ScopedConnection () {}
40 ScopedConnection (UnscopedConnection c) : _c (c) {}
41 ~ScopedConnection () {
52 ScopedConnection& operator= (UnscopedConnection const & o)
59 UnscopedConnection _c;
62 class ScopedConnectionList : public boost::noncopyable
65 ScopedConnectionList();
66 virtual ~ScopedConnectionList ();
68 void add_connection (const UnscopedConnection& c);
69 void drop_connections ();
72 /* this class is not copyable */
73 ScopedConnectionList(const ScopedConnectionList&);
75 /* this lock is shared by all instances of a ScopedConnectionList.
76 We do not want one mutex per list, and since we only need the lock
77 when adding or dropping connections, which are generally occuring
78 in object creation and UI operations, the contention on this
79 lock is low and not of significant consequence. Even though
80 boost::signals2 is thread-safe, this additional list of
81 scoped connections needs to be protected in 2 cases:
83 (1) (unlikely) we make a connection involving a callback on the
84 same object from 2 threads. (wouldn't that just be appalling
87 (2) where we are dropping connections in one thread and adding
91 static Glib::StaticMutex _lock;
93 typedef std::list<ScopedConnection*> ConnectionList;
100 typedef SimpleSignal0<R> SignalType;
102 Signal0 () : _signal (SignalType::create ()) {}
104 /** Arrange for @a slot to be executed whenever this signal is emitted.
105 Store the connection that represents this arrangement in @a c.
107 NOTE: @a slot will be executed in the same thread that the signal is
111 void connect_same_thread (ScopedConnection& c,
112 const typename SignalType::slot_function_type& slot) {
113 c = _signal->connect (slot);
116 /** Arrange for @a slot to be executed whenever this signal is emitted.
117 Add the connection that represents this arrangement to @a clist.
119 NOTE: @a slot will be executed in the same thread that the signal is
123 void connect_same_thread (ScopedConnectionList& clist,
124 const typename SignalType::slot_function_type& slot) {
125 clist.add_connection (_signal->connect (slot));
128 /** Arrange for @a slot to be executed in the context of @a event_loop
129 whenever this signal is emitted. Add the connection that represents
130 this arrangement to @a clist.
132 If the event loop/thread in which @a slot will be executed will
133 outlive the lifetime of any object referenced in @a slot,
134 then an InvalidationRecord should be passed, allowing
135 any request sent to the @a event_loop and not executed
136 before the object is destroyed to be marked invalid.
138 "outliving the lifetime" doesn't have a specific, detailed meaning,
139 but is best illustrated by two contrasting examples:
141 1) the main GUI event loop/thread - this will outlive more or
142 less all objects in the application, and thus when arranging for
143 @a slot to be called in that context, an invalidation record is
146 2) a secondary event loop/thread which will be destroyed along
147 with the objects that are typically referenced by @a slot.
148 Assuming that the event loop is stopped before the objects are
149 destroyed, there is no reason to pass in an invalidation record,
150 and MISSING_INVALIDATOR may be used.
153 void connect (ScopedConnectionList& clist,
154 PBD::EventLoop::InvalidationRecord* ir,
155 const typename SignalType::slot_function_type& slot,
156 PBD::EventLoop* event_loop) {
158 ir->event_loop = event_loop;
160 clist.add_connection (_signal->connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot)));
163 /** See notes for the ScopedConnectionList variant of this function. This
164 * differs in that it stores the connection to the signal in a single
165 * ScopedConnection rather than a ScopedConnectionList.
168 void connect (ScopedConnection& c,
169 PBD::EventLoop::InvalidationRecord* ir,
170 const typename SignalType::slot_function_type& slot,
171 PBD::EventLoop* event_loop) {
173 ir->event_loop = event_loop;
175 c = _signal->connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot));
178 /** Emit this signal. This will cause all slots connected to it be executed
179 in the order that they were connected (cross-thread issues may alter
180 the precise execution time of cross-thread slots).
183 typename SignalType::result_type operator()() {
184 return _signal->emit ();
187 /** Return true if there is nothing connected to this signal, false
191 bool empty() const { return _signal->empty(); }
194 boost::shared_ptr<SignalType> _signal;
197 template<typename R, typename A, typename C = OptionalLastValue<R> >
200 typedef SimpleSignal1<R, A, C> SignalType;
201 Signal1 () : _signal (SignalType::create()) {}
203 void connect_same_thread (ScopedConnectionList& clist,
204 const typename SignalType::slot_function_type& slot) {
205 clist.add_connection (_signal->connect (slot));
208 void connect_same_thread (ScopedConnection& c,
209 const typename SignalType::slot_function_type& slot) {
210 c = _signal->connect (slot);
213 static void compositor (typename boost::function<void(A)> f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir, A arg) {
214 event_loop->call_slot (ir, boost::bind (f, arg));
217 void connect (ScopedConnectionList& clist,
218 PBD::EventLoop::InvalidationRecord* ir,
219 const typename SignalType::slot_function_type& slot,
220 PBD::EventLoop* event_loop) {
222 ir->event_loop = event_loop;
224 clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1)));
227 void connect (ScopedConnection& c,
228 PBD::EventLoop::InvalidationRecord* ir,
229 const typename SignalType::slot_function_type& slot,
230 PBD::EventLoop* event_loop) {
232 ir->event_loop = event_loop;
234 c = _signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1));
238 typename SignalType::result_type operator()(A arg1) {
239 return _signal->emit (arg1);
242 bool empty() const { return _signal->empty(); }
245 boost::shared_ptr<SignalType> _signal;
248 template<typename R, typename A1, typename A2>
251 typedef SimpleSignal2<R, A1, A2> SignalType;
252 Signal2 () : _signal (SignalType::create()) {}
254 void connect_same_thread (ScopedConnectionList& clist,
255 const typename SignalType::slot_function_type& slot) {
256 clist.add_connection (_signal->connect (slot));
259 void connect_same_thread (ScopedConnection& c,
260 const typename SignalType::slot_function_type& slot) {
261 c = _signal->connect (slot);
264 static void compositor (typename boost::function<void(A1,A2)> f, PBD::EventLoop* event_loop,
265 EventLoop::InvalidationRecord* ir,
267 event_loop->call_slot (ir, boost::bind (f, arg1, arg2));
270 void connect (ScopedConnectionList& clist,
271 PBD::EventLoop::InvalidationRecord* ir,
272 const typename SignalType::slot_function_type& slot,
273 PBD::EventLoop* event_loop) {
275 ir->event_loop = event_loop;
277 clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2)));
280 void connect (ScopedConnection& c,
281 PBD::EventLoop::InvalidationRecord* ir,
282 const typename SignalType::slot_function_type& slot,
283 PBD::EventLoop* event_loop) {
285 ir->event_loop = event_loop;
287 c = _signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2));
290 typename SignalType::result_type operator()(A1 arg1, A2 arg2) {
291 return _signal->emit (arg1, arg2);
294 bool empty() const { return _signal->empty(); }
297 boost::shared_ptr<SignalType> _signal;
300 template<typename R, typename A1, typename A2, typename A3>
303 typedef SimpleSignal3<R, A1, A2, A3> SignalType;
304 Signal3 () : _signal (SignalType::create()) {}
306 void connect_same_thread (ScopedConnectionList& clist,
307 const typename SignalType::slot_function_type& slot) {
308 clist.add_connection (_signal->connect (slot));
311 void connect_same_thread (ScopedConnection& c,
312 const typename SignalType::slot_function_type& slot) {
313 c = _signal->connect (slot);
316 static void compositor (typename boost::function<void(A1,A2,A3)> f, PBD::EventLoop* event_loop,
317 EventLoop::InvalidationRecord* ir,
318 A1 arg1, A2 arg2, A3 arg3) {
319 event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3));
322 void connect (ScopedConnectionList& clist,
323 PBD::EventLoop::InvalidationRecord* ir,
324 const typename SignalType::slot_function_type& slot,
325 PBD::EventLoop* event_loop) {
327 ir->event_loop = event_loop;
329 clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
332 void connect (ScopedConnection& c,
333 PBD::EventLoop::InvalidationRecord* ir,
334 const typename SignalType::slot_function_type& slot,
335 PBD::EventLoop* event_loop) {
337 ir->event_loop = event_loop;
339 c = _signal->connect (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
342 typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3) {
343 return _signal->emit (arg1, arg2, arg3);
346 bool empty() const { return _signal->empty(); }
349 boost::shared_ptr<SignalType> _signal;
352 template<typename R, typename A1, typename A2, typename A3, typename A4>
355 typedef SimpleSignal4<R, A1, A2, A3, A4> SignalType;
356 Signal4 () : _signal (SignalType::create()) {}
358 void connect_same_thread (ScopedConnectionList& clist,
359 const typename SignalType::slot_function_type& slot) {
360 clist.add_connection (_signal->connect (slot));
363 void connect_same_thread (ScopedConnection& c,
364 const typename SignalType::slot_function_type& slot) {
365 c = _signal->connect (slot);
368 static void compositor (typename boost::function<void(A1,A2,A3)> f, PBD::EventLoop* event_loop,
369 EventLoop::InvalidationRecord* ir,
370 A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
371 event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3, arg4));
374 void connect (ScopedConnectionList& clist,
375 PBD::EventLoop::InvalidationRecord* ir,
376 const typename SignalType::slot_function_type& slot,
377 PBD::EventLoop* event_loop) {
379 ir->event_loop = event_loop;
381 clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
384 void connect (ScopedConnection& c,
385 PBD::EventLoop::InvalidationRecord* ir,
386 const typename SignalType::slot_function_type& slot,
387 PBD::EventLoop* event_loop) {
389 ir->event_loop = event_loop;
391 c = _signal->connect (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
394 typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
395 return _signal->emit (arg1, arg2, arg3, arg4);
398 bool empty() const { return _signal->empty(); }
401 boost::shared_ptr<SignalType> _signal;
404 template<typename R, typename A1, typename A2, typename A3, typename A4, typename A5>
407 typedef SimpleSignal5<R, A1, A2, A3, A4, A5> SignalType;
408 Signal5 () : _signal (SignalType::create()) {}
410 void connect_same_thread (ScopedConnectionList& clist,
411 const typename SignalType::slot_function_type& slot) {
412 clist.add_connection (_signal->connect (slot));
415 void connect_same_thread (ScopedConnection& c,
416 const typename SignalType::slot_function_type& slot) {
417 c = _signal->connect (slot);
420 static void compositor (typename boost::function<void(A1,A2,A3,A4,A5)> f, PBD::EventLoop* event_loop,
421 EventLoop::InvalidationRecord* ir,
422 A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) {
423 event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3, arg4, arg5));
426 void connect (ScopedConnectionList& clist,
427 PBD::EventLoop::InvalidationRecord* ir,
428 const typename SignalType::slot_function_type& slot,
429 PBD::EventLoop* event_loop) {
431 ir->event_loop = event_loop;
433 clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5)));
436 void connect (ScopedConnection& c,
437 PBD::EventLoop::InvalidationRecord* ir,
438 const typename SignalType::slot_function_type& slot,
439 PBD::EventLoop* event_loop) {
441 ir->event_loop = event_loop;
443 c = _signal->connect (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5)));
446 typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) {
447 return _signal->emit (arg1, arg2, arg3, arg4, arg5);
450 bool empty() const { return _signal->empty(); }
453 boost::shared_ptr<SignalType> _signal;
458 #endif /* __pbd_signals_h__ */