Ardour  8.12
signals.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2009-2016 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2010-2012 Carl Hetherington <carl@carlh.net>
4  * Copyright (C) 2013 John Emmas <john@creativepost.co.uk>
5  * Copyright (C) 2015-2017 Robin Gareus <robin@gareus.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #ifndef __pbd_signals_h__
23 #define __pbd_signals_h__
24 
25 #include <csignal>
26 
27 #include <list>
28 #include <map>
29 
30 #ifdef nil
31 #undef nil
32 #endif
33 
34 #include <atomic>
35 
36 #include <glibmm/threads.h>
37 
38 #include <boost/noncopyable.hpp>
39 #include <boost/bind.hpp>
40 #include <boost/bind/protect.hpp>
41 #include <boost/function.hpp>
42 #include <boost/enable_shared_from_this.hpp>
43 #include <boost/optional.hpp>
44 
45 #include "pbd/libpbd_visibility.h"
46 #include "pbd/event_loop.h"
47 
48 #ifndef NDEBUG
49 #define DEBUG_PBD_SIGNAL_CONNECTIONS
50 #endif
51 
52 #ifdef DEBUG_PBD_SIGNAL_CONNECTIONS
53 #include "pbd/stacktrace.h"
54 #include <iostream>
55 #endif
56 
57 namespace PBD {
58 
59 class LIBPBD_API Connection;
60 
62 {
63 public:
65  : _in_dtor (false)
67  , _debug_connection (false)
68 #endif
69  {}
70  virtual ~SignalBase () { }
71  virtual void disconnect (std::shared_ptr<Connection>) = 0;
72 #ifdef DEBUG_PBD_SIGNAL_CONNECTIONS
73  void set_debug_connection (bool yn) { _debug_connection = yn; }
74 #endif
75 
76 protected:
77  mutable Glib::Threads::Mutex _mutex;
78  std::atomic<bool> _in_dtor;
79 #ifdef DEBUG_PBD_SIGNAL_CONNECTIONS
81 #endif
82 };
83 
84 class LIBPBD_API Connection : public std::enable_shared_from_this<Connection>
85 {
86 public:
88  : _signal (b)
89  , _invalidation_record (ir)
90  {
91  if (_invalidation_record) {
92  _invalidation_record->ref ();
93  }
94  }
95 
96  void disconnect ()
97  {
98  Glib::Threads::Mutex::Lock lm (_mutex);
99  SignalBase* signal = _signal.exchange (0, std::memory_order_acq_rel);
100  if (signal) {
101  /* It is safe to assume that signal has not been destructed.
102  * If ~Signal d'tor runs, it will call our signal_going_away()
103  * which will block until we're done here.
104  *
105  * This will lock Signal::_mutex, and call disconnected ()
106  * or return immediately if Signal is being destructed.
107  */
108  signal->disconnect (shared_from_this ());
109  }
110  }
111 
112  void disconnected ()
113  {
114  if (_invalidation_record) {
115  _invalidation_record->unref ();
116  }
117  }
118 
120  {
121  /* called with Signal::_mutex held */
122  if (!_signal.exchange (0, std::memory_order_acq_rel)) {
123  /* disconnect () grabbed the signal, but signal->disconnect()
124  * has not [yet] removed the entry from the list.
125  *
126  * Allow disconnect () to complete, which will
127  * be an effective NO-OP since SignalBase::_in_dtor is true,
128  * then we can proceed.
129  */
130  Glib::Threads::Mutex::Lock lm (_mutex);
131  }
132  if (_invalidation_record) {
133  _invalidation_record->unref ();
134  }
135  }
136 
137 private:
138  Glib::Threads::Mutex _mutex;
139  std::atomic<SignalBase*> _signal;
141 };
142 
143 template<typename R>
144 class /*LIBPBD_API*/ OptionalLastValue
145 {
146 public:
147  typedef boost::optional<R> result_type;
148 
149  template <typename Iter>
150  result_type operator() (Iter first, Iter last) const {
151  result_type r;
152  while (first != last) {
153  r = *first;
154  ++first;
155  }
156 
157  return r;
158  }
159 };
160 
161 typedef std::shared_ptr<Connection> UnscopedConnection;
162 
164 {
165 public:
169  disconnect ();
170  }
171 
172  void disconnect ()
173  {
174  if (_c) {
175  _c->disconnect ();
176  }
177  }
178 
179  ScopedConnection& operator= (UnscopedConnection const & o)
180  {
181  if (_c == o) {
182  return *this;
183  }
184 
185  disconnect ();
186  _c = o;
187  return *this;
188  }
189 
190  UnscopedConnection const & the_connection() const { return _c; }
191 
192 private:
194 };
195 
196 class LIBPBD_API ScopedConnectionList : public boost::noncopyable
197 {
198  public:
201 
204 
205  private:
206  /* this class is not copyable */
208 
209  /* Even though our signals code is thread-safe, this additional list of
210  scoped connections needs to be protected in 2 cases:
211 
212  (1) (unlikely) we make a connection involving a callback on the
213  same object from 2 threads. (wouldn't that just be appalling
214  programming style?)
215 
216  (2) where we are dropping connections in one thread and adding
217  one from another.
218  */
219 
220  Glib::Threads::Mutex _scoped_connection_lock;
221 
222  typedef std::list<ScopedConnection*> ConnectionList;
224 };
225 
226 #include "pbd/signals_generated.h"
227 
228 } /* namespace */
229 
230 #endif /* __pbd_signals_h__ */
void signal_going_away()
Definition: signals.h:119
void disconnect()
Definition: signals.h:96
std::atomic< SignalBase * > _signal
Definition: signals.h:139
Glib::Threads::Mutex _mutex
Definition: signals.h:138
PBD::EventLoop::InvalidationRecord * _invalidation_record
Definition: signals.h:140
void disconnected()
Definition: signals.h:112
Connection(SignalBase *b, PBD::EventLoop::InvalidationRecord *ir)
Definition: signals.h:87
result_type operator()(Iter first, Iter last) const
Definition: signals.h:150
boost::optional< R > result_type
Definition: signals.h:147
Glib::Threads::Mutex _scoped_connection_lock
Definition: signals.h:220
void add_connection(const UnscopedConnection &c)
ConnectionList _scoped_connection_list
Definition: signals.h:223
ScopedConnectionList(const ScopedConnectionList &)
std::list< ScopedConnection * > ConnectionList
Definition: signals.h:222
ScopedConnection(UnscopedConnection c)
Definition: signals.h:167
UnscopedConnection _c
Definition: signals.h:193
UnscopedConnection const & the_connection() const
Definition: signals.h:190
std::atomic< bool > _in_dtor
Definition: signals.h:78
virtual void disconnect(std::shared_ptr< Connection >)=0
virtual ~SignalBase()
Definition: signals.h:70
void set_debug_connection(bool yn)
Definition: signals.h:73
bool _debug_connection
Definition: signals.h:80
Glib::Threads::Mutex _mutex
Definition: signals.h:77
#define LIBPBD_API
Definition: axis_view.h:42
std::shared_ptr< Connection > UnscopedConnection
Definition: signals.h:161
#define DEBUG_PBD_SIGNAL_CONNECTIONS
Definition: signals.h:49