cfada198792a4eb106ecb83eeb1105e179a2b62f
[ardour.git] / libs / pbd / pbd / signals.h
1 /*
2     Copyright (C) 2009 Paul Davis 
3
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.
8
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.
13
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.
17
18 */
19
20 #ifndef __pbd_signals_h__
21 #define __pbd_signals_h__
22
23 #include <list>
24 #include <glibmm/thread.h>
25
26 #include <boost/signals2.hpp>
27 #include <boost/noncopyable.hpp>
28 #include <boost/bind.hpp>
29 #include <boost/bind/protect.hpp>
30
31 #include "pbd/event_loop.h"
32
33 namespace PBD {
34
35 typedef boost::signals2::connection UnscopedConnection;
36 typedef boost::signals2::scoped_connection ScopedConnection;
37
38 class ScopedConnectionList  : public boost::noncopyable
39 {
40   public:
41         ScopedConnectionList();
42         ~ScopedConnectionList ();
43         
44         void add_connection (const UnscopedConnection& c);
45         void drop_connections ();
46
47   private:
48         /* this class is not copyable */
49         ScopedConnectionList(const ScopedConnectionList&);
50
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:
58
59            (1) (unlikely) we make a connection involving a callback on the
60                same object from 2 threads. (wouldn't that just be appalling 
61                programming style?)
62              
63            (2) where we are dropping connections in one thread and adding
64                one from another.
65          */
66
67         static Glib::StaticMutex _lock;
68
69         typedef std::list<ScopedConnection*> ConnectionList;
70         ConnectionList _list;
71 };
72
73 template<typename R>
74 class Signal0 {
75 public:
76     Signal0 () {}
77     typedef boost::signals2::signal<R()> SignalType;
78
79     /** Arrange for @a slot to be executed whenever this signal is emitted. 
80         Store the connection that represents this arrangement in @a c.
81
82         NOTE: @a slot will be executed in the same thread that the signal is
83         emitted in.
84     */
85
86     void connect_same_thread (ScopedConnection& c, 
87                   const typename SignalType::slot_function_type& slot) {
88             c = _signal.connect (slot);
89     }
90
91     /** Arrange for @a slot to be executed whenever this signal is emitted. 
92         Add the connection that represents this arrangement to @a clist.
93
94         NOTE: @a slot will be executed in the same thread that the signal is
95         emitted in.
96     */
97         
98     void connect_same_thread (ScopedConnectionList& clist, 
99                   const typename SignalType::slot_function_type& slot) {
100             clist.add_connection (_signal.connect (slot));
101     }
102
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.
106         
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.
112         
113         "outliving the lifetime" doesn't have a specific, detailed meaning,
114         but is best illustrated by two contrasting examples:
115         
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 
119         highly advisable.
120         
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.
126     */
127
128     void connect (ScopedConnectionList& clist, 
129                   PBD::EventLoop::InvalidationRecord* ir, 
130                   const typename SignalType::slot_function_type& slot,
131                   PBD::EventLoop* event_loop) {
132             if (ir) {
133                     ir->event_loop = event_loop;
134             }
135             clist.add_connection (_signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot)));
136     }
137
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.
141      */
142
143     void connect (ScopedConnection& c, 
144                   PBD::EventLoop::InvalidationRecord* ir, 
145                   const typename SignalType::slot_function_type& slot,
146                   PBD::EventLoop* event_loop) {
147             if (ir) {
148                     ir->event_loop = event_loop;
149             }
150             c = _signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot));
151     }
152
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).
156     */
157     
158     typename SignalType::result_type operator()() {
159             return _signal ();
160     }
161
162     /** Return true if there is nothing connected to this signal, false
163      *  otherwise.
164      */
165
166     bool empty() const { return _signal.empty(); }
167     
168 private:
169     SignalType _signal;
170 };
171
172 template<typename R, typename A, typename C = boost::signals2::optional_last_value<R> >
173 class Signal1 {
174 public:
175     Signal1 () {}
176     typedef boost::signals2::signal<R(A), C> SignalType;
177
178     void connect_same_thread (ScopedConnectionList& clist, 
179                   const typename SignalType::slot_function_type& slot) {
180             clist.add_connection (_signal.connect (slot));
181     }
182
183     void connect_same_thread (ScopedConnection& c, 
184                      const typename SignalType::slot_function_type& slot) {
185             c = _signal.connect (slot);
186     }
187
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));
190     }
191
192     void connect (ScopedConnectionList& clist, 
193                   PBD::EventLoop::InvalidationRecord* ir, 
194                   const typename SignalType::slot_function_type& slot,
195                   PBD::EventLoop* event_loop) {
196             if (ir) {
197                     ir->event_loop = event_loop;
198             }
199             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1)));
200     }
201
202     void connect (ScopedConnection& c, 
203                   PBD::EventLoop::InvalidationRecord* ir, 
204                   const typename SignalType::slot_function_type& slot,
205                   PBD::EventLoop* event_loop) {
206             if (ir) {
207                     ir->event_loop = event_loop;
208             }
209             c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1));
210
211     }
212     
213     typename SignalType::result_type operator()(A arg1) {
214             return _signal (arg1);
215     }
216     
217     bool empty() const { return _signal.empty(); }
218
219 private:
220     SignalType _signal;
221 };
222
223 template<typename R, typename A1, typename A2>
224 class Signal2 {
225 public:
226     Signal2 () {}
227     typedef boost::signals2::signal<R(A1, A2)> SignalType;
228
229     void connect_same_thread (ScopedConnectionList& clist, 
230                   const typename SignalType::slot_function_type& slot) {
231             clist.add_connection (_signal.connect (slot));
232     }
233
234     void connect_same_thread (ScopedConnection& c, 
235                      const typename SignalType::slot_function_type& slot) {
236             c = _signal.connect (slot);
237     }
238
239     static void compositor (typename boost::function<void(A1,A2)> f, PBD::EventLoop* event_loop, 
240                             EventLoop::InvalidationRecord* ir,
241                             A1 arg1, A2 arg2) {
242             event_loop->call_slot (ir, boost::bind (f, arg1, arg2));
243     }
244
245     void connect (ScopedConnectionList& clist, 
246                   PBD::EventLoop::InvalidationRecord* ir, 
247                   const typename SignalType::slot_function_type& slot,
248                   PBD::EventLoop* event_loop) {
249             if (ir) {
250                     ir->event_loop = event_loop;
251             }
252             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2)));
253     }
254
255     void connect (ScopedConnection& c, 
256                   PBD::EventLoop::InvalidationRecord* ir, 
257                   const typename SignalType::slot_function_type& slot,
258                   PBD::EventLoop* event_loop) {
259             if (ir) {
260                     ir->event_loop = event_loop;
261             }
262             c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2));
263     }
264
265     typename SignalType::result_type operator()(A1 arg1, A2 arg2) {
266             return _signal (arg1, arg2);
267     }
268     
269     bool empty() const { return _signal.empty(); }
270
271 private:
272     SignalType _signal;
273 };
274
275 template<typename R, typename A1, typename A2, typename A3>
276 class Signal3 {
277 public:
278     Signal3 () {}
279     typedef boost::signals2::signal<R(A1,A2,A3)> SignalType;
280
281     void connect_same_thread (ScopedConnectionList& clist, 
282                   const typename SignalType::slot_function_type& slot) {
283             clist.add_connection (_signal.connect (slot));
284     }
285
286     void connect_same_thread (ScopedConnection& c, 
287                               const typename SignalType::slot_function_type& slot) {
288             c = _signal.connect (slot);
289     }
290
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));
295     }
296
297     void connect (ScopedConnectionList& clist, 
298                   PBD::EventLoop::InvalidationRecord* ir, 
299                   const typename SignalType::slot_function_type& slot,
300                   PBD::EventLoop* event_loop) {
301             if (ir) {
302                     ir->event_loop = event_loop;
303             }
304             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
305     }
306     
307     void connect (ScopedConnection& c, 
308                   PBD::EventLoop::InvalidationRecord* ir, 
309                   const typename SignalType::slot_function_type& slot,
310                   PBD::EventLoop* event_loop) {
311             if (ir) {
312                     ir->event_loop = event_loop;
313             }
314             c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
315     }
316     
317     typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3) {
318             return _signal (arg1, arg2, arg3);
319     }
320     
321     bool empty() const { return _signal.empty(); }
322
323 private:
324     SignalType _signal;
325 };
326
327 template<typename R, typename A1, typename A2, typename A3, typename A4>
328 class Signal4 {
329 public:
330     Signal4 () {}
331     typedef boost::signals2::signal<R(A1,A2,A3,A4)> SignalType;
332
333     void connect_same_thread (ScopedConnectionList& clist, 
334                   const typename SignalType::slot_function_type& slot) {
335             clist.add_connection (_signal.connect (slot));
336     }
337
338     void connect_same_thread (ScopedConnection& c, 
339                               const typename SignalType::slot_function_type& slot) {
340             c = _signal.connect (slot);
341     }
342
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));
347     }
348
349     void connect (ScopedConnectionList& clist, 
350                   PBD::EventLoop::InvalidationRecord* ir, 
351                   const typename SignalType::slot_function_type& slot,
352                   PBD::EventLoop* event_loop) {
353             if (ir) {
354                     ir->event_loop = event_loop;
355             }
356             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
357     }
358     
359     void connect (ScopedConnection& c, 
360                   PBD::EventLoop::InvalidationRecord* ir, 
361                   const typename SignalType::slot_function_type& slot,
362                   PBD::EventLoop* event_loop) {
363             if (ir) {
364                     ir->event_loop = event_loop;
365             }
366             c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
367     }
368     
369     typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
370             return _signal (arg1, arg2, arg3, arg4);
371     }
372     
373     bool empty() const { return _signal.empty(); }
374
375 private:
376     SignalType _signal;
377 };
378
379 template<typename R, typename A1, typename A2, typename A3, typename A4, typename A5>
380 class Signal5 {
381 public:
382     Signal5 () {}
383     typedef boost::signals2::signal<R(A1,A2,A3,A4,A5)> SignalType;
384
385     void connect_same_thread (ScopedConnectionList& clist, 
386                   const typename SignalType::slot_function_type& slot) {
387             clist.add_connection (_signal.connect (slot));
388     }
389
390     void connect_same_thread (ScopedConnection& c, 
391                               const typename SignalType::slot_function_type& slot) {
392             c = _signal.connect (slot);
393     }
394
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));
399     }
400
401     void connect (ScopedConnectionList& clist, 
402                   PBD::EventLoop::InvalidationRecord* ir, 
403                   const typename SignalType::slot_function_type& slot,
404                   PBD::EventLoop* event_loop) {
405             if (ir) {
406                     ir->event_loop = event_loop;
407             }
408             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5)));
409     }
410     
411     void connect (ScopedConnection& c, 
412                   PBD::EventLoop::InvalidationRecord* ir, 
413                   const typename SignalType::slot_function_type& slot,
414                   PBD::EventLoop* event_loop) {
415             if (ir) {
416                     ir->event_loop = event_loop;
417             }
418             c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5)));
419     }
420     
421     typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) {
422             return _signal (arg1, arg2, arg3, arg4, arg5);
423     }
424     
425     bool empty() const { return _signal.empty(); }
426
427 private:
428     SignalType _signal;
429 };
430         
431 } /* namespace */
432
433 #endif /* __pbd_signals_h__ */