Home > OS >  reassignment/move of std::future waits for existing future to complete
reassignment/move of std::future waits for existing future to complete

Time:07-12

The main method below launches two std::asyncs. The future f in the main method is initially used to hold the future for the first async before being reassigned to the future of the second std::async. Both threads still appear to still complete on schedule which surprised me. Initially I (foolishly?) expected the first thread to be terminated/suspended on the reassignment of the future but was surprised to find it still lingering around.

Based on what I observed today I think I understand that the first async thread becomes detached at the future reassignment. I kept seeing segfaults during my ordeal today and I reckon this was simply the main thread terminating before the orphaned async processes could finish.

////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <future>
#include <thread>
#include <chrono>

#define WAIT_A_MINUTE std::this_thread::sleep_for(std::chrono::seconds(5));

int myBusyFunc() {
    WAIT_A_MINUTE
    std::cout<<"done!"<<std::endl;
    return 71675;
}

std::string futureStatusStr(std::future<int>& f){
    switch (f.wait_for(std::chrono::seconds(0))){
    case std::future_status::ready: return "ready";
    case std::future_status::timeout: return "timeout";
    case std::future_status::deferred: return "deferred";
    default:return "unknown";
    };
}

void printFutureStatus(std::future<int>& f){
    std::cout<<futureStatusStr(f)<<std::endl;
}
/////////////////////////////////////////////////////////////////////////////

int main()
{
    std::future<int> f = std::async(std::launch::async,myBusyFunc);
    printFutureStatus(f);
    f = std::async(std::launch::async,myBusyFunc);  // waits a minute
    printFutureStatus(f);
    f.wait(); // no wait required - ready
    printFutureStatus(f);

    return 0;
}

CodePudding user response:

This behavior is actually documented in std::future::~future. Excerpt from the documentation below:

  • these actions will not block for the shared state to become ready, except that it may block if all of the following are true:

    • the shared state was created by a call to std::async,
    • the shared state is not yet ready, and
    • this was the last reference to the shared state.

In practice, these actions will block only if the task’s launch policy is std::launch::async (see "Effective Modern C " Item 36), either because that was chosen by the runtime system or because it was specified in the call to std::async.

So, you can see that this result is not unexpected. When you reassign f, the old object is destroyed and in that process the caller becomes blocked.

Taking the above into consideration, if you need to replace the future without blocking, you have a few options:

  1. Use deferred execution policy (std::launch::deferred)
  2. Store the shared state before replacing: auto g = f.share();
  3. If you want to discard or "abort" the old async call, introduce a mechanism to signal it for early-out.
  • Related