1 /*
2   boost::signals2::connection provides a handle to a signal/slot connection.
3 
4   Author: Frank Mori Hess <fmhess@users.sourceforge.net>
5   Begin: 2007-01-23
6 */
7 // Copyright Frank Mori Hess 2007-2008.
8 // Distributed under the Boost Software License, Version
9 // 1.0. (See accompanying file LICENSE_1_0.txt or copy at
10 // http://www.boost.org/LICENSE_1_0.txt)
11 
12 // See http://www.boost.org/libs/signals2 for library home page.
13 
14 #ifndef BOOST_SIGNALS2_CONNECTION_HPP
15 #define BOOST_SIGNALS2_CONNECTION_HPP
16 
17 #include <boost/config.hpp>
18 #include <boost/function.hpp>
19 #include <boost/mpl/bool.hpp>
20 #include <boost/noncopyable.hpp>
21 #include <boost/shared_ptr.hpp>
22 #include <boost/signals2/detail/auto_buffer.hpp>
23 #include <boost/signals2/detail/null_output_iterator.hpp>
24 #include <boost/signals2/detail/unique_lock.hpp>
25 #include <boost/signals2/slot.hpp>
26 #include <boost/weak_ptr.hpp>
27 
28 namespace boost
29 {
30   namespace signals2
31   {
null_deleter(const void *)32     inline void null_deleter(const void*) {}
33     namespace detail
34     {
35       // This lock maintains a list of shared_ptr<void>
36       // which will be destroyed only after the lock
37       // has released its mutex.  Used to garbage
38       // collect disconnected slots
39       template<typename Mutex>
40       class garbage_collecting_lock: public noncopyable
41       {
42       public:
garbage_collecting_lock(Mutex & m)43         garbage_collecting_lock(Mutex &m):
44           lock(m)
45         {}
add_trash(const shared_ptr<void> & piece_of_trash)46         void add_trash(const shared_ptr<void> &piece_of_trash)
47         {
48           garbage.push_back(piece_of_trash);
49         }
50       private:
51         // garbage must be declared before lock
52         // to insure it is destroyed after lock is
53         // destroyed.
54         auto_buffer<shared_ptr<void>, store_n_objects<10> > garbage;
55         unique_lock<Mutex> lock;
56       };
57 
58       class connection_body_base
59       {
60       public:
connection_body_base()61         connection_body_base():
62           _connected(true), m_slot_refcount(1)
63         {
64         }
~connection_body_base()65         virtual ~connection_body_base() {}
disconnect()66         void disconnect()
67         {
68           garbage_collecting_lock<connection_body_base> local_lock(*this);
69           nolock_disconnect(local_lock);
70         }
71         template<typename Mutex>
nolock_disconnect(garbage_collecting_lock<Mutex> & lock_arg) const72         void nolock_disconnect(garbage_collecting_lock<Mutex> &lock_arg) const
73         {
74           if(_connected)
75           {
76             _connected = false;
77             dec_slot_refcount(lock_arg);
78           }
79         }
80         virtual bool connected() const = 0;
get_blocker()81         shared_ptr<void> get_blocker()
82         {
83           unique_lock<connection_body_base> local_lock(*this);
84           shared_ptr<void> blocker = _weak_blocker.lock();
85           if(blocker == shared_ptr<void>())
86           {
87             blocker.reset(this, &null_deleter);
88             _weak_blocker = blocker;
89           }
90           return blocker;
91         }
blocked() const92         bool blocked() const
93         {
94           return !_weak_blocker.expired();
95         }
nolock_nograb_blocked() const96         bool nolock_nograb_blocked() const
97         {
98           return nolock_nograb_connected() == false || blocked();
99         }
nolock_nograb_connected() const100         bool nolock_nograb_connected() const {return _connected;}
101         // expose part of Lockable concept of mutex
102         virtual void lock() = 0;
103         virtual void unlock() = 0;
104 
105         // Slot refcount should be incremented while
106         // a signal invocation is using the slot, in order
107         // to prevent slot from being destroyed mid-invocation.
108         // garbage_collecting_lock parameter enforces
109         // the existance of a lock before this
110         // method is called
111         template<typename Mutex>
inc_slot_refcount(const garbage_collecting_lock<Mutex> &)112         void inc_slot_refcount(const garbage_collecting_lock<Mutex> &)
113         {
114           BOOST_ASSERT(m_slot_refcount != 0);
115           ++m_slot_refcount;
116         }
117         // if slot refcount decrements to zero due to this call,
118         // it puts a
119         // shared_ptr to the slot in the garbage collecting lock,
120         // which will destroy the slot only after it unlocks.
121         template<typename Mutex>
dec_slot_refcount(garbage_collecting_lock<Mutex> & lock_arg) const122         void dec_slot_refcount(garbage_collecting_lock<Mutex> &lock_arg) const
123         {
124           BOOST_ASSERT(m_slot_refcount != 0);
125           if(--m_slot_refcount == 0)
126           {
127             lock_arg.add_trash(release_slot());
128           }
129         }
130 
131       protected:
132         virtual shared_ptr<void> release_slot() const = 0;
133 
134         weak_ptr<void> _weak_blocker;
135       private:
136         mutable bool _connected;
137         mutable unsigned m_slot_refcount;
138       };
139 
140       template<typename GroupKey, typename SlotType, typename Mutex>
141       class connection_body: public connection_body_base
142       {
143       public:
144         typedef Mutex mutex_type;
connection_body(const SlotType & slot_in,const boost::shared_ptr<mutex_type> & signal_mutex)145         connection_body(const SlotType &slot_in, const boost::shared_ptr<mutex_type> &signal_mutex):
146           m_slot(new SlotType(slot_in)), _mutex(signal_mutex)
147         {
148         }
~connection_body()149         virtual ~connection_body() {}
connected() const150         virtual bool connected() const
151         {
152           garbage_collecting_lock<mutex_type> local_lock(*_mutex);
153           nolock_grab_tracked_objects(local_lock, detail::null_output_iterator());
154           return nolock_nograb_connected();
155         }
group_key() const156         const GroupKey& group_key() const {return _group_key;}
set_group_key(const GroupKey & key)157         void set_group_key(const GroupKey &key) {_group_key = key;}
158         template<typename M>
disconnect_expired_slot(garbage_collecting_lock<M> & lock_arg)159         void disconnect_expired_slot(garbage_collecting_lock<M> &lock_arg)
160         {
161           if(!m_slot) return;
162           bool expired = slot().expired();
163           if(expired == true)
164           {
165             nolock_disconnect(lock_arg);
166           }
167         }
168         template<typename M, typename OutputIterator>
nolock_grab_tracked_objects(garbage_collecting_lock<M> & lock_arg,OutputIterator inserter) const169         void nolock_grab_tracked_objects(garbage_collecting_lock<M> &lock_arg,
170           OutputIterator inserter) const
171         {
172           if(!m_slot) return;
173           slot_base::tracked_container_type::const_iterator it;
174           for(it = slot().tracked_objects().begin();
175             it != slot().tracked_objects().end();
176             ++it)
177           {
178             void_shared_ptr_variant locked_object
179             (
180               apply_visitor
181               (
182                 detail::lock_weak_ptr_visitor(),
183                 *it
184               )
185             );
186             if(apply_visitor(detail::expired_weak_ptr_visitor(), *it))
187             {
188               nolock_disconnect(lock_arg);
189               return;
190             }
191             *inserter++ = locked_object;
192           }
193         }
194         // expose Lockable concept of mutex
lock()195         virtual void lock()
196         {
197           _mutex->lock();
198         }
unlock()199         virtual void unlock()
200         {
201           _mutex->unlock();
202         }
slot()203         SlotType &slot()
204         {
205           return *m_slot;
206         }
slot() const207         const SlotType &slot() const
208         {
209           return *m_slot;
210         }
211       protected:
release_slot() const212         virtual shared_ptr<void> release_slot() const
213         {
214 
215           shared_ptr<void> released_slot = m_slot;
216           m_slot.reset();
217           return released_slot;
218         }
219       private:
220         mutable boost::shared_ptr<SlotType> m_slot;
221         const boost::shared_ptr<mutex_type> _mutex;
222         GroupKey _group_key;
223       };
224     }
225 
226     class shared_connection_block;
227 
228     class connection
229     {
230     public:
231       friend class shared_connection_block;
232 
connection()233       connection() BOOST_NOEXCEPT {}
connection(const connection & other)234       connection(const connection &other) BOOST_NOEXCEPT: _weak_connection_body(other._weak_connection_body)
235       {}
connection(const boost::weak_ptr<detail::connection_body_base> & connectionBody)236       connection(const boost::weak_ptr<detail::connection_body_base> &connectionBody) BOOST_NOEXCEPT:
237         _weak_connection_body(connectionBody)
238       {}
239 
240       // move support
241 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
connection(connection && other)242       connection(connection && other) BOOST_NOEXCEPT: _weak_connection_body(std::move(other._weak_connection_body))
243       {
244         // make sure other is reset, in case it is a scoped_connection (so it
245         // won't disconnect on destruction after being moved away from).
246         other._weak_connection_body.reset();
247       }
operator =(connection && other)248       connection & operator=(connection && other) BOOST_NOEXCEPT
249       {
250         if(&other == this) return *this;
251         _weak_connection_body = std::move(other._weak_connection_body);
252         // make sure other is reset, in case it is a scoped_connection (so it
253         // won't disconnect on destruction after being moved away from).
254         other._weak_connection_body.reset();
255         return *this;
256       }
257 #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
operator =(const connection & other)258       connection & operator=(const connection & other) BOOST_NOEXCEPT
259       {
260         if(&other == this) return *this;
261         _weak_connection_body = other._weak_connection_body;
262         return *this;
263       }
264 
~connection()265       ~connection() {}
disconnect() const266       void disconnect() const
267       {
268         boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
269         if(connectionBody == 0) return;
270         connectionBody->disconnect();
271       }
connected() const272       bool connected() const
273       {
274         boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
275         if(connectionBody == 0) return false;
276         return connectionBody->connected();
277       }
blocked() const278       bool blocked() const
279       {
280         boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
281         if(connectionBody == 0) return true;
282         return connectionBody->blocked();
283       }
operator ==(const connection & other) const284       bool operator==(const connection& other) const
285       {
286         boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
287         boost::shared_ptr<detail::connection_body_base> otherConnectionBody(other._weak_connection_body.lock());
288         return connectionBody == otherConnectionBody;
289       }
operator !=(const connection & other) const290       bool operator!=(const connection& other) const
291       {
292         return !(*this == other);
293       }
operator <(const connection & other) const294       bool operator<(const connection& other) const
295       {
296         boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
297         boost::shared_ptr<detail::connection_body_base> otherConnectionBody(other._weak_connection_body.lock());
298         return connectionBody < otherConnectionBody;
299       }
swap(connection & other)300       void swap(connection &other) BOOST_NOEXCEPT
301       {
302         using std::swap;
303         swap(_weak_connection_body, other._weak_connection_body);
304       }
305     protected:
306 
307       boost::weak_ptr<detail::connection_body_base> _weak_connection_body;
308     };
swap(connection & conn1,connection & conn2)309     inline void swap(connection &conn1, connection &conn2) BOOST_NOEXCEPT
310     {
311       conn1.swap(conn2);
312     }
313 
314     class scoped_connection: public connection
315     {
316     public:
scoped_connection()317       scoped_connection() BOOST_NOEXCEPT {}
scoped_connection(const connection & other)318       scoped_connection(const connection &other) BOOST_NOEXCEPT:
319         connection(other)
320       {}
~scoped_connection()321       ~scoped_connection()
322       {
323         disconnect();
324       }
operator =(const connection & rhs)325       scoped_connection& operator=(const connection &rhs) BOOST_NOEXCEPT
326       {
327         disconnect();
328         connection::operator=(rhs);
329         return *this;
330       }
331 
332       // move support
333 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
scoped_connection(scoped_connection && other)334       scoped_connection(scoped_connection && other) BOOST_NOEXCEPT: connection(std::move(other))
335       {
336       }
scoped_connection(connection && other)337       scoped_connection(connection && other) BOOST_NOEXCEPT: connection(std::move(other))
338       {
339       }
operator =(scoped_connection && other)340       scoped_connection & operator=(scoped_connection && other) BOOST_NOEXCEPT
341       {
342         if(&other == this) return *this;
343         disconnect();
344         connection::operator=(std::move(other));
345         return *this;
346       }
operator =(connection && other)347       scoped_connection & operator=(connection && other) BOOST_NOEXCEPT
348       {
349         if(&other == this) return *this;
350         disconnect();
351         connection::operator=(std::move(other));
352         return *this;
353       }
354 #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
355 
release()356       connection release()
357       {
358         connection conn(_weak_connection_body);
359         _weak_connection_body.reset();
360         return conn;
361       }
362     private:
363       scoped_connection(const scoped_connection &other);
364       scoped_connection& operator=(const scoped_connection &rhs);
365     };
366     // Sun 5.9 compiler doesn't find the swap for base connection class when
367     // arguments are scoped_connection, so we provide this explicitly.
swap(scoped_connection & conn1,scoped_connection & conn2)368     inline void swap(scoped_connection &conn1, scoped_connection &conn2) BOOST_NOEXCEPT
369     {
370       conn1.swap(conn2);
371     }
372   }
373 }
374 
375 #endif  // BOOST_SIGNALS2_CONNECTION_HPP
376