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/signals2.hpp>
27 #include <boost/noncopyable.hpp>
28 #include <boost/bind.hpp>
29 #include <boost/bind/protect.hpp>
31 #include "pbd/event_loop.h"
35 typedef boost::signals2::connection UnscopedConnection;
36 typedef boost::signals2::scoped_connection ScopedConnection;
38 class ScopedConnectionList : public boost::noncopyable
41 ScopedConnectionList();
42 ~ScopedConnectionList ();
44 void add_connection (const UnscopedConnection& c);
45 void drop_connections ();
48 /* this class is not copyable */
49 ScopedConnectionList(const ScopedConnectionList&);
51 /* this lock is shared by all instances of a ScopedConnectionList.
52 We do not want one mutex per list, and since we only need the lock
53 when adding or dropping connections, which are generally occuring
54 in object creation and UI operations, the contention on this
55 lock is low and not of significant consequence. Even though
56 boost::signals2 is thread-safe, this additional list of
57 scoped connections needs to be protected in 2 cases:
59 (1) (unlikely) we make a connection involving a callback on the
60 same object from 2 threads. (wouldn't that just be appalling
63 (2) where we are dropping connections in one thread and adding
67 static Glib::StaticMutex _lock;
69 typedef std::list<ScopedConnection*> ConnectionList;
77 typedef boost::signals2::signal<R()> SignalType;
79 /** Arrange for @a slot to be executed whenever this signal is emitted.
80 Store the connection that represents this arrangement in @a c.
82 NOTE: @a slot will be executed in the same thread that the signal is
86 void connect_same_thread (ScopedConnection& c,
87 const typename SignalType::slot_function_type& slot) {
88 c = _signal.connect (slot);
91 /** Arrange for @a slot to be executed whenever this signal is emitted.
92 Add the connection that represents this arrangement to @a clist.
94 NOTE: @a slot will be executed in the same thread that the signal is
98 void connect_same_thread (ScopedConnectionList& clist,
99 const typename SignalType::slot_function_type& slot) {
100 clist.add_connection (_signal.connect (slot));
103 /** Arrange for @a slot to be executed in the context of @a event_loop
104 whenever this signal is emitted. Add the connection that represents
105 this arrangement to @a clist.
107 If the event loop/thread in which @a slot will be executed will
108 outlive the lifetime of any object referenced in @a slot,
109 then an InvalidationRecord should be passed, allowing
110 any request sent to the @a event_loop and not executed
111 before the object is destroyed to be marked invalid.
113 "outliving the lifetime" doesn't have a specific, detailed meaning,
114 but is best illustrated by two contrasting examples:
116 1) the main GUI event loop/thread - this will outlive more or
117 less all objects in the application, and thus when arranging for
118 @a slot to be called in that context, an invalidation record is
121 2) a secondary event loop/thread which will be destroyed along
122 with the objects that are typically referenced by @a slot.
123 Assuming that the event loop is stopped before the objects are
124 destroyed, there is no reason to pass in an invalidation record,
125 and MISSING_INVALIDATOR may be used.
128 void connect (ScopedConnectionList& clist,
129 PBD::EventLoop::InvalidationRecord* ir,
130 const typename SignalType::slot_function_type& slot,
131 PBD::EventLoop* event_loop) {
133 ir->event_loop = event_loop;
135 clist.add_connection (_signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot)));
138 /** See notes for the ScopedConnectionList variant of this function. This
139 * differs in that it stores the connection to the signal in a single
140 * ScopedConnection rather than a ScopedConnectionList.
143 void connect (ScopedConnection& c,
144 PBD::EventLoop::InvalidationRecord* ir,
145 const typename SignalType::slot_function_type& slot,
146 PBD::EventLoop* event_loop) {
148 ir->event_loop = event_loop;
150 c = _signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot));
153 /** Emit this signal. This will cause all slots connected to it be executed
154 in the order that they were connected (cross-thread issues may alter
155 the precise execution time of cross-thread slots).
158 typename SignalType::result_type operator()() {
162 /** Return true if there is nothing connected to this signal, false
166 bool empty() const { return _signal.empty(); }
172 template<typename R, typename A, typename C = boost::signals2::optional_last_value<R> >
176 typedef boost::signals2::signal<R(A), C> SignalType;
178 void connect_same_thread (ScopedConnectionList& clist,
179 const typename SignalType::slot_function_type& slot) {
180 clist.add_connection (_signal.connect (slot));
183 void connect_same_thread (ScopedConnection& c,
184 const typename SignalType::slot_function_type& slot) {
185 c = _signal.connect (slot);
188 static void compositor (typename boost::function<void(A)> f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir, A arg) {
189 event_loop->call_slot (ir, boost::bind (f, arg));
192 void connect (ScopedConnectionList& clist,
193 PBD::EventLoop::InvalidationRecord* ir,
194 const typename SignalType::slot_function_type& slot,
195 PBD::EventLoop* event_loop) {
197 ir->event_loop = event_loop;
199 clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1)));
202 void connect (ScopedConnection& c,
203 PBD::EventLoop::InvalidationRecord* ir,
204 const typename SignalType::slot_function_type& slot,
205 PBD::EventLoop* event_loop) {
207 ir->event_loop = event_loop;
209 c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1));
213 typename SignalType::result_type operator()(A arg1) {
214 return _signal (arg1);
217 bool empty() const { return _signal.empty(); }
223 template<typename R, typename A1, typename A2>
227 typedef boost::signals2::signal<R(A1, A2)> SignalType;
229 void connect_same_thread (ScopedConnectionList& clist,
230 const typename SignalType::slot_function_type& slot) {
231 clist.add_connection (_signal.connect (slot));
234 void connect_same_thread (ScopedConnection& c,
235 const typename SignalType::slot_function_type& slot) {
236 c = _signal.connect (slot);
239 static void compositor (typename boost::function<void(A1,A2)> f, PBD::EventLoop* event_loop,
240 EventLoop::InvalidationRecord* ir,
242 event_loop->call_slot (ir, boost::bind (f, arg1, arg2));
245 void connect (ScopedConnectionList& clist,
246 PBD::EventLoop::InvalidationRecord* ir,
247 const typename SignalType::slot_function_type& slot,
248 PBD::EventLoop* event_loop) {
250 ir->event_loop = event_loop;
252 clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2)));
255 void connect (ScopedConnection& c,
256 PBD::EventLoop::InvalidationRecord* ir,
257 const typename SignalType::slot_function_type& slot,
258 PBD::EventLoop* event_loop) {
260 ir->event_loop = event_loop;
262 c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2));
265 typename SignalType::result_type operator()(A1 arg1, A2 arg2) {
266 return _signal (arg1, arg2);
269 bool empty() const { return _signal.empty(); }
275 template<typename R, typename A1, typename A2, typename A3>
279 typedef boost::signals2::signal<R(A1,A2,A3)> SignalType;
281 void connect_same_thread (ScopedConnectionList& clist,
282 const typename SignalType::slot_function_type& slot) {
283 clist.add_connection (_signal.connect (slot));
286 void connect_same_thread (ScopedConnection& c,
287 const typename SignalType::slot_function_type& slot) {
288 c = _signal.connect (slot);
291 static void compositor (typename boost::function<void(A1,A2,A3)> f, PBD::EventLoop* event_loop,
292 EventLoop::InvalidationRecord* ir,
293 A1 arg1, A2 arg2, A3 arg3) {
294 event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3));
297 void connect (ScopedConnectionList& clist,
298 PBD::EventLoop::InvalidationRecord* ir,
299 const typename SignalType::slot_function_type& slot,
300 PBD::EventLoop* event_loop) {
302 ir->event_loop = event_loop;
304 clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
307 void connect (ScopedConnection& c,
308 PBD::EventLoop::InvalidationRecord* ir,
309 const typename SignalType::slot_function_type& slot,
310 PBD::EventLoop* event_loop) {
312 ir->event_loop = event_loop;
314 c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
317 typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3) {
318 return _signal (arg1, arg2, arg3);
321 bool empty() const { return _signal.empty(); }
327 template<typename R, typename A1, typename A2, typename A3, typename A4>
331 typedef boost::signals2::signal<R(A1,A2,A3,A4)> SignalType;
333 void connect_same_thread (ScopedConnectionList& clist,
334 const typename SignalType::slot_function_type& slot) {
335 clist.add_connection (_signal.connect (slot));
338 void connect_same_thread (ScopedConnection& c,
339 const typename SignalType::slot_function_type& slot) {
340 c = _signal.connect (slot);
343 static void compositor (typename boost::function<void(A1,A2,A3)> f, PBD::EventLoop* event_loop,
344 EventLoop::InvalidationRecord* ir,
345 A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
346 event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3, arg4));
349 void connect (ScopedConnectionList& clist,
350 PBD::EventLoop::InvalidationRecord* ir,
351 const typename SignalType::slot_function_type& slot,
352 PBD::EventLoop* event_loop) {
354 ir->event_loop = event_loop;
356 clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
359 void connect (ScopedConnection& c,
360 PBD::EventLoop::InvalidationRecord* ir,
361 const typename SignalType::slot_function_type& slot,
362 PBD::EventLoop* event_loop) {
364 ir->event_loop = event_loop;
366 c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
369 typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
370 return _signal (arg1, arg2, arg3, arg4);
373 bool empty() const { return _signal.empty(); }
379 template<typename R, typename A1, typename A2, typename A3, typename A4, typename A5>
383 typedef boost::signals2::signal<R(A1,A2,A3,A4,A5)> SignalType;
385 void connect_same_thread (ScopedConnectionList& clist,
386 const typename SignalType::slot_function_type& slot) {
387 clist.add_connection (_signal.connect (slot));
390 void connect_same_thread (ScopedConnection& c,
391 const typename SignalType::slot_function_type& slot) {
392 c = _signal.connect (slot);
395 static void compositor (typename boost::function<void(A1,A2,A3,A4,A5)> f, PBD::EventLoop* event_loop,
396 EventLoop::InvalidationRecord* ir,
397 A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) {
398 event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3, arg4, arg5));
401 void connect (ScopedConnectionList& clist,
402 PBD::EventLoop::InvalidationRecord* ir,
403 const typename SignalType::slot_function_type& slot,
404 PBD::EventLoop* event_loop) {
406 ir->event_loop = event_loop;
408 clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5)));
411 void connect (ScopedConnection& c,
412 PBD::EventLoop::InvalidationRecord* ir,
413 const typename SignalType::slot_function_type& slot,
414 PBD::EventLoop* event_loop) {
416 ir->event_loop = event_loop;
418 c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5)));
421 typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) {
422 return _signal (arg1, arg2, arg3, arg4, arg5);
425 bool empty() const { return _signal.empty(); }
433 #endif /* __pbd_signals_h__ */