C++
std::future
concurrency
multithreading
programming tips

Get the status of a stdfuture

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

To check the state of a std::future, use wait_for or wait_until. Those functions let you ask whether the result is ready, still waiting, or deferred without immediately calling get().

This matters because get() blocks until the result is available. If you want non-blocking polling or a short timed wait, future_status is the tool you are looking for.

Use wait_for to Inspect the State

The most common pattern is a zero-duration wait:

cpp
1#include <chrono>
2#include <future>
3#include <iostream>
4
5int main() {
6    auto future = std::async(std::launch::async, [] {
7        std::this_thread::sleep_for(std::chrono::seconds(1));
8        return 42;
9    });
10
11    auto status = future.wait_for(std::chrono::seconds(0));
12
13    if (status == std::future_status::ready) {
14        std::cout << "ready\n";
15    } else if (status == std::future_status::timeout) {
16        std::cout << "not ready yet\n";
17    } else if (status == std::future_status::deferred) {
18        std::cout << "deferred\n";
19    }
20}

With a zero timeout, wait_for does not meaningfully block. It simply reports the current state.

Understand the Three Possible Results

wait_for returns one of three std::future_status values:

  • 'ready: the result can be retrieved immediately'
  • 'timeout: the wait duration expired and the result is still not ready'
  • 'deferred: the task was created with deferred launch semantics and has not started running yet'

That third case surprises people. A deferred future does not represent "running in the background." It represents work that will run only when you call get() or wait().

Example with Deferred Execution

Here is a small example that shows the deferred state:

cpp
1#include <future>
2#include <iostream>
3
4int main() {
5    auto future = std::async(std::launch::deferred, [] {
6        return 99;
7    });
8
9    auto status = future.wait_for(std::chrono::milliseconds(0));
10
11    if (status == std::future_status::deferred) {
12        std::cout << "task is deferred\n";
13    }
14
15    std::cout << future.get() << "\n";
16}

That code does not start work until get() is called.

Polling Versus Waiting

Polling a future in a tight loop is usually a bad idea because it burns CPU and adds noise to your design. If you genuinely need polling, use a small delay between checks or integrate the future into a larger event loop or scheduling system.

If you can wait for completion, a timed wait is often better:

cpp
auto status = future.wait_for(std::chrono::milliseconds(100));

That gives the task some time to finish while still returning control if it takes too long.

It also documents intent better than a manual sleep loop because the waiting behavior stays attached to the future itself.

That usually improves readability too.

In many applications, the cleanest approach is not polling at all. Let one thread wait and notify the rest of the program through a queue, callback, condition variable, or another higher-level synchronization mechanism.

Remember That get() Consumes the Future

After calling get(), the future no longer holds a retrievable result. You cannot keep checking status and then call get() multiple times on the same std::future.

If you need multiple readers, use std::shared_future instead.

Common Pitfalls

  • Calling get() just to see whether the result is ready, which blocks unnecessarily.
  • Forgetting that wait_for(0s) can return deferred, not only ready or timeout.
  • Polling continuously in a tight loop and wasting CPU.
  • Assuming a deferred future is already running in the background.
  • Trying to read from the same std::future again after get() has already consumed it.

Summary

  • Use wait_for or wait_until to inspect a std::future without immediately blocking on get().
  • A status check can report ready, timeout, or deferred.
  • 'wait_for(std::chrono::seconds(0)) is the usual non-blocking status probe.'
  • Deferred futures do not start until you explicitly wait for or get their result.
  • If you need repeated access to one result, use std::shared_future instead of std::future.

Course illustration
Course illustration