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/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>
31 inline void null_deleter(const void*) {}
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
42 garbage_collecting_lock(Mutex &m):
45 void add_trash(const shared_ptr<void> &piece_of_trash)
47 garbage.push_back(piece_of_trash);
50 // garbage must be declared before lock
51 // to insure it is destroyed after lock is
53 auto_buffer<shared_ptr<void>, store_n_objects<10> > garbage;
54 unique_lock<Mutex> lock;
57 class connection_body_base
60 connection_body_base():
61 _connected(true), m_slot_refcount(1)
64 virtual ~connection_body_base() {}
67 garbage_collecting_lock<connection_body_base> local_lock(*this);
68 nolock_disconnect(local_lock);
70 template<typename Mutex>
71 void nolock_disconnect(garbage_collecting_lock<Mutex> &lock_arg) const
76 dec_slot_refcount(lock_arg);
79 virtual bool connected() const = 0;
80 shared_ptr<void> get_blocker()
82 unique_lock<connection_body_base> local_lock(*this);
83 shared_ptr<void> blocker = _weak_blocker.lock();
84 if(blocker == shared_ptr<void>())
86 blocker.reset(this, &null_deleter);
87 _weak_blocker = blocker;
93 return !_weak_blocker.expired();
95 bool nolock_nograb_blocked() const
97 return nolock_nograb_connected() == false || blocked();
99 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;
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
110 template<typename Mutex>
111 void inc_slot_refcount(const garbage_collecting_lock<Mutex> &)
113 BOOST_ASSERT(m_slot_refcount != 0);
116 // if slot refcount decrements to zero due to this call,
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>
121 void dec_slot_refcount(garbage_collecting_lock<Mutex> &lock_arg) const
123 BOOST_ASSERT(m_slot_refcount != 0);
124 if(--m_slot_refcount == 0)
126 lock_arg.add_trash(release_slot());
131 virtual shared_ptr<void> release_slot() const = 0;
133 weak_ptr<void> _weak_blocker;
135 mutable bool _connected;
136 mutable unsigned m_slot_refcount;
139 template<typename GroupKey, typename SlotType, typename Mutex>
140 class connection_body: public connection_body_base
143 typedef Mutex mutex_type;
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)
148 virtual ~connection_body() {}
149 virtual bool connected() const
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();
155 const GroupKey& group_key() const {return _group_key;}
156 void set_group_key(const GroupKey &key) {_group_key = key;}
158 void disconnect_expired_slot(garbage_collecting_lock<M> &lock_arg)
161 bool expired = slot().expired();
164 nolock_disconnect(lock_arg);
167 template<typename M, typename OutputIterator>
168 void nolock_grab_tracked_objects(garbage_collecting_lock<M> &lock_arg,
169 OutputIterator inserter) const
172 slot_base::tracked_container_type::const_iterator it;
173 for(it = slot().tracked_objects().begin();
174 it != slot().tracked_objects().end();
177 void_shared_ptr_variant locked_object
181 detail::lock_weak_ptr_visitor(),
185 if(apply_visitor(detail::expired_weak_ptr_visitor(), *it))
187 nolock_disconnect(lock_arg);
190 *inserter++ = locked_object;
193 // expose Lockable concept of mutex
198 virtual void unlock()
206 const SlotType &slot() const
211 virtual shared_ptr<void> release_slot() const
214 shared_ptr<void> released_slot = m_slot;
216 return released_slot;
219 mutable boost::shared_ptr<SlotType> m_slot;
220 const boost::shared_ptr<mutex_type> _mutex;
225 class shared_connection_block;
230 friend class shared_connection_block;
233 connection(const connection &other): _weak_connection_body(other._weak_connection_body)
235 connection(const boost::weak_ptr<detail::connection_body_base> &connectionBody):
236 _weak_connection_body(connectionBody)
240 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
241 connection(connection && other): _weak_connection_body(std::move(other._weak_connection_body))
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();
247 connection & operator=(connection && other)
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();
256 #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
257 connection & operator=(const connection & other)
259 if(&other == this) return *this;
260 _weak_connection_body = other._weak_connection_body;
265 void disconnect() const
267 boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
268 if(connectionBody == 0) return;
269 connectionBody->disconnect();
271 bool connected() const
273 boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
274 if(connectionBody == 0) return false;
275 return connectionBody->connected();
279 boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
280 if(connectionBody == 0) return true;
281 return connectionBody->blocked();
283 bool operator==(const connection& other) const
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;
289 bool operator!=(const connection& other) const
291 return !(*this == other);
293 bool operator<(const connection& other) const
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;
299 void swap(connection &other)
302 swap(_weak_connection_body, other._weak_connection_body);
306 boost::weak_ptr<detail::connection_body_base> _weak_connection_body;
308 inline void swap(connection &conn1, connection &conn2)
313 class scoped_connection: public connection
316 scoped_connection() {}
317 scoped_connection(const connection &other):
324 scoped_connection& operator=(const connection &rhs)
327 connection::operator=(rhs);
332 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
333 scoped_connection(scoped_connection && other): connection(std::move(other))
336 scoped_connection(connection && other): connection(std::move(other))
339 scoped_connection & operator=(scoped_connection && other)
341 if(&other == this) return *this;
343 connection::operator=(std::move(other));
346 scoped_connection & operator=(connection && other)
348 if(&other == this) return *this;
350 connection::operator=(std::move(other));
353 #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
357 connection conn(_weak_connection_body);
358 _weak_connection_body.reset();
362 scoped_connection(const scoped_connection &other);
363 scoped_connection& operator=(const scoped_connection &rhs);
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.
367 inline void swap(scoped_connection &conn1, scoped_connection &conn2)
374 #endif // BOOST_SIGNALS2_CONNECTION_HPP