Get the Status of a Std::Future

Get the status of a std::future

You are correct, and apart from calling wait_until with a time in the past (which is equivalent) there is no better way.

You could always write a little wrapper if you want a more convenient syntax:

template<typename R>
bool is_ready(std::future<R> const& f)
{ return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready; }

N.B. if the function is deferred this will never return true, so it's probably better to check wait_for directly in the case where you might want to run the deferred task synchronously after a certain time has passed or when system load is low.

std::future get() blocks when wait_for() status is ready and wait() returns

Interesting! The first thing I found was that you are waiting on the second thread to have something to pop as pointed out by @rafix07. I'm not sure what the ultimate objective is but this works. I tested on MSVC and here it is with g++ on Coliru

#include <chrono>
#include <condition_variable>
#include <future>
#include <functional>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
#include <cassert>

template<class T>
class BlockingQueue {
std::queue<T> theQueue;
std::mutex mtx;
std::condition_variable hasDataCondition;

public:
void push(const T& t) {
std::unique_lock<std::mutex> lock{ mtx };
theQueue.push(t);
hasDataCondition.notify_all();
}

T popWhenAvailable(int i) {
std::unique_lock<std::mutex> lock{ mtx };
std::cout << "popWhenAvailable: " << i << std::endl;
if (theQueue.empty()) {
std::cout << "Waiting " << i << std::endl;
hasDataCondition.wait(lock, [this] {return ! theQueue.empty(); });
std::cout << "Done waiting " << i << std::endl;
}
T front = std::move(theQueue.front());
theQueue.pop();
std::cout << "Got value " << front << " and popped it on " << i << std::endl;
return front;
}
};

int main(int argc, char** argv) {
using namespace std::chrono_literals;
BlockingQueue<int> q;

auto futureInt0 = std::async(std::launch::async, [&] {return q.popWhenAvailable(0); });
auto futureInt1 = std::async(std::launch::async, [&] {return q.popWhenAvailable(1); });
std::cout << "Starting threads...\n" << std::endl;
std::this_thread::sleep_for(1000ms);

assert(futureInt0.wait_for(std::chrono::milliseconds(300)) != std::future_status::ready);
assert(futureInt1.wait_for(std::chrono::milliseconds(300)) != std::future_status::ready);

std::cout << "Pushing data..." << std::endl;
q.push(4);
std::cout << "Pushed! Checking results..." << std::endl;

std::pair<bool, bool> done = { false,false };
for (;;) {
if (!done.first && futureInt0.wait_for(std::chrono::milliseconds(300)) == std::future_status::ready) {
std::cout << "Future 0 ready." << std::endl;
futureInt0.wait();
std::cout << "Now get() the value 0: " << futureInt0.get() << std::endl;
done.first = true;
}
else if(!done.second && futureInt1.wait_for(std::chrono::milliseconds(300)) == std::future_status::ready) {
std::cout << "Future 1 ready." << std::endl;
futureInt1.wait();
std::cout << "Now get() the value 1: " << futureInt1.get() << std::endl;
done.second = true;
}
if (done.first && done.second)
break;
else if(done.first || done.second)
q.push(8);
}
}

Is there a way to check if std::future state is ready in a guaranteed wait-free manner?

The quote from cppreference is simply there to remind you that the OS scheduler is a factor here and that other tasks requiring platform resources could be using the CPU-time your thread needs in order to return from wait_for() -- regardless of the specified timeout duration being zero or not. That's all. You cannot technically be guaranteed to get more than that on a non-realtime platform. As such, the C++ Standard says nothing about this, but you can see other interesting stuff there -- see the paragraph for wait_for() under [futures.unique_future¶21]:

Effects: None if the shared state contains a deferred function
([futures.async]), otherwise blocks until the shared state is ready or
until the relative timeout ([thread.req.timing]) specified by
rel_­time has expired.

No such mention of the additional delay here, but it does say that you are blocked, and it remains implementation dependent whether wait_for() is yield()ing the thread1 first thing upon such blocking or immediately returning if the timeout duration is zero. In addition, it might also be necessary for an implementation to synchronize access to the future's status in a locking manner, which would have to be applied prior to checking if a potential immediate return is to take place. Hence, you don't even have the guarantee for lock-freedom here, let alone wait-freedom.

Note that the same applies for calling wait_until with a time in the past.

Is it still the case when timeout_duration is 0 ? If so, is there
another way to query the state in a guaranteed wait-free manner ?

So yes, implementation of wait_free() notwithstanding, this is still the case. As such, this is the closest to wait-free you're going to get for checking the state.


1 In simple terms, this means "releasing" the CPU and putting your thread at the back of the scheduler's queue, giving other threads some CPU-time.

How to comprehend std::future shared state?

According to the cppreference documentation on std::future:

When the asynchronous operation is ready to send a result to the creator, it can do so by modifying shared state (e.g. std::promise::set_value) that is linked to the creator's std::future.

Shared state here refers to some mutable stuff readable/writeable by multiple threads. Consider the example straight from the docs:

std::promise<int> p;
std::future<int> f = p.get_future();
std::thread([&p]{ p.set_value_at_thread_exit(9); }).detach();
f.wait();

The main thread and the created thread share p (and since p is stateful, they share state!). Calling std::promise::set_value_at_thread_exit inside the body of the lambda effectively modifies shared state between these two threads. This is essentially all that is meant.

The std::future given by the promise is a representation of the "interesting" part of the shared state -- the value you care about.

Can std::future cause coredump without get or wait

Futures created with std::async have a blocking destructor that waits for the task to finish.

Even if you instead create a future from std::promise, the docs for ~future() don't mention the problem you're asking about.

The docs use the term "shared state" a lot, implying that a promise and a future share a (heap-allocated?) state, which holds the return value, and isn't destroyed until both die.



Related Topics



Leave a reply



Submit