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 in the context of @a event_loop
80 whenever this signal is emitted. Store the connection that represents
81 this arrangement to @a c.
83 NOTE: @a slot will be executed in the same thread that the signal is
87 void connect_same_thread (ScopedConnection& c,
88 const typename SignalType::slot_function_type& slot) {
89 c = _signal.connect (slot);
92 /** Arrange for @a slot to be executed in the context of @a event_loop
93 whenever this signal is emitted. Add the connection that represents
94 this arrangement to @a clist.
96 NOTE: @a slot will be executed in the same thread that the signal is
100 void connect_same_thread (ScopedConnectionList& clist,
101 const typename SignalType::slot_function_type& slot) {
102 clist.add_connection (_signal.connect (slot));
105 /** Arrange for @a slot to be executed in the context of @a event_loop
106 whenever this signal is emitted. Add the connection that represents
107 this arrangement to @a clist.
109 If the event loop/thread in which @a slot will be executed will
110 outlive the lifetime of any object referenced in @a slot,
111 then an InvalidationRecord should be passed, allowing
112 any request sent to the @a event_loop and not executed
113 before the object is destroyed to be marked invalid.
115 "outliving the lifetime" doesn't have a specific, detailed meaning,
116 but is best illustrated by two contrasting examples:
118 1) the main GUI event loop/thread - this will outlive more or
119 less all objects in the application, and thus when arranging for
120 @a slot to be called in that context, an invalidation record is
123 2) a secondary event loop/thread which will be destroyed along
124 with the objects that are typically referenced by @a slot.
125 Assuming that the event loop is stopped before the objects are
126 destroyed, there is no reason to pass in an invalidation record,
127 and MISSING_INVALIDATOR may be used.
130 void connect (ScopedConnectionList& clist,
131 PBD::EventLoop::InvalidationRecord* ir,
132 const typename SignalType::slot_function_type& slot,
133 PBD::EventLoop* event_loop) {
135 ir->event_loop = event_loop;
137 clist.add_connection (_signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot)));
140 /** See notes for the ScopedConnectionList variant of this function. This
141 * differs in that it stores the connection to the signal in a single
142 * ScopedConnection rather than a ScopedConnectionList.
145 void connect (ScopedConnection& c,
146 PBD::EventLoop::InvalidationRecord* ir,
147 const typename SignalType::slot_function_type& slot,
148 PBD::EventLoop* event_loop) {
150 ir->event_loop = event_loop;
152 c = _signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot));
155 /** Emit this signal. This will cause all slots connected to it be executed
156 in the order that they were connected (cross-thread issues may alter
157 the precise execution time of cross-thread slots).
160 typename SignalType::result_type operator()() {
164 /** Return true if there is nothing connected to this signal, false
168 bool empty() const { return _signal.empty(); }
174 template<typename R, typename A, typename C = boost::signals2::optional_last_value<R> >
178 typedef boost::signals2::signal<R(A), C> SignalType;
180 void connect_same_thread (ScopedConnectionList& clist,
181 const typename SignalType::slot_function_type& slot) {
182 clist.add_connection (_signal.connect (slot));
185 void connect_same_thread (ScopedConnection& c,
186 const typename SignalType::slot_function_type& slot) {
187 c = _signal.connect (slot);
190 static void compositor (typename boost::function<void(A)> f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir, A arg) {
191 event_loop->call_slot (ir, boost::bind (f, arg));
194 void connect (ScopedConnectionList& clist,
195 PBD::EventLoop::InvalidationRecord* ir,
196 const typename SignalType::slot_function_type& slot,
197 PBD::EventLoop* event_loop) {
199 ir->event_loop = event_loop;
201 clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1)));
204 void connect (ScopedConnection& c,
205 PBD::EventLoop::InvalidationRecord* ir,
206 const typename SignalType::slot_function_type& slot,
207 PBD::EventLoop* event_loop) {
209 ir->event_loop = event_loop;
211 c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1));
215 typename SignalType::result_type operator()(A arg1) {
216 return _signal (arg1);
219 bool empty() const { return _signal.empty(); }
225 template<typename R, typename A1, typename A2>
229 typedef boost::signals2::signal<R(A1, A2)> SignalType;
231 void connect_same_thread (ScopedConnectionList& clist,
232 const typename SignalType::slot_function_type& slot) {
233 clist.add_connection (_signal.connect (slot));
236 void connect_same_thread (ScopedConnection& c,
237 const typename SignalType::slot_function_type& slot) {
238 c = _signal.connect (slot);
241 static void compositor (typename boost::function<void(A1,A2)> f, PBD::EventLoop* event_loop,
242 EventLoop::InvalidationRecord* ir,
244 event_loop->call_slot (ir, boost::bind (f, arg1, arg2));
247 void connect (ScopedConnectionList& clist,
248 PBD::EventLoop::InvalidationRecord* ir,
249 const typename SignalType::slot_function_type& slot,
250 PBD::EventLoop* event_loop) {
252 ir->event_loop = event_loop;
254 clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2)));
257 void connect (ScopedConnection& c,
258 PBD::EventLoop::InvalidationRecord* ir,
259 const typename SignalType::slot_function_type& slot,
260 PBD::EventLoop* event_loop) {
262 ir->event_loop = event_loop;
264 c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2));
267 typename SignalType::result_type operator()(A1 arg1, A2 arg2) {
268 return _signal (arg1, arg2);
271 bool empty() const { return _signal.empty(); }
277 template<typename R, typename A1, typename A2, typename A3>
281 typedef boost::signals2::signal<R(A1,A2,A3)> SignalType;
283 void connect_same_thread (ScopedConnectionList& clist,
284 const typename SignalType::slot_function_type& slot) {
285 clist.add_connection (_signal.connect (slot));
288 void connect_same_thread (ScopedConnection& c,
289 const typename SignalType::slot_function_type& slot) {
290 c = _signal.connect (slot);
293 static void compositor (typename boost::function<void(A1,A2,A3)> f, PBD::EventLoop* event_loop,
294 EventLoop::InvalidationRecord* ir,
295 A1 arg1, A2 arg2, A3 arg3) {
296 event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3));
299 void connect (ScopedConnectionList& clist,
300 PBD::EventLoop::InvalidationRecord* ir,
301 const typename SignalType::slot_function_type& slot,
302 PBD::EventLoop* event_loop) {
304 ir->event_loop = event_loop;
306 clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
309 void connect (ScopedConnection& c,
310 PBD::EventLoop::InvalidationRecord* ir,
311 const typename SignalType::slot_function_type& slot,
312 PBD::EventLoop* event_loop) {
314 ir->event_loop = event_loop;
316 c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
319 typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3) {
320 return _signal (arg1, arg2, arg3);
323 bool empty() const { return _signal.empty(); }
329 template<typename R, typename A1, typename A2, typename A3, typename A4>
333 typedef boost::signals2::signal<R(A1,A2,A3,A4)> SignalType;
335 void connect_same_thread (ScopedConnectionList& clist,
336 const typename SignalType::slot_function_type& slot) {
337 clist.add_connection (_signal.connect (slot));
340 void connect_same_thread (ScopedConnection& c,
341 const typename SignalType::slot_function_type& slot) {
342 c = _signal.connect (slot);
345 static void compositor (typename boost::function<void(A1,A2,A3)> f, PBD::EventLoop* event_loop,
346 EventLoop::InvalidationRecord* ir,
347 A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
348 event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3, arg4));
351 void connect (ScopedConnectionList& clist,
352 PBD::EventLoop::InvalidationRecord* ir,
353 const typename SignalType::slot_function_type& slot,
354 PBD::EventLoop* event_loop) {
356 ir->event_loop = event_loop;
358 clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
361 void connect (ScopedConnection& c,
362 PBD::EventLoop::InvalidationRecord* ir,
363 const typename SignalType::slot_function_type& slot,
364 PBD::EventLoop* event_loop) {
366 ir->event_loop = event_loop;
368 c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
371 typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
372 return _signal (arg1, arg2, arg3, arg4);
375 bool empty() const { return _signal.empty(); }
381 template<typename R, typename A1, typename A2, typename A3, typename A4, typename A5>
385 typedef boost::signals2::signal<R(A1,A2,A3,A4,A5)> SignalType;
387 void connect_same_thread (ScopedConnectionList& clist,
388 const typename SignalType::slot_function_type& slot) {
389 clist.add_connection (_signal.connect (slot));
392 void connect_same_thread (ScopedConnection& c,
393 const typename SignalType::slot_function_type& slot) {
394 c = _signal.connect (slot);
397 static void compositor (typename boost::function<void(A1,A2,A3,A4,A5)> f, PBD::EventLoop* event_loop,
398 EventLoop::InvalidationRecord* ir,
399 A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) {
400 event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3, arg4, arg5));
403 void connect (ScopedConnectionList& clist,
404 PBD::EventLoop::InvalidationRecord* ir,
405 const typename SignalType::slot_function_type& slot,
406 PBD::EventLoop* event_loop) {
408 ir->event_loop = event_loop;
410 clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5)));
413 void connect (ScopedConnection& c,
414 PBD::EventLoop::InvalidationRecord* ir,
415 const typename SignalType::slot_function_type& slot,
416 PBD::EventLoop* event_loop) {
418 ir->event_loop = event_loop;
420 c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5)));
423 typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) {
424 return _signal (arg1, arg2, arg3, arg4, arg5);
427 bool empty() const { return _signal.empty(); }
435 #endif /* __pbd_signals_h__ */