Boost logo

Boost :

From: Frank Mori Hess (frank.hess_at_[hidden])
Date: 2008-02-29 17:12:07


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I came up with another idea for handling automatic connection management in
thread_safe_signals (thread-safe version of boost.signals). I've attached
some proof-of-concept code. The new code is supposed to enable going back to
more of a boost::signals::trackable idea, where you inherit from
a "trackable" base class to get automatic connection management. It still
requires the trackable object be managed by a shared_ptr, but now the owning
shared_ptr would not have to be explicitly passed to the slot for tracking.
Also, it should make it possible to track connections to "this" when making
connections in a constructor. Instead, the signal (or slot rather) would
create a "tracker" object for each trackable object detected by visit_each.
The tracker::lock_target() function returns a bool and a shared_ptr which can
be in one of three states:

bool is false: the trackable object has been destructed and its associated
connections should be disconnected.

bool is true, and shared_ptr is non-zero: the trackable object is alive and
its associated connections should operate normally. The returned shared_ptr
can be used to insure the trackable object doesn't destruct while the signal
invocation is in progress.

bool is true, and shared_ptr is zero: the trackable object is either
constructed but not yet owned by a shared_ptr, or it is in the process of
being destructed. In either case, the connection would be blocked until one
of the other two states is reached.

For those who have no idea what I'm talking about, I'll give a little
background. The trackable/visit_each scheme of boost.signals was abandoned
in thread_safe_signals since it disconnects the connections in the destructor
of the trackable class. This isn't thread-safe, since the trackable
destructor isn't called until after any derived class' destructors. The
replacement idea was to allow shared_ptrs to be passed to slots for tracking.
The shared_ptrs are assumed to own (directly or indirectly) any objects whose
destruction should cause the slot to automatically disconnect. The slot
keeps weak_ptr copies, and disconnects when any of the tracked weak_ptrs
expire. The slot's weak_ptrs are all be converted to shared_ptrs whenever
the slot is run, to insure the tracked objects don't destruct in the middle
of signal invocation. One limitation is an object can't make automatically
tracked connections to itself in its constructor, since
boost::enable_shared_from_this doesn't work in constructors.

- --
Frank
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQFHyIM/5vihyNWuA4URApuKAKCAGIT2kMXn8mCBU18/TtySbRF0HACgx/3l
Qt6uPigHRD6QREqKW7v/Vjk=
=H5Sb
-----END PGP SIGNATURE-----

--Boundary-00=_/MIyHgfv3ahgl2H
Content-Type: text/x-c++src;
  charset="utf-8";
  name="poc_trackable.cpp"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
        filename="poc_trackable.cpp"

// Copyright Frank Mori Hess 2008. Use, modification and
// distribution is subject to 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)

// For more information, see http://www.boost.org

#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread/mutex.hpp>
#include <utility>

namespace poc
{
        class trackable;

        namespace detail
        {
                class trackable_impl
                {
                public:
                        trackable_impl(trackable &t): _trackable(t), _alive(true)
                        {}
                        ~trackable_impl()
                        {}
                        std::pair<bool, boost::shared_ptr<void> > lock();

                        boost::mutex _mutex;
                        trackable &_trackable;
                        bool _alive;
                };
        };

        class trackable: public boost::enable_shared_from_this<trackable>
        {
        public:
                trackable(): _impl(new detail::trackable_impl(*this))
                {}
                ~trackable()
                {
                        boost::mutex::scoped_lock lock(_impl->_mutex);
                        _impl->_alive = false;
                }
        private:
                friend class tracker;

                boost::shared_ptr<detail::trackable_impl> _impl;
        };

        class tracker
        {
        public:
                tracker(trackable &target): _target_impl(target._impl)
                {}
                std::pair<bool, boost::shared_ptr<void> > lock_target()
                {
                        return _target_impl->lock();
                }
        private:
                boost::shared_ptr<detail::trackable_impl> _target_impl;
        };

        namespace detail
        {
                std::pair<bool, boost::shared_ptr<void> > trackable_impl::lock()
                {
                        boost::mutex::scoped_lock lock(_mutex);
                        if(_alive == false) return std::make_pair(_alive, boost::shared_ptr<void>());
                        try
                        {
                                return std::make_pair(_alive, _trackable.shared_from_this());
                        }
                        catch(const boost::bad_weak_ptr &err)
                        {
                                return std::make_pair(_alive, boost::shared_ptr<void>());
                        }
                }
        }
}

class MyClass: public poc::trackable
{
};

#include <iostream>

void dump_tracker(poc::tracker &t)
{
        std::cerr << "tracker alive = " << t.lock_target().first << "\n" <<
                "tracker pointer = " << t.lock_target().second.get() << std::endl;
}

int main()
{
        MyClass *myobj = new MyClass();
        poc::tracker tracker(*myobj);
        dump_tracker(tracker);
        boost::shared_ptr<MyClass> pointer(myobj);
        dump_tracker(tracker);
        pointer.reset();
        dump_tracker(tracker);
        return 0;
}

--Boundary-00=_/MIyHgfv3ahgl2H--


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk