diff options
Diffstat (limited to 'boost/signals2/connection.hpp')
-rw-r--r-- | boost/signals2/connection.hpp | 125 |
1 files changed, 101 insertions, 24 deletions
diff --git a/boost/signals2/connection.hpp b/boost/signals2/connection.hpp index 0ab4dacbf6..dac806b221 100644 --- a/boost/signals2/connection.hpp +++ b/boost/signals2/connection.hpp @@ -18,6 +18,7 @@ #include <boost/mpl/bool.hpp> #include <boost/noncopyable.hpp> #include <boost/shared_ptr.hpp> +#include <boost/signals2/detail/auto_buffer.hpp> #include <boost/signals2/detail/null_output_iterator.hpp> #include <boost/signals2/detail/unique_lock.hpp> #include <boost/signals2/slot.hpp> @@ -27,25 +28,53 @@ namespace boost { namespace signals2 { - extern inline void null_deleter(const void*) {} + inline void null_deleter(const void*) {} namespace detail { + // This lock maintains a list of shared_ptr<void> + // which will be destroyed only after the lock + // has released its mutex. Used to garbage + // collect disconnected slots + template<typename Mutex> + class garbage_collecting_lock: public noncopyable + { + public: + garbage_collecting_lock(Mutex &m): + lock(m) + {} + void add_trash(const shared_ptr<void> &piece_of_trash) + { + garbage.push_back(piece_of_trash); + } + private: + // garbage must be declared before lock + // to insure it is destroyed after lock is + // destroyed. + auto_buffer<shared_ptr<void>, store_n_objects<10> > garbage; + unique_lock<Mutex> lock; + }; + class connection_body_base { public: connection_body_base(): - _connected(true) + _connected(true), m_slot_refcount(1) { } virtual ~connection_body_base() {} void disconnect() { - unique_lock<connection_body_base> local_lock(*this); - nolock_disconnect(); + garbage_collecting_lock<connection_body_base> local_lock(*this); + nolock_disconnect(local_lock); } - void nolock_disconnect() + template<typename Mutex> + void nolock_disconnect(garbage_collecting_lock<Mutex> &lock_arg) const { - _connected = false; + if(_connected) + { + _connected = false; + dec_slot_refcount(lock_arg); + } } virtual bool connected() const = 0; shared_ptr<void> get_blocker() @@ -72,10 +101,39 @@ namespace boost virtual void lock() = 0; virtual void unlock() = 0; + // Slot refcount should be incremented while + // a signal invocation is using the slot, in order + // to prevent slot from being destroyed mid-invocation. + // garbage_collecting_lock parameter enforces + // the existance of a lock before this + // method is called + template<typename Mutex> + void inc_slot_refcount(const garbage_collecting_lock<Mutex> &) + { + BOOST_ASSERT(m_slot_refcount != 0); + ++m_slot_refcount; + } + // if slot refcount decrements to zero due to this call, + // it puts a + // shared_ptr to the slot in the garbage collecting lock, + // which will destroy the slot only after it unlocks. + template<typename Mutex> + void dec_slot_refcount(garbage_collecting_lock<Mutex> &lock_arg) const + { + BOOST_ASSERT(m_slot_refcount != 0); + if(--m_slot_refcount == 0) + { + lock_arg.add_trash(release_slot()); + } + } + protected: + virtual shared_ptr<void> release_slot() const = 0; - mutable bool _connected; weak_ptr<void> _weak_blocker; + private: + mutable bool _connected; + mutable unsigned m_slot_refcount; }; template<typename GroupKey, typename SlotType, typename Mutex> @@ -83,34 +141,37 @@ namespace boost { public: typedef Mutex mutex_type; - connection_body(const SlotType &slot_in): - slot(slot_in) + connection_body(const SlotType &slot_in, const boost::shared_ptr<mutex_type> &signal_mutex): + m_slot(new SlotType(slot_in)), _mutex(signal_mutex) { } virtual ~connection_body() {} virtual bool connected() const { - unique_lock<mutex_type> local_lock(_mutex); - nolock_grab_tracked_objects(detail::null_output_iterator()); + garbage_collecting_lock<mutex_type> local_lock(*_mutex); + nolock_grab_tracked_objects(local_lock, detail::null_output_iterator()); return nolock_nograb_connected(); } const GroupKey& group_key() const {return _group_key;} void set_group_key(const GroupKey &key) {_group_key = key;} - bool nolock_slot_expired() const + template<typename M> + void disconnect_expired_slot(garbage_collecting_lock<M> &lock_arg) { - bool expired = slot.expired(); + if(!m_slot) return; + bool expired = slot().expired(); if(expired == true) { - _connected = false; + nolock_disconnect(lock_arg); } - return expired; } - template<typename OutputIterator> - void nolock_grab_tracked_objects(OutputIterator inserter) const + template<typename M, typename OutputIterator> + void nolock_grab_tracked_objects(garbage_collecting_lock<M> &lock_arg, + OutputIterator inserter) const { + if(!m_slot) return; slot_base::tracked_container_type::const_iterator it; - for(it = slot.tracked_objects().begin(); - it != slot.tracked_objects().end(); + for(it = slot().tracked_objects().begin(); + it != slot().tracked_objects().end(); ++it) { void_shared_ptr_variant locked_object @@ -123,7 +184,7 @@ namespace boost ); if(apply_visitor(detail::expired_weak_ptr_visitor(), *it)) { - _connected = false; + nolock_disconnect(lock_arg); return; } *inserter++ = locked_object; @@ -132,15 +193,31 @@ namespace boost // expose Lockable concept of mutex virtual void lock() { - _mutex.lock(); + _mutex->lock(); } virtual void unlock() { - _mutex.unlock(); + _mutex->unlock(); + } + SlotType &slot() + { + return *m_slot; + } + const SlotType &slot() const + { + return *m_slot; + } + protected: + virtual shared_ptr<void> release_slot() const + { + + shared_ptr<void> released_slot = m_slot; + m_slot.reset(); + return released_slot; } - SlotType slot; private: - mutable mutex_type _mutex; + mutable boost::shared_ptr<SlotType> m_slot; + const boost::shared_ptr<mutex_type> _mutex; GroupKey _group_key; }; } |