Punteros inteligentes y esto en un método de ejecución prolongada

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.

Considere el siguiente código:

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.

Sé que hay cosas como 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...

preguntado el 24 de agosto de 12 a las 09:08

2 Respuestas

Con respecto a múltiples hilos: when you pass data between threads, you should always pass shared pointers by copia, not by reference. This rule will ensure that the thread cannot lose by another thread clearing a shared pointer it has a reference to.

Con respecto a llamadas a métodos recursivos: 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. Esto es , solamente an issue when you store shared pointers in a class, and only affects methods on that class.

Respondido 24 ago 12, 14:08

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();
}

Respondido 24 ago 12, 09:08

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... - gexicida

@gexicide: It's called "shared pointer" for a reason. If you don't quieres to share the resource, you need to change your design. - Kerrek SB

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 - gexicida

@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

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. - gexicida

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.