Question Does std::future call wait() or get() on destruction?
Example
void fun()
{
std::future<int> fut = my_thread_pool.submit(some_work);
}//is fut.wait() or fut.get() called? here
3) std::future is not CopyConstructible.
The class template std::future provides a mechanism to access the result of asynchronous operations: An asynchronous operation (created via std::async, std::packaged_task, or std::promise) can provide a std::future object to the creator of that asynchronous operation.
The class template std::promise provides a facility to store a value or an exception that is later acquired asynchronously via a std::future object created by the std::promise object. Note that the std::promise object is meant to be used only once.
From: The View from the C++ Standard meeting September 2013 Part 2 of 2.
On the issue that async destructors should not block we devoted a great deal of discussion on it. [..] The only position that received considerable support was [..] giving advisory that future destructors will not block, unless returned from async, making it the notable exception. [..] After significant discussion, the only part that we tried to carry was N3776, an attempt to clarify the position that ~future and ~shared_future don’t block except possibly in the presence of async. There was an attempt to issue a deprecation along the lines of C. Deprecate async without replacement. This motion was actually almost put forward. But [..] it died even before it reached the operating table.
Also check: N3679: Async() future destructors must wait
The basic issue
Futures returned by async() with async launch policy wait in their destructor for the associated shared state to become ready. This prevents a situation in which the associated thread continues to run, and there is no longer a means to wait for it to complete because the associated future has been destroyed. Without heroic efforts to otherwise wait for completion, such a "run-away" thread can continue to run past the lifetime of the objects on which it depends.
As an example, consider the following pair of functions:
void f() { vector<int> v; ... do_parallel_foo(v); ... } void do_parallel_foo(vector<int>& v) { auto fut = no_join_async([&] {... foo(v); return ...; }); a: ... fut.get(); ... }
If no_join_async() returns a future whose destructor does not wait for async completion, everything may work well until the code at a throws an exception. At that point nothing waits for the async to complete, and it may continue to run past the exit from both do_parallel_foo() and f(), causing the async task to access and overwite memory previously allocated to v way past it's lifetime.
The end result is likely to be a cross-thread "memory smash" similar to that described in N2802 under similar conditions.
This problem is of course avoided if get() or wait() is called on no_join_async()-generated futures before they are destroyed. The difficulty, as in N2802, is that an unexpected exception may cause that code to be bypassed. Thus some sort of scope guard is usually needed to ensure safety. If the programmer forgets to add the scope guard, it appears likely that an attacker could generate e.g. a bad_alloc exception at an opportune point to take advantage of the oversight, and cause a stack to be overwritten. It may be possible to also control the data used to overwrite the stack, and thus gain control over the process. This is a sufficiently subtle error that, in our experience, it is likely to be overlooked in real code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With