Rafael Lopes
Rafael Lopes

Reputation: 27

Shared Pointer set in Lambda after out of scope

I am having a problem with shared pointers, lambdas and scope.

My situation is that I have a method which I call, so I can use later the returned value. Kind of normal until here. The problem comes when I have an async method inside this method, that I have to make a set of an exception to a shared_pointer. I am using a lambda function to process the callback from this async method. In this lambda I have to use my shared_pointer and set an exception on it (a method from its class) resultPtr->setException();

I first used a shared_ptr, then a weak_ptr in the lambda (because I read in some places that the shared_ptr could lead to memory leaks). But until now I guess I really need to use a shared_ptr.

I have to test if this exception is being set in a second .cpp, so I call the method and use the returned value. I put the thread waiting for about 500ms then I test if it has exceptions, but it does not. However, in very rare cases, it "hasException". I can see that the lambda is being called and that it sets the exception through my logs, however it seems like it does not change the value I returned first (like it is pointing to a different place).

The inputs of the first method I mentioned are not important for my case, only that they are Pointers.

In my case I have something like:

file1.cpp
typedef shared_ptr<Result> ResultPtr;
ResultPtr method_name(TypePtr args)
{
    if(args != nullptr)
    {
        ResultPtr result = make_shared<Result>();
        stuff->callAsync([this, result](input)
        {
            if(result == nullptr)
            {
                result->setException();
            }
        });
    }
    else
    {
        result->setError();
    }
    return result;
}

file2.cpp
bool testingMethod()
{
    ResultPtr result = file1::methodName(TypePtr args)
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    test(result->hasException);
}

For testing I am using another class where I call this method with certain inputs(not important here) and I need to compare the object value of resultPtr. What happens is that as soon as it goes out of scope (return a), I can't access the object anymore. After I call this method, I put the thread waiting for about 1 second, but it never changes the state (since it can't point to a destroyed object)

Is there anyway to overcome this? Other suggestions?

Thank you!

Upvotes: 1

Views: 1568

Answers (3)

Sean Burton
Sean Burton

Reputation: 960

If you need the lambda to guarantee that it keeps the object alive, why are you using a weak pointer to pass in, instead of a shared pointer? The purpose of a weak pointer is the behaviour you are seeing, where it does not itself keep the object alive.

Because you are only capturing a weak pointer it does not keep the shared pointer alive when the only shared pointer goes out of scope, if it goes out of scope before you have locked the weak pointer. But you could just capture the shared pointer (by value, so a copy is taken) instead of using a weak pointer. Then the reference count will be incremented and the shared pointer inside the lambda will keep it alive, and it will only be deleted when both shared pointers have gone out of scope.

Shared ownership like this is the reason for a shared pointer, you should only use a weak pointer if it is desirable that the object will no longer be accessible if the shared pointer reference count reaches zero.

Upvotes: 2

Murphy
Murphy

Reputation: 3999

Of course you can't access the object anymore, because it is destroyed when the shared pointer leaves the scope of the if(args) block, where it is created. You'll need to have the object living outside the method, e. g. storing it as class member or (<deity> forbid!) in a global variable.

Whether you need to wrap it in a shared pointer or not is unclear, you'll need to provide a MCVE together with a clearer description of what you want to achieve.

Upvotes: 2

user2807083
user2807083

Reputation: 2982

If your callAsync is really asynchronous, your lambda could start to execute when local variables result and a is alreay out of scope. You should pass needed variables as parameters, e.g. as binding with std::bind:

typedef shared_ptr<Result> ResultPtr;
ResultPtr method_name(args)
{
    if(args)
    {
        ResultPtr a = make_shared<Result>();
        std::weak_ptr<Result> result(a);
        stuff->callAsync(std::bind([this](std::weak_ptr<Result> result_param)
        {
            auto resultPtr = result_param.lock();
            if(resultPtr)
            {
                resultPtr->setValue()
                ...other stuff...
            }
        }, result));
    }
    else
    {
        a->setError();
    }
    return a;
}

and yes, your example is confusing

Upvotes: 1

Related Questions