summaryrefslogtreecommitdiff
path: root/boost/signals2/connection.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/signals2/connection.hpp')
-rw-r--r--boost/signals2/connection.hpp125
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;
};
}