/* boost::signals2::connection provides a handle to a signal/slot connection. Author: Frank Mori Hess Begin: 2007-01-23 */ // Copyright Frank Mori Hess 2007-2008. // Distributed under the Boost Software License, Version // 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org/libs/signals2 for library home page. #ifndef BOOST_SIGNALS2_CONNECTION_HPP #define BOOST_SIGNALS2_CONNECTION_HPP #include #include #include #include #include #include #include #include #include namespace boost { namespace signals2 { inline void null_deleter(const void*) {} namespace detail { // This lock maintains a list of shared_ptr // which will be destroyed only after the lock // has released its mutex. Used to garbage // collect disconnected slots template class garbage_collecting_lock: public noncopyable { public: garbage_collecting_lock(Mutex &m): lock(m) {} void add_trash(const shared_ptr &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, store_n_objects<10> > garbage; unique_lock lock; }; class connection_body_base { public: connection_body_base(): _connected(true), m_slot_refcount(1) { } virtual ~connection_body_base() {} void disconnect() { garbage_collecting_lock local_lock(*this); nolock_disconnect(local_lock); } template void nolock_disconnect(garbage_collecting_lock &lock_arg) const { if(_connected) { _connected = false; dec_slot_refcount(lock_arg); } } virtual bool connected() const = 0; shared_ptr get_blocker() { unique_lock local_lock(*this); shared_ptr blocker = _weak_blocker.lock(); if(blocker == shared_ptr()) { blocker.reset(this, &null_deleter); _weak_blocker = blocker; } return blocker; } bool blocked() const { return !_weak_blocker.expired(); } bool nolock_nograb_blocked() const { return nolock_nograb_connected() == false || blocked(); } bool nolock_nograb_connected() const {return _connected;} // expose part of Lockable concept of mutex 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 void inc_slot_refcount(const garbage_collecting_lock &) { 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 void dec_slot_refcount(garbage_collecting_lock &lock_arg) const { BOOST_ASSERT(m_slot_refcount != 0); if(--m_slot_refcount == 0) { lock_arg.add_trash(release_slot()); } } protected: virtual shared_ptr release_slot() const = 0; weak_ptr _weak_blocker; private: mutable bool _connected; mutable unsigned m_slot_refcount; }; template class connection_body: public connection_body_base { public: typedef Mutex mutex_type; connection_body(const SlotType &slot_in, const boost::shared_ptr &signal_mutex): m_slot(new SlotType(slot_in)), _mutex(signal_mutex) { } virtual ~connection_body() {} virtual bool connected() const { garbage_collecting_lock 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;} template void disconnect_expired_slot(garbage_collecting_lock &lock_arg) { if(!m_slot) return; bool expired = slot().expired(); if(expired == true) { nolock_disconnect(lock_arg); } } template void nolock_grab_tracked_objects(garbage_collecting_lock &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(); ++it) { void_shared_ptr_variant locked_object ( apply_visitor ( detail::lock_weak_ptr_visitor(), *it ) ); if(apply_visitor(detail::expired_weak_ptr_visitor(), *it)) { nolock_disconnect(lock_arg); return; } *inserter++ = locked_object; } } // expose Lockable concept of mutex virtual void lock() { _mutex->lock(); } virtual void unlock() { _mutex->unlock(); } SlotType &slot() { return *m_slot; } const SlotType &slot() const { return *m_slot; } protected: virtual shared_ptr release_slot() const { shared_ptr released_slot = m_slot; m_slot.reset(); return released_slot; } private: mutable boost::shared_ptr m_slot; const boost::shared_ptr _mutex; GroupKey _group_key; }; } class shared_connection_block; class connection { public: friend class shared_connection_block; connection() {} connection(const connection &other): _weak_connection_body(other._weak_connection_body) {} connection(const boost::weak_ptr &connectionBody): _weak_connection_body(connectionBody) {} // move support #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) connection(connection && other): _weak_connection_body(std::move(other._weak_connection_body)) { // make sure other is reset, in case it is a scoped_connection (so it // won't disconnect on destruction after being moved away from). other._weak_connection_body.reset(); } connection & operator=(connection && other) { if(&other == this) return *this; _weak_connection_body = std::move(other._weak_connection_body); // make sure other is reset, in case it is a scoped_connection (so it // won't disconnect on destruction after being moved away from). other._weak_connection_body.reset(); return *this; } #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) connection & operator=(const connection & other) { if(&other == this) return *this; _weak_connection_body = other._weak_connection_body; return *this; } ~connection() {} void disconnect() const { boost::shared_ptr connectionBody(_weak_connection_body.lock()); if(connectionBody == 0) return; connectionBody->disconnect(); } bool connected() const { boost::shared_ptr connectionBody(_weak_connection_body.lock()); if(connectionBody == 0) return false; return connectionBody->connected(); } bool blocked() const { boost::shared_ptr connectionBody(_weak_connection_body.lock()); if(connectionBody == 0) return true; return connectionBody->blocked(); } bool operator==(const connection& other) const { boost::shared_ptr connectionBody(_weak_connection_body.lock()); boost::shared_ptr otherConnectionBody(other._weak_connection_body.lock()); return connectionBody == otherConnectionBody; } bool operator!=(const connection& other) const { return !(*this == other); } bool operator<(const connection& other) const { boost::shared_ptr connectionBody(_weak_connection_body.lock()); boost::shared_ptr otherConnectionBody(other._weak_connection_body.lock()); return connectionBody < otherConnectionBody; } void swap(connection &other) { using std::swap; swap(_weak_connection_body, other._weak_connection_body); } protected: boost::weak_ptr _weak_connection_body; }; inline void swap(connection &conn1, connection &conn2) { conn1.swap(conn2); } class scoped_connection: public connection { public: scoped_connection() {} scoped_connection(const connection &other): connection(other) {} ~scoped_connection() { disconnect(); } scoped_connection& operator=(const connection &rhs) { disconnect(); connection::operator=(rhs); return *this; } // move support #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) scoped_connection(scoped_connection && other): connection(std::move(other)) { } scoped_connection(connection && other): connection(std::move(other)) { } scoped_connection & operator=(scoped_connection && other) { if(&other == this) return *this; disconnect(); connection::operator=(std::move(other)); return *this; } scoped_connection & operator=(connection && other) { if(&other == this) return *this; disconnect(); connection::operator=(std::move(other)); return *this; } #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) connection release() { connection conn(_weak_connection_body); _weak_connection_body.reset(); return conn; } private: scoped_connection(const scoped_connection &other); scoped_connection& operator=(const scoped_connection &rhs); }; // Sun 5.9 compiler doesn't find the swap for base connection class when // arguments are scoped_connection, so we provide this explicitly. inline void swap(scoped_connection &conn1, scoped_connection &conn2) { conn1.swap(conn2); } } } #endif // BOOST_SIGNALS2_CONNECTION_HPP