7cdbcef414cc2d7da5f2b0d7c9d7156fdff3a043
[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/noncopyable.hpp>
27 #include <boost/bind.hpp>
28 #include <boost/bind/protect.hpp>
29
30 #include "pbd/event_loop.h"
31 #include "pbd/signal.h"
32
33 namespace PBD {
34
35 typedef boost::shared_ptr<Connection> UnscopedConnection;
36 class ScopedConnection
37 {
38 public:
39         ScopedConnection () {}
40         ScopedConnection (UnscopedConnection c) : _c (c) {}
41         ~ScopedConnection () {
42                 disconnect ();
43         }
44
45         void disconnect ()
46         {
47                 if (_c) {
48                         _c->disconnect ();
49                 }
50         }
51
52         ScopedConnection& operator= (UnscopedConnection const & o)
53         {
54                 _c = o;
55                 return *this;
56         }
57
58 private:
59         UnscopedConnection _c;
60 };
61         
62 class ScopedConnectionList  : public boost::noncopyable
63 {
64   public:
65         ScopedConnectionList();
66         virtual ~ScopedConnectionList ();
67         
68         void add_connection (const UnscopedConnection& c);
69         void drop_connections ();
70
71   private:
72         /* this class is not copyable */
73         ScopedConnectionList(const ScopedConnectionList&);
74
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:
82
83            (1) (unlikely) we make a connection involving a callback on the
84                same object from 2 threads. (wouldn't that just be appalling 
85                programming style?)
86              
87            (2) where we are dropping connections in one thread and adding
88                one from another.
89          */
90
91         static Glib::StaticMutex _lock;
92
93         typedef std::list<ScopedConnection*> ConnectionList;
94         ConnectionList _list;
95 };
96
97 template<typename R>
98 class Signal0 {
99 public:
100     typedef SimpleSignal0<R> SignalType;
101         
102     Signal0 () : _signal (SignalType::create ()) {}
103
104     /** Arrange for @a slot to be executed whenever this signal is emitted. 
105         Store the connection that represents this arrangement in @a c.
106
107         NOTE: @a slot will be executed in the same thread that the signal is
108         emitted in.
109     */
110
111     void connect_same_thread (ScopedConnection& c, 
112                   const typename SignalType::slot_function_type& slot) {
113             c = _signal->connect (slot);
114     }
115
116     /** Arrange for @a slot to be executed whenever this signal is emitted. 
117         Add the connection that represents this arrangement to @a clist.
118
119         NOTE: @a slot will be executed in the same thread that the signal is
120         emitted in.
121     */
122         
123     void connect_same_thread (ScopedConnectionList& clist, 
124                   const typename SignalType::slot_function_type& slot) {
125             clist.add_connection (_signal->connect (slot));
126     }
127
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.
131         
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.
137         
138         "outliving the lifetime" doesn't have a specific, detailed meaning,
139         but is best illustrated by two contrasting examples:
140         
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 
144         highly advisable.
145         
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.
151     */
152
153     void connect (ScopedConnectionList& clist, 
154                   PBD::EventLoop::InvalidationRecord* ir, 
155                   const typename SignalType::slot_function_type& slot,
156                   PBD::EventLoop* event_loop) {
157             if (ir) {
158                     ir->event_loop = event_loop;
159             }
160             clist.add_connection (_signal->connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot)));
161     }
162
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.
166      */
167
168     void connect (ScopedConnection& c, 
169                   PBD::EventLoop::InvalidationRecord* ir, 
170                   const typename SignalType::slot_function_type& slot,
171                   PBD::EventLoop* event_loop) {
172             if (ir) {
173                     ir->event_loop = event_loop;
174             }
175             c = _signal->connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot));
176     }
177
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).
181     */
182     
183     typename SignalType::result_type operator()() {
184             return _signal->emit ();
185     }
186
187     /** Return true if there is nothing connected to this signal, false
188      *  otherwise.
189      */
190
191     bool empty() const { return _signal->empty(); }
192     
193 private:
194    boost::shared_ptr<SignalType> _signal;
195 };
196
197 template<typename R, typename A, typename C = OptionalLastValue<R> >
198 class Signal1 {
199 public:
200     typedef SimpleSignal1<R, A, C> SignalType;
201     Signal1 () : _signal (SignalType::create()) {}
202
203     void connect_same_thread (ScopedConnectionList& clist, 
204                   const typename SignalType::slot_function_type& slot) {
205             clist.add_connection (_signal->connect (slot));
206     }
207
208     void connect_same_thread (ScopedConnection& c, 
209                      const typename SignalType::slot_function_type& slot) {
210             c = _signal->connect (slot);
211     }
212
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));
215     }
216
217     void connect (ScopedConnectionList& clist, 
218                   PBD::EventLoop::InvalidationRecord* ir, 
219                   const typename SignalType::slot_function_type& slot,
220                   PBD::EventLoop* event_loop) {
221             if (ir) {
222                     ir->event_loop = event_loop;
223             }
224             clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1)));
225     }
226
227     void connect (ScopedConnection& c, 
228                   PBD::EventLoop::InvalidationRecord* ir, 
229                   const typename SignalType::slot_function_type& slot,
230                   PBD::EventLoop* event_loop) {
231             if (ir) {
232                     ir->event_loop = event_loop;
233             }
234             c = _signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1));
235
236     }
237     
238     typename SignalType::result_type operator()(A arg1) {
239             return _signal->emit (arg1);
240     }
241     
242     bool empty() const { return _signal->empty(); }
243
244 private:
245     boost::shared_ptr<SignalType> _signal;
246 };
247
248 template<typename R, typename A1, typename A2>
249 class Signal2 {
250 public:
251     typedef SimpleSignal2<R, A1, A2> SignalType;
252     Signal2 () : _signal (SignalType::create()) {}
253
254     void connect_same_thread (ScopedConnectionList& clist, 
255                   const typename SignalType::slot_function_type& slot) {
256             clist.add_connection (_signal->connect (slot));
257     }
258
259     void connect_same_thread (ScopedConnection& c, 
260                      const typename SignalType::slot_function_type& slot) {
261             c = _signal->connect (slot);
262     }
263
264     static void compositor (typename boost::function<void(A1,A2)> f, PBD::EventLoop* event_loop, 
265                             EventLoop::InvalidationRecord* ir,
266                             A1 arg1, A2 arg2) {
267             event_loop->call_slot (ir, boost::bind (f, arg1, arg2));
268     }
269
270     void connect (ScopedConnectionList& clist, 
271                   PBD::EventLoop::InvalidationRecord* ir, 
272                   const typename SignalType::slot_function_type& slot,
273                   PBD::EventLoop* event_loop) {
274             if (ir) {
275                     ir->event_loop = event_loop;
276             }
277             clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2)));
278     }
279
280     void connect (ScopedConnection& c, 
281                   PBD::EventLoop::InvalidationRecord* ir, 
282                   const typename SignalType::slot_function_type& slot,
283                   PBD::EventLoop* event_loop) {
284             if (ir) {
285                     ir->event_loop = event_loop;
286             }
287             c = _signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2));
288     }
289
290     typename SignalType::result_type operator()(A1 arg1, A2 arg2) {
291             return _signal->emit (arg1, arg2);
292     }
293     
294     bool empty() const { return _signal->empty(); }
295
296 private:
297     boost::shared_ptr<SignalType> _signal;
298 };
299
300 template<typename R, typename A1, typename A2, typename A3>
301 class Signal3 {
302 public:
303     typedef SimpleSignal3<R, A1, A2, A3> SignalType;
304     Signal3 () : _signal (SignalType::create()) {}
305
306     void connect_same_thread (ScopedConnectionList& clist, 
307                   const typename SignalType::slot_function_type& slot) {
308             clist.add_connection (_signal->connect (slot));
309     }
310
311     void connect_same_thread (ScopedConnection& c, 
312                               const typename SignalType::slot_function_type& slot) {
313             c = _signal->connect (slot);
314     }
315
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));
320     }
321
322     void connect (ScopedConnectionList& clist, 
323                   PBD::EventLoop::InvalidationRecord* ir, 
324                   const typename SignalType::slot_function_type& slot,
325                   PBD::EventLoop* event_loop) {
326             if (ir) {
327                     ir->event_loop = event_loop;
328             }
329             clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
330     }
331     
332     void connect (ScopedConnection& c, 
333                   PBD::EventLoop::InvalidationRecord* ir, 
334                   const typename SignalType::slot_function_type& slot,
335                   PBD::EventLoop* event_loop) {
336             if (ir) {
337                     ir->event_loop = event_loop;
338             }
339             c = _signal->connect (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
340     }
341     
342     typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3) {
343             return _signal->emit (arg1, arg2, arg3);
344     }
345     
346     bool empty() const { return _signal->empty(); }
347
348 private:
349     boost::shared_ptr<SignalType> _signal;
350 };
351
352 template<typename R, typename A1, typename A2, typename A3, typename A4>
353 class Signal4 {
354 public:
355     typedef SimpleSignal4<R, A1, A2, A3, A4> SignalType;
356     Signal4 () : _signal (SignalType::create()) {}
357
358     void connect_same_thread (ScopedConnectionList& clist, 
359                   const typename SignalType::slot_function_type& slot) {
360             clist.add_connection (_signal->connect (slot));
361     }
362
363     void connect_same_thread (ScopedConnection& c, 
364                               const typename SignalType::slot_function_type& slot) {
365             c = _signal->connect (slot);
366     }
367
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));
372     }
373
374     void connect (ScopedConnectionList& clist, 
375                   PBD::EventLoop::InvalidationRecord* ir, 
376                   const typename SignalType::slot_function_type& slot,
377                   PBD::EventLoop* event_loop) {
378             if (ir) {
379                     ir->event_loop = event_loop;
380             }
381             clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
382     }
383     
384     void connect (ScopedConnection& c, 
385                   PBD::EventLoop::InvalidationRecord* ir, 
386                   const typename SignalType::slot_function_type& slot,
387                   PBD::EventLoop* event_loop) {
388             if (ir) {
389                     ir->event_loop = event_loop;
390             }
391             c = _signal->connect (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
392     }
393     
394     typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
395             return _signal->emit (arg1, arg2, arg3, arg4);
396     }
397     
398     bool empty() const { return _signal->empty(); }
399
400 private:
401     boost::shared_ptr<SignalType> _signal;
402 };
403
404 template<typename R, typename A1, typename A2, typename A3, typename A4, typename A5>
405 class Signal5 {
406 public:
407     typedef SimpleSignal5<R, A1, A2, A3, A4, A5> SignalType;
408     Signal5 () : _signal (SignalType::create()) {}
409
410     void connect_same_thread (ScopedConnectionList& clist, 
411                   const typename SignalType::slot_function_type& slot) {
412             clist.add_connection (_signal->connect (slot));
413     }
414
415     void connect_same_thread (ScopedConnection& c, 
416                               const typename SignalType::slot_function_type& slot) {
417             c = _signal->connect (slot);
418     }
419
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));
424     }
425
426     void connect (ScopedConnectionList& clist, 
427                   PBD::EventLoop::InvalidationRecord* ir, 
428                   const typename SignalType::slot_function_type& slot,
429                   PBD::EventLoop* event_loop) {
430             if (ir) {
431                     ir->event_loop = event_loop;
432             }
433             clist.add_connection (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5)));
434     }
435     
436     void connect (ScopedConnection& c, 
437                   PBD::EventLoop::InvalidationRecord* ir, 
438                   const typename SignalType::slot_function_type& slot,
439                   PBD::EventLoop* event_loop) {
440             if (ir) {
441                     ir->event_loop = event_loop;
442             }
443             c = _signal->connect (_signal->connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4, _5)));
444     }
445     
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);
448     }
449     
450     bool empty() const { return _signal->empty(); }
451
452 private:
453     boost::shared_ptr<SignalType> _signal;
454 };
455         
456 } /* namespace */
457
458 #endif /* __pbd_signals_h__ */