11

I have a multithreaded application, with an loop waiting for user input as main thread. On the correct input, it is supposed to stop the loop and wait for all other threads, to end propperly.

For this purpose I created an std::list in which I put the std::future objects created for thread creation

std::list<std::future<int>> threads;
threads.emplace_front(std::async(std::launch::async, ...));

I was under the impression, that letting the list run out of scope, should block, until all threads return their main function, because the lists destructor will destrurct all std::future elements and the destructor of those will wait, for the thread to finish.

EDIT: Since it is relevant I will add it here: This is on Win7 with the MSVC version in Visual Studio 2013 Professional /EDIT

When I tried this, it didn't block, I had to add

for (auto it = threads.begin(); it != threads.end(); ++it) {
    it->get();
}

to the end of the function, to block correctly.

Did I missunderstand something, or do I have to create the thread in a different way, to do what I want to do here?

Ongy
  • 271
  • 3
  • 13
  • @T.C.if it really is that easy, and I shouldn't rely on them blocking, you should add this as an answer, so I can accept it – Ongy Sep 06 '14 at 09:14
  • 4
    looks like a bug. What is the compiler? – ixSci Sep 06 '14 at 09:14
  • Hmm, Scott Meyers [makes a good argument](http://scottmeyers.blogspot.com/2013/03/stdfutures-from-stdasync-arent-special.html) that `std::async`'s specification requires it to block. Interesting... – T.C. Sep 06 '14 at 09:16
  • You may want to note the implementation you're using, including platform. The behavior you're seemingly expecting is exactly what I experience on my clang3.4 impl (OS X) – WhozCraig Sep 06 '14 at 09:23
  • tried in MSVC with MSVS 2013 on current patches, I will try on clang/gcc and report back – Ongy Sep 06 '14 at 09:30
  • 1
    https://connect.microsoft.com/VisualStudio/feedback/details/810623 – T.C. Sep 06 '14 at 09:33
  • Ok, on linux with clang it works as expected, it works as I expected, so I guess the "may" is the importand part here – Ongy Sep 06 '14 at 09:35
  • T.C. I think that link is the correct answer, thank you – Ongy Sep 06 '14 at 09:43

2 Answers2

13

This is a MSVC bug that has been fixed, but the fix won't be available until MS releases a new version of Visual C++, probably some time in 2015. (It's also available in the CTP for the new version, but it's a pretty bad idea to use that for any production code...)

As Scott Meyers explained in his blog post, the destructor of a std::future returned by a std::async call using the launch::async policy is required to block until the spawned thread completes execution (§30.6.8 [futures.async]/p5):

If the implementation chooses the launch::async policy,

  • [...]
  • the associated thread completion synchronizes with (1.10) the return from the first function that successfully detects the ready status of the shared state or with the return from the last function that releases the shared state, whichever happens first.

In this case, the future's destructor is the "last function that releases the shared state", so the thread completion must synchronize with (i.e., happen before) the return of that function.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • +1 MS did something similar with a VS2010 regex bug concerning a sequence count incorrectly being +1'ed. The result was to use their regex with that specific feature you had to "break" your expression so it "worked" with their implementation, but in reality was the *wrong* expression and therefore wouldn't work anywhere else. As you can imagine, MS's decision to *not* fix this in SP1 though they knew about it long-before was not well received (people were furious). At least here we can wrap you're future in a movable that invokes `.wait()` on destruction, so you have a viable alternative. – WhozCraig Sep 06 '14 at 09:46
  • Thanks for the link! Just got crash because of this bug. – Mikhail Nov 25 '15 at 16:28
0

I've looked at the documentation of std::future and found this for the destructor of std::future:

Releases any shared state. This means

  • if the return object or provider holds the last reference to its shared state, the shared state is destroyed; and
  • the return object or provider gives up its reference to its shared state; and
  • these actions will not block for the shared state to become ready, except that it may block if all of the following are true: the shared state was created by a call to std::async, the shared state is not yet ready, and this was the last reference to the shared state.

Pay attention on the last point. In my opinion you have to call the get's at the end of your scope.

Franz
  • 93
  • 6
  • `future's dtor` should block if that future was acquired via `async` call. No need for `get` there – ixSci Sep 06 '14 at 09:23
  • @ixSci Is this also the case if you do it without std::async? – Franz Sep 06 '14 at 09:26
  • No, it's special to `std::async` call. It's pretty confusing, though. Look at the link @T.C. posted in the comments to the original question. – ixSci Sep 06 '14 at 09:28