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