Suppose we have the following code:
std::promise<int> promise;
auto future = promise.get_future();
const auto task = [](auto promise) {
try {
promise.set_value_at_thread_exit(int_generator_that_can_throw());
} catch (...) {
promise.set_exception_at_thread_exit(std::current_exception());
}
};
std::thread thread(task, std::move(promise));
// use future
thread.join();
I wonder if this code is correct and safe, and if no, why.
It appears to work fine when compiled with GCC, but crashes (no message is printed) when compiled with MSVC (2017). My guess is that a crash happens because promise
local variable inside task
goes out of scope and is destroyed too early. If I remove _at_thread_exit
suffixes, this code works as expected (or appears to work). It also works correctly when the promise is captured:
const auto task = [p = std::move(promise)]() mutable {
/*...*/
};
Why does your code generate problems? Let's start with ansewer to 'when _at_thread_exit
writes to shared state of std::future
and std::promise
?'. It happens after destruction of all thread local variables. Your lambda is called within the thread and after its scope is left, the promise is already destroyed. But what happens when thread calling your lambda has some thread-local variables? Well, the writing will occur after destruction of the std::promise
object. Actually, the rest is really undefined in standard. It seems that passing data to shared state could be done after destruction of std::promise
but information is not really there.
Simplest solution is of course this:
std::promise<int> promise;
auto future = promise.get_future();
const auto task = [](std::promise<int>& promise) {
try {
promise.set_value_at_thread_exit(int_generator_that_can_throw());
} catch (...) {
promise.set_exception_at_thread_exit(std::current_exception());
}
};
std::thread thread(task, std::ref(promise));
// use future
thread.join();