247 lines
7.2 KiB
C++
247 lines
7.2 KiB
C++
/*
|
|
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> local_lock(*this);
|
|
nolock_disconnect();
|
|
}
|
|
void nolock_disconnect()
|
|
{
|
|
_connected = false;
|
|
}
|
|
virtual bool connected() const = 0;
|
|
shared_ptr<void> get_blocker()
|
|
{
|
|
unique_lock<connection_body_base> local_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> local_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
|