2 boost::signals2::connection provides a handle to a signal/slot connection.
4 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
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)
12 // See http://www.boost.org/libs/signals2 for library home page.
14 #ifndef BOOST_SIGNALS2_CONNECTION_HPP
15 #define BOOST_SIGNALS2_CONNECTION_HPP
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>
32 inline void null_deleter(const void*) {}
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
43 garbage_collecting_lock(Mutex &m):
46 void add_trash(const shared_ptr<void> &piece_of_trash)
48 garbage.push_back(piece_of_trash);
51 // garbage must be declared before lock
52 // to insure it is destroyed after lock is
54 auto_buffer<shared_ptr<void>, store_n_objects<10> > garbage;
55 unique_lock<Mutex> lock;
58 class connection_body_base
61 connection_body_base():
62 _connected(true), m_slot_refcount(1)
65 virtual ~connection_body_base() {}
68 garbage_collecting_lock<connection_body_base> local_lock(*this);
69 nolock_disconnect(local_lock);
71 template<typename Mutex>
72 void nolock_disconnect(garbage_collecting_lock<Mutex> &lock_arg) const
77 dec_slot_refcount(lock_arg);
80 virtual bool connected() const = 0;
81 shared_ptr<void> get_blocker()
83 unique_lock<connection_body_base> local_lock(*this);
84 shared_ptr<void> blocker = _weak_blocker.lock();
85 if(blocker == shared_ptr<void>())
87 blocker.reset(this, &null_deleter);
88 _weak_blocker = blocker;
94 return !_weak_blocker.expired();
96 bool nolock_nograb_blocked() const
98 return nolock_nograb_connected() == false || blocked();
100 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;
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
111 template<typename Mutex>
112 void inc_slot_refcount(const garbage_collecting_lock<Mutex> &)
114 BOOST_ASSERT(m_slot_refcount != 0);
117 // if slot refcount decrements to zero due to this call,
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>
122 void dec_slot_refcount(garbage_collecting_lock<Mutex> &lock_arg) const
124 BOOST_ASSERT(m_slot_refcount != 0);
125 if(--m_slot_refcount == 0)
127 lock_arg.add_trash(release_slot());
132 virtual shared_ptr<void> release_slot() const = 0;
134 weak_ptr<void> _weak_blocker;
136 mutable bool _connected;
137 mutable unsigned m_slot_refcount;
140 template<typename GroupKey, typename SlotType, typename Mutex>
141 class connection_body: public connection_body_base
144 typedef Mutex mutex_type;
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)
149 virtual ~connection_body() {}
150 virtual bool connected() const
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();
156 const GroupKey& group_key() const {return _group_key;}
157 void set_group_key(const GroupKey &key) {_group_key = key;}
159 void disconnect_expired_slot(garbage_collecting_lock<M> &lock_arg)
162 bool expired = slot().expired();
165 nolock_disconnect(lock_arg);
168 template<typename M, typename OutputIterator>
169 void nolock_grab_tracked_objects(garbage_collecting_lock<M> &lock_arg,
170 OutputIterator inserter) const
173 slot_base::tracked_container_type::const_iterator it;
174 for(it = slot().tracked_objects().begin();
175 it != slot().tracked_objects().end();
178 void_shared_ptr_variant locked_object
182 detail::lock_weak_ptr_visitor(),
186 if(apply_visitor(detail::expired_weak_ptr_visitor(), *it))
188 nolock_disconnect(lock_arg);
191 *inserter++ = locked_object;
194 // expose Lockable concept of mutex
199 virtual void unlock()
207 const SlotType &slot() const
212 virtual shared_ptr<void> release_slot() const
215 shared_ptr<void> released_slot = m_slot;
217 return released_slot;
220 mutable boost::shared_ptr<SlotType> m_slot;
221 const boost::shared_ptr<mutex_type> _mutex;
226 class shared_connection_block;
231 friend class shared_connection_block;
233 connection() BOOST_NOEXCEPT {}
234 connection(const connection &other) BOOST_NOEXCEPT: _weak_connection_body(other._weak_connection_body)
236 connection(const boost::weak_ptr<detail::connection_body_base> &connectionBody) BOOST_NOEXCEPT:
237 _weak_connection_body(connectionBody)
241 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
242 connection(connection && other) BOOST_NOEXCEPT: _weak_connection_body(std::move(other._weak_connection_body))
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();
248 connection & operator=(connection && other) BOOST_NOEXCEPT
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();
257 #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
258 connection & operator=(const connection & other) BOOST_NOEXCEPT
260 if(&other == this) return *this;
261 _weak_connection_body = other._weak_connection_body;
266 void disconnect() const
268 boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
269 if(connectionBody == 0) return;
270 connectionBody->disconnect();
272 bool connected() const
274 boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
275 if(connectionBody == 0) return false;
276 return connectionBody->connected();
280 boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
281 if(connectionBody == 0) return true;
282 return connectionBody->blocked();
284 bool operator==(const connection& other) const
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;
290 bool operator!=(const connection& other) const
292 return !(*this == other);
294 bool operator<(const connection& other) const
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;
300 void swap(connection &other) BOOST_NOEXCEPT
303 swap(_weak_connection_body, other._weak_connection_body);
307 boost::weak_ptr<detail::connection_body_base> _weak_connection_body;
309 inline void swap(connection &conn1, connection &conn2) BOOST_NOEXCEPT
314 class scoped_connection: public connection
317 scoped_connection() BOOST_NOEXCEPT {}
318 scoped_connection(const connection &other) BOOST_NOEXCEPT:
325 scoped_connection& operator=(const connection &rhs) BOOST_NOEXCEPT
328 connection::operator=(rhs);
333 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
334 scoped_connection(scoped_connection && other) BOOST_NOEXCEPT: connection(std::move(other))
337 scoped_connection(connection && other) BOOST_NOEXCEPT: connection(std::move(other))
340 scoped_connection & operator=(scoped_connection && other) BOOST_NOEXCEPT
342 if(&other == this) return *this;
344 connection::operator=(std::move(other));
347 scoped_connection & operator=(connection && other) BOOST_NOEXCEPT
349 if(&other == this) return *this;
351 connection::operator=(std::move(other));
354 #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
358 connection conn(_weak_connection_body);
359 _weak_connection_body.reset();
363 scoped_connection(const scoped_connection &other);
364 scoped_connection& operator=(const scoped_connection &rhs);
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.
368 inline void swap(scoped_connection &conn1, scoped_connection &conn2) BOOST_NOEXCEPT
375 #endif // BOOST_SIGNALS2_CONNECTION_HPP