I'm going through "C++ Concurrency in Action" book which shows a following code snippet as a kind of substitute for std::experimental::async
#include <experimental/future>
template<typename Func>
std::experimental::future<decltype(std::declval<Func>()())>
spawn_async(Func&& func){
std::experimental::promise<decltype(std::declval<Func>()())> p;
auto res=p.get_future();
std::thread t(
[p=std::move(p),f=std::decay_t<Func>(func)]()
mutable{
try{
p.set_value_at_thread_exit(f());
} catch(...){
p.set_exception_at_thread_exit(std::current_exception());
}
});
t.detach();
return res;
}
There is an usage of *_at_thread_exit()
functions to handle both happy and exception paths with an emphasis they are required to set the future ready after all thread_local variables have been properly destroyed as opposed to their ordinary counterparts (set_value
/set_exception
). Why is it so crucial to modify the future's shared stated right after thread_local have been destroyed assuming a thread gets detached (as in the code)?
Any example/crcumstances when this function will lead to program crash or manifest undefined behaviour when set_value
or set_exception
is used rather than actual calls?
There is a bunch of similar questions on stackoverflow but I cannot grasp anything useful from them.