17

For legacy reasons I need to use intrusive pointers, as I need the ability to convert raw pointers to smart pointers.

However I noticed there is no weak intrusive pointer for boost. I did find a talk about it on the boost thread list, however nothing concrete.

Does anyone know of a thread safe implementation of weak intrusive pointer?

Thanks Rich

Peter Alexander
  • 53,344
  • 14
  • 119
  • 168
Rich
  • 3,722
  • 5
  • 33
  • 47

4 Answers4

12

It does not make any sense.

To elaborate: weak_ptr points to the same instance of a counter object that shared_ptr do. When the shared_ptr goes out of scope, the instance of the counter stays (with a count effectively at 0), which allows the weak_ptr instances to check that they effectively point to a freed object.

With Intrusive Counting, the counter is integrated within the object. When the count reaches 0, the object is usually either recycled or deleted... but the point is the counter is no longer available. The rationale is that this allow for a more efficient storage (1 single chunk) and greater speed (cache locality).

If you need Weak Reference counting and do not care for the benefits of intrusive counting, you can use a combination of shared_ptr and weak_ptr.

The idea is to deassociate the counter from the objects.

class Counted
{
  // bla
private:
  boost::shared_ptr<int> mCounter;
};

Now you can return weak handles:

class WeakHandle
{
public:
  explicit WeakHandle(Counted& c): mCounter(c.mCounter), mObject(&c) {}

  bool expired() const { return mCounter.expired(); }

private:
  boost::weak_ptr<int> mCounter;
  Counted* mObject;
};

Here, we deassociate the lifetime of the counter from the lifetime of the object, so that it will survive the destruction of the object... partially. Thus making the weak_ptr effectively possible.

And of course, using shared_ptr and weak_ptr this is Thread Safe ;)

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • My idea is to embed the shared object used by shared ptr and weak ptr inside of the host object and intrusive_weak_ptr (if that existed) would use it the same way as weak_ptr does. I still need the functionality of deleting the object when there are no more references. In addition I need to take weak references. – Rich Mar 08 '10 at 13:46
  • I think you did not understand my point: that is exactly what I propose. The `Counted` object is "intrusively" counted, I just changed the counter from a plain integer to a pointer to an integer. As for your `intrusive_weak_ptr` that's just what I called `WeakHandle`. – Matthieu M. Mar 08 '10 at 15:00
  • Ah I think I'm with you. So I'd use an intrusive pointer as normal, however when intrusive_ptr_add_ref is called, I reference the int from the shared pointer as my count. When that hits zero the object is freed, however any weak references maintain a pointer to the int. Is that correct? – Rich Mar 08 '10 at 15:11
  • I don't think `WeakHandle` is thread safe in the same way as `weak_ptr`. `WeakHandle` only holds a raw pointer which does not keep the `Counted` alive. OTOH, locking a `weak_ptr` yields a `shared_ptr` so the pointee can't be deleted while the thread is accessing it. – Taylor Oct 22 '15 at 03:47
8

I didn't really like either of the previous answers so:

No, I don't know of an implementation, but I think it is possible. The standard implementation of the shared_ptr holds two reference counts, one for the "strong" and one for the "weak" references, and a pointer to the referent. In an intrusive_ptr implementation the strong count needs to be part of the object, but the weak can't be. So, it seems like you could create a "weakable" intrusive_ptr.

Define a weak pointer helper:

template<class X>
class intrusive_ptr_weak_helper {
    long weak_ref_count;
    X *target_instance;
};

Then record that into the object beside the reference count:

struct X {
    ...
    intrusive_ptr_weak_helper *ref_weak_helper;
    ...
    long ref_count;
    ...
};

When constructing X:

ref_count = 0;
ref_weak_helper = NULL;

The "strong" pointer, intrusive_strong_ptr, is identical to intrusive_ptr, until deletion occurs. When the strong ref count goes to zero (before deletion occurs):

if (ref_weak_helper != NULL) {
    if (ref_weak_helper->weak_ref_count == 0)
        delete ref_weak_helper;
    else
        ref_weak_helper->target_instance = NULL;
}

The "weak" version, intrusive_weak_ptr, records the pointer to the weak helper, manipulating that reference count, and accessing the target object via the target_instance pointer. When the weak_ref_count decrements to zero the status of target_instance determines whether the helper is deleted or not.

There are many details missing (concurrency concerns for instance) but this is a mixing of the shared_ptr and the intrusive_ptr. It maintains the basic benefits of the intrusive_ptr (cache optimization, reuse of 3rd party intrusive (strong) ref count, strong and weak pointer stand-ins are pointer sized) while adding extra work mainly in the weak reference path.

Speed8ump
  • 1,307
  • 10
  • 25
4

Current implementation of intrusive pointer is using reference counter. So deleting object delete also delete the counter, so weak_intrusive_pointer will never know that the object was deleted.

If you need to get weak_ptr from this, you probably search boost::enable_shared_from_this<T>.

Peter Alexander
  • 53,344
  • 14
  • 119
  • 168
Arpegius
  • 5,817
  • 38
  • 53
2

OpenSceneGraph and its successor, VulkanSceneGraph, each have comprehensive implementations of intrusive strong pointers and associated weak pointers, named ref_ptr<> and observer_ptr<>, respectively.

I don't know every detail of these systems, but it seems they work using an additional object which is informed when the referent (a descendant of the class Referenced) is deleted. Weak pointers use this third object when an attempt is made to convert them to strong pointers.

VulkanSceneGraph is the next generation scene graph that is currently in development and intended to replace OpenSceneGraph, so I assume its intrusive pointer system is a more advanced implementation.

Worth checking out:

https://github.com/vsg-dev/VulkanSceneGraph/blob/master/include/vsg/core/observer_ptr.h

schpidah
  • 21
  • 1
  • The weak pointer here is implemented using a somewhat heavy object call an Auxiliary (containing what look like some debugging helpers for floating weak references). Looks like it is solving some additional problems other than just weak intrusive pointers, but in general looks like an implementation of what I described in my comment – Speed8ump Mar 30 '21 at 03:33