ChronoTrigger
ChronoTrigger

Reputation: 8617

Who is the creator of a std::future?

Cppreference says about std::future that

The creator of the asynchronous operation can then use a variety of methods to query, wait for, or extract a value from the std::future.

Who is the creator of the asynchronous operation? Is it the thread that creates the std::future object, or any thread that has access to that object? In the end, my question is if non-creators can also use the get method on the std::future.

In particular, I would like to know if this piece of code is correct:

std::future<int> foo;
std::thread t([&foo](){ 
    foo = std::async(std::launch::async, [](){ return 4; });
});
t.join();
int n = foo.get();  // can the main thread call foo.get()?

Upvotes: 3

Views: 156

Answers (1)

WhiZTiM
WhiZTiM

Reputation: 21576

Who is the creator of the asynchronous operation? Is it the thread that creates the std::future object, or any thread that has access to that object?

There are essentially 3 things that can create such "asynchronous operation":

  • std::async
  • std::promise
  • std::packaged_task

These "creators" create a so called shared state, in which the std::future obtained equally shares access to. The shared state is where the results of such operation is stored. both the provider (an std::async, std::promise, or std::packaged_task object) and the consumer (the std::future obtained) accesses the shared state in a thread safe manner, its an implementation detail you shouldn't be bothered about.

In the end, my question is if non-creators can also use the get method on the std::future.

Of cause; An "asynchronous operation" typically takes place in a different thread of execution, and the purpose of std::future is to safely query and access the results of such "asynchronous operation" happening somewhere else, without you explicitly using any extra synchronization mechanism.

In particular, I would like to know if this piece of code is correct:

std::future<int> foo;
std::thread t([&foo](){ 
    foo = std::async(std::launch::async, [](){ return 4; });
});
t.join();
int n = foo.get();  // can the main thread call foo.get()?

While this "particular short snippet" doesn't seem to invoke a race condition; It's not in any way a good code. At any point in time, the std::future to a shared state obtained from an "asynchronous operation" should not be used by multiple thread at a time.

In your case, the get() and the assignment operator isn't thread-safe. And sooner or later, this code will quickly grow to invoke a race-condition

One more note, the use of a std::thread in your code isn't needed, in practice a decent implementation will create a new thread or use a thread pool when your launch policy is std::launch::async. Hence you should just do:

std::future<int> foo = std::async(std::launch::async, [](){ return 4; });
int n = foo.get();

Upvotes: 1

Related Questions