Must disconnect old UnscopedConnection when setting a new one in operator=, otherwise...
[ardour.git] / libs / pbd / pbd / signals.h
1 /*
2     Copyright (C) 2009-2012 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 #include <boost/function.hpp>
30 #include <boost/thread/mutex.hpp>
31 #include <boost/enable_shared_from_this.hpp>
32 #include <boost/optional.hpp>
33
34 #include "pbd/event_loop.h"
35
36 namespace PBD {
37
38 class Connection;
39
40 class SignalBase
41 {
42 public:
43         virtual ~SignalBase () {}
44         virtual void disconnect (boost::shared_ptr<Connection>) = 0;
45
46 protected:
47         boost::mutex _mutex;
48 };
49
50 class Connection : public boost::enable_shared_from_this<Connection>
51 {
52 public:
53         Connection (SignalBase* b) : _signal (b) {}
54
55         void disconnect ()
56         {
57                 boost::mutex::scoped_lock lm (_mutex);
58                 if (_signal) {
59                         _signal->disconnect (shared_from_this ());
60                         _signal = 0;
61                 }
62         }
63
64         void signal_going_away ()
65         {
66                 boost::mutex::scoped_lock lm (_mutex);
67                 _signal = 0;
68         }
69
70 private:
71         boost::mutex _mutex;
72         SignalBase* _signal;
73 };
74
75 template<typename R>
76 class OptionalLastValue
77 {
78 public:
79         typedef boost::optional<R> result_type;
80
81         template <typename Iter>
82         result_type operator() (Iter first, Iter last) const {
83                 result_type r;
84                 while (first != last) {
85                         r = *first;
86                         ++first;
87                 }
88
89                 return r;
90         }
91 };
92         
93 typedef boost::shared_ptr<Connection> UnscopedConnection;
94         
95 class ScopedConnection
96 {
97 public:
98         ScopedConnection () {}
99         ScopedConnection (UnscopedConnection c) : _c (c) {}
100         ~ScopedConnection () {
101                 disconnect ();
102         }
103
104         void disconnect ()
105         {
106                 if (_c) {
107                         _c->disconnect ();
108                 }
109         }
110
111         ScopedConnection& operator= (UnscopedConnection const & o)
112         {
113                 if (_c == o) {
114                         return *this;
115                 }
116                 
117                 disconnect ();
118                 _c = o;
119                 return *this;
120         }
121
122 private:
123         UnscopedConnection _c;
124 };
125         
126 class ScopedConnectionList  : public boost::noncopyable
127 {
128   public:
129         ScopedConnectionList();
130         virtual ~ScopedConnectionList ();
131         
132         void add_connection (const UnscopedConnection& c);
133         void drop_connections ();
134
135   private:
136         /* this class is not copyable */
137         ScopedConnectionList(const ScopedConnectionList&);
138
139         /* Even though our signals code is thread-safe, this additional list of
140            scoped connections needs to be protected in 2 cases:
141
142            (1) (unlikely) we make a connection involving a callback on the
143                same object from 2 threads. (wouldn't that just be appalling 
144                programming style?)
145              
146            (2) where we are dropping connections in one thread and adding
147                one from another.
148          */
149
150         Glib::Mutex _lock;
151
152         typedef std::list<ScopedConnection*> ConnectionList;
153         ConnectionList _list;
154 };
155
156 #include "pbd/signals_generated.h"      
157         
158 } /* namespace */
159
160 #endif /* __pbd_signals_h__ */