Boost logo

Boost Users :

From: ostware (ostware_at_[hidden])
Date: 2003-05-15 01:09:28


--- In Boost-Users_at_[hidden], "Peter Dimov" <pdimov_at_m...> wrote:
> ostware wrote:
> >
> > class T : public enable_shared_from_this {
> > public:
> > T() {}
> > ~T()
> > {
> > shared_ptr<T> ptr = shared_from_this();
> > // trouble: exception, can't get a shared_ptr
>
> What are you trying to achieve with this? ~T has already started;
the object
> is on its way to A Better Place. Even if you could get a shared_ptr
to it,
> you can't stop the object's destruction. The shared_ptr would point to a
> destroyed T once ~T finishes.

(First off, thanks for the reply. I am feeling my way in the dark here
and I could easily be missing something obvious. But perhaps it would
help 'boost' to clarify or fix this, as needed.

I keep having that 'what is going on!' feeling from boost that I got
from STL, when I first started digging into it. But I am also excited
by the possiblities of boost. Onward! So...)

~T needs to tell another object it holds a pointer to (say class U)
that it is 'going away' during its destructor. T and U are loosely
connected, from two different libraries, and each holds a weak_ptr to
the other. When U finds out that T is 'going away' it needs to void
out its weak_ptr<T>. I am converting code where the weak_ptrs were
simple U* or T*.

I think this code distills it down as much as possible. Obviously
there is more going on, but I think this sums it up...

class T : enable_shared_from_this<T>
{
   ~T()
   {
      shared_ptr<U> u = m_u.lock();
      if (u) {
         shared_ptr<T> ptr(shared_from_this()); // exception!
         u->UnsetT(ptr);
      }
   }

   void SetU(shared_ptr<U> u)
   {
      shared_ptr<T> ptr(shared_from_this());
      t->SetT(ptr);
      m_u = u;
   }

private:
   weak_ptr<U> m_u;
};
 
class U
{
public:
   void SetT(shared_ptr<T> t)
   {
      m_t = t;
   }

   void UnsetT(shared_ptr<T> t)
   {
      if (t == m_t.lock())
         m_t.reset();
   }

private:
   weak_ptr<T> m_t;
};

main()
{
   shared_ptr<T> t(new T);
   shared_ptr<U> u(new U);

   t->SetU(u);
   t.reset();
   // u->m_t should have been voided out
}

Your null_deleter suggestion was good. I tried it and it worked. So
using the below works...

~T()
{
   shared_ptr<T> ptr(this, null_deleter());
   m_u->UnsetT(ptr);
}

It also worked to move the use_count_-- down past dispose() in
boost/detail/shared_count.hpp::release()...

shared_count::release()
{
   {
      mutex_type::scoped_lock lock(mtx_);
      long new_use_count = use_count_ - 1;
      if (new_use_count != 0)
      {
         --use_count_;
         --weak_count_;
         return;
      }
   }

   dispose();
   use_count_ = new_use_count;
   weak_release();
}

The use of the mutex doesn't look kosher, but you get the idea (and I
am handling thread safety elsewhere anyway).

So it seems that either (1) this is a bug to fix, or (2) it should be
documented that shared_from_this doesn't work in destructors (or
constructors for that matter).

Cheers... mo


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net