1

there are related questions like smart pointers + "this" considered harmful? but they do not handle my case. All these questions are about exposing a raw this pointer in case of reference counting smart pointers. However, my problem is not that I do expose this but rather use it only in the method, but maybe for a longer period of time.

Consider the following code:

class X{
    void foo(){...} //Performs some long running task
}

shared_ptr<X> x;

void bar(){
    x->foo();
}

Okay, so some code calls the foo method of object x. Consider that the only smart reference to the instance behind x is the one shared_ptr x. Now, foo performs some long running task invoking other functions and stuff.

Now, consider, while foo is running, another thread, a signal handler, or even a recursive call in foo changes or clears the reference x. This would trigger the deletion of the object. Now, the this pointer on the call stack of foo points to a deleted object. Further code execution in foo will therefore produce unpredictable results.

How to avoid this? I mean, whenever being in the execution of a method of an object that is handled via reference counting, there is the danger that some code might clear the references to the object and the method call will fail or produce strange results. The problem is the semantics: The reference counting thinks that there is no more reference to the object and thus deletes it, but this is not true - there is still the this reference, but sadly, it is not a smart pointer.

I know there is stuff like enable_shared_from_this, but should I ALWAYS rewrite all methods of objects that could potentially be reference counted to first obtain a shared pointer from this and then use that pointer, to be save from this problem? This would clutter all method code, and in addition, it might still fail: If an event clears x after the call to foo has been made but before the first statement in the method (which obtains a shared instance) executes (maybe another thread), then things will fail again.

So the general question is: When using any kind of smart pointers, how can I be safe during a method call to the managed object? How can I ensure that the this pointer inside the method will not become invalid? Is rewriting all methods to use enable_shared_from_this in their first statement a good solution?

Or is code that clears a reference while that reference is still on the call stack simply ill-formed? But if it is, then it is hard to write non-ill-formed code once multiple threads and complex recursive calls are used...

Community
  • 1
  • 1
gexicide
  • 38,535
  • 21
  • 92
  • 152

2 Answers2

1

Regarding multiple threads: when you pass data between threads, you should always pass shared pointers by copy, not by reference. This rule will ensure that the thread cannot lose by another thread clearing a shared pointer it has a reference to.

Regarding recursive method calls: yes, you should make it a rule that whenever you call a method via a shared pointer that might result in other shared pointers to the object being cleared, you have a copy of the shared pointer locally. This should not usually be a problem if you pass shared pointers by value to functions.

The only case I can see that would be an issue is that it appears in your code that x is a class data member:

class C {
    shared_ptr<X> x;
public:
    void method1() { x.reset; }
    void method2() {
        x->foo();
    }
};

In this case if it is conceivable that method1 could be called from X::foo you would have to take a local copy of x. This is only an issue when you store shared pointers in a class, and only affects methods on that class.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
0

Sharing is caring: Get a new share of the variable!

shared_ptr<X> x;

void bar()
{
    shared_ptr<X> my_x = x;
    my_x->foo();
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Yes, but then, whenever working with any smart pointers, I always have to create a local copy first. This will not clutter the method code of the object, but might severly clutter all client code... – gexicide Aug 24 '12 at 08:21
  • @gexicide: It's called "shared pointer" for a reason. If you don't *want* to share the resource, you need to change your design. – Kerrek SB Aug 24 '12 at 08:22
  • so, is this a reasonable general rule for using smart pointers: Whenever invoking some method via a smart pointer - if that pointer is a field in an object or a global variable - make a local copy first – gexicide Aug 24 '12 at 08:26
  • @gexicide: There isn't a general rule. However, if you want an object to live as long as it has consumers, then you can implement this lifetime management with a `shared_ptr`, and each consumer must have her own copy of said shared pointer. – Kerrek SB Aug 24 '12 at 09:54
  • I would like to have a "If I respect this, my code might be too defensive but at least I do not have to care about this problem anymore" rule. My problem is very generic, so is there a generic rule that could solve it? The aforementioned one seems to solve it easily. – gexicide Aug 24 '12 at 10:50
  • @gexicide: The "generic rule" is part of your overall, broad design skills. If anything, I'd say think about object lifetime, ownership and responsibilites. Shared pointers are one of the many tools in your toolbox that may or may not be appropriate to implement a given design goal. – Kerrek SB Aug 24 '12 at 10:51