| /* |
| boost::signals2::connection provides a handle to a signal/slot connection. |
| |
| Author: Frank Mori Hess <fmhess@users.sourceforge.net> |
| 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 <boost/function.hpp> |
| #include <boost/mpl/bool.hpp> |
| #include <boost/noncopyable.hpp> |
| #include <boost/shared_ptr.hpp> |
| #include <boost/signals2/detail/null_output_iterator.hpp> |
| #include <boost/signals2/detail/unique_lock.hpp> |
| #include <boost/signals2/slot.hpp> |
| #include <boost/weak_ptr.hpp> |
| |
| namespace boost |
| { |
| namespace signals2 |
| { |
| extern inline void null_deleter(const void*) {} |
| namespace detail |
| { |
| class connection_body_base |
| { |
| public: |
| connection_body_base(): |
| _connected(true) |
| { |
| } |
| virtual ~connection_body_base() {} |
| void disconnect() |
| { |
| unique_lock<connection_body_base> lock(*this); |
| nolock_disconnect(); |
| } |
| void nolock_disconnect() |
| { |
| _connected = false; |
| } |
| virtual bool connected() const = 0; |
| shared_ptr<void> get_blocker() |
| { |
| unique_lock<connection_body_base> lock(*this); |
| shared_ptr<void> blocker = _weak_blocker.lock(); |
| if(blocker == shared_ptr<void>()) |
| { |
| 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; |
| |
| protected: |
| |
| mutable bool _connected; |
| weak_ptr<void> _weak_blocker; |
| }; |
| |
| template<typename GroupKey, typename SlotType, typename Mutex> |
| class connection_body: public connection_body_base |
| { |
| public: |
| typedef Mutex mutex_type; |
| connection_body(const SlotType &slot_in): |
| slot(slot_in) |
| { |
| } |
| virtual ~connection_body() {} |
| virtual bool connected() const |
| { |
| unique_lock<mutex_type> lock(_mutex); |
| nolock_grab_tracked_objects(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 |
| { |
| bool expired = slot.expired(); |
| if(expired == true) |
| { |
| _connected = false; |
| } |
| return expired; |
| } |
| template<typename OutputIterator> |
| void nolock_grab_tracked_objects(OutputIterator inserter) const |
| { |
| 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)) |
| { |
| _connected = false; |
| return; |
| } |
| *inserter++ = locked_object; |
| } |
| } |
| // expose Lockable concept of mutex |
| virtual void lock() |
| { |
| _mutex.lock(); |
| } |
| virtual void unlock() |
| { |
| _mutex.unlock(); |
| } |
| SlotType slot; |
| private: |
| mutable mutex_type _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<detail::connection_body_base> &connectionBody): |
| _weak_connection_body(connectionBody) |
| {} |
| ~connection() {} |
| void disconnect() const |
| { |
| boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock()); |
| if(connectionBody == 0) return; |
| connectionBody->disconnect(); |
| } |
| bool connected() const |
| { |
| boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock()); |
| if(connectionBody == 0) return false; |
| return connectionBody->connected(); |
| } |
| bool blocked() const |
| { |
| boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock()); |
| if(connectionBody == 0) return true; |
| return connectionBody->blocked(); |
| } |
| bool operator==(const connection& other) const |
| { |
| boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock()); |
| boost::shared_ptr<detail::connection_body_base> 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<detail::connection_body_base> connectionBody(_weak_connection_body.lock()); |
| boost::shared_ptr<detail::connection_body_base> 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<detail::connection_body_base> _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; |
| } |
| 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 |