How does std :: future affect the lifetime of an associated std :: packaged_task?

I have a std::packaged_taskcontaining lambda that grabs the variable with a copy. When this one std::packaged_taskis deleted, I would expect the variable living inside the lambda to be destroyed, but I noticed that if I get bound std::futurefor this std::packaged_task, the object futureextends the lifetime of the variable inside the lambda.

For instance:

#include <iostream>
#include <future>

class Dummy
{
public:
    Dummy() {std::cout << this << ": default constructed;" << std::endl;}
    Dummy(const Dummy&) {std::cout << this << ": copy constructed;" << std::endl;}
    Dummy(Dummy&&) {std::cout << this << ": move constructed;" << std::endl;}
    ~Dummy() {std::cout << this << ": destructed;" << std::endl;}
};

int main()
{
    std::packaged_task<void()>* p_task;
    {
        Dummy ScopedDummy;
        p_task = new std::packaged_task<void()>([ScopedDummy](){std::cout << "lambda call with: " << &ScopedDummy << std::endl;});
        std::cout << "p_task completed" << std::endl;
    }
    {
        std::future<void> future_result;
        {
            future_result = p_task->get_future();
            (*p_task)();
            delete p_task;
        }
        std::cout << "after p_task has been deleted, the scope of future_result determines the scope of the dummy inside p_task" << std::endl;
    }
    std::cout << "p_task cleans up when future_result dies" << std::endl;
}

Possible output:

0x7fff9cf873fe: default constructed;
0x7fff9cf873ff: copy constructed;
0x1904b38: move constructed;
0x7fff9cf873ff: destructed;
0x7fff9cf873fe: destructed;
lambda call with: 0x1904b38
after p_task has been deleted, the scope of future_result determines the scope of the dummy inside p_task
0x1904b38: destructed;
p_task cleans up when future_result dies

Thus, the object inside the lambda has a lifetime extended by volume future_result.

If we comment out the line future_result = p_task->get_future();, the possible output is:

0x7fff57087896: default constructed;
0x7fff57087897: copy constructed;
0x197cb38: move constructed;
0x7fff57087897: destructed;
0x7fff57087896: destructed;
lambda call with: 0x197cb38
0x197cb38: destructed;
after p_task has been deleted, the scope of future_result determines the scope of the dummy inside p_task
p_task cleans up when future_result dies

I am wondering which mechanism comes into play here, std::futurecontains a link that supports related objects?

+4
1

gcc7.2.0 packaged_task sources, :

packaged_task(allocator_arg_t, const _Alloc &__a, _Fn &&__fn)
    : _M_state(__create_task_state<_Res(_ArgTypes...)>(std::forward<_Fn>(__fn), __a)){}

~packaged_task()
{
  if (static_cast<bool>(_M_state) && !_M_state.unique())
    _M_state->_M_break_promise(std::move(_M_state->_M_result));
}

_M_state shared_ptr packaged_task. , , gcc packaged_task, , , , _, , shared_future.

, clang , ( clang ).

? ; ,

[[futures.task]]

packaged_task , .

packaged_task (F && f) [...] packaged_task std:: forward (f).

packaged_task (packaged_task & rhs) [...] rhs *.

reset() [...] : * this = packaged_task (std:: move (f)), f - , * .

, _,

[[futures.state]]

- , , . (, ) , (, ) . [. , promises , , . -endnote]

- [: , , , async [...]]

[futures.task.members]

-packaged_task (F && f); [...] f , f [...] - ~ packaged_task(); :

, (, ; , , , , ); , dtor.

, , clang , gcc-. , , , ...

+5

Source: https://habr.com/ru/post/1689008/


All Articles