2

This sample prints all 2 messages under windows7 and on ideone.com, but fails to print second message on windows xp. What im doing wrong? If it is a bug, where i should report it?

Compiled for windows xp using visual studio 2017, platform toolset v141_xp.

#include <iostream>
#include <future>
#include <thread>

using namespace std;
int main()
{
    auto f1 = async(launch::async, []()->int {
        cout << "in outer async" << endl;

         auto f2 = async(launch::async, []()->int {
             cout << "in inner async" << endl;
             return 2;
         });
         f2.get();

        return 1;
    });
    f1.get();

    return 0;
}

UPD when using std::thread instead of std::async for inner function - it works well on both systems

auto f2 = thread([]()->int {
    cout << "in inner async" << endl;
    return 2;
});
f2.join();

UPD2

visual studio 2017 cl.exe version 19.14.26428 toolset v141_xp

commandline:

/permissive- /Yu"stdafx.h" /GS /GL /analyze- /Wall /Gy /Zc:wchar_t /Zi /Gm- /O2 /sdl /Fd"Release\vc141.pdb" /Zc:inline /fp:precise /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_USING_V110_SDK71_" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Gd /Oy- /Oi /MD /std:c++17 /FC /Fa"Release\" /EHsc /nologo /Fo"Release\" /Fp"Release\testasync.pch" /diagnostics:classic 

UPD3 looks like launch::async is ignored when used on windows xp

vector<future<void>> v;
for( int i = 0; i < 10; i++ )
    v.push_back(async(launch::async, []() {cout << "thread" << endl; this_thread::sleep_for(5s); }));
for( auto &f : v )
    f.get();

on windows7 this tooks ~6seconds to complete, on windows xp ~50 seconds

Fl0
  • 166
  • 6
  • Compilers versions? Compiler commands? – YSC Oct 10 '18 at 08:44
  • So what happens? Is line `cout << "in inner async" << endl;` executed but not visible in output or what? – user7860670 Oct 10 '18 at 08:50
  • 1
    https://stackoverflow.com/q/6374264/4955498 but this does not explain missing text, only intermixed text. Also, the second cout happens only after the first one is completed, so intermixing does not apply in this case – Michael Veksler Oct 10 '18 at 08:55
  • under xp program just hangs, only one line "in outer async" printed – Fl0 Oct 10 '18 at 08:56
  • 1
    @Fl0 You should post proper problem description then. "fails to print second message" and "program just hangs" are quite different. At which line does it hang? It would be a good idea to include call stacks for running threads as well. – user7860670 Oct 10 '18 at 08:58
  • 1
    On windows XP does your program finishes? In Windows `std::async(...)` sits on top of thread pool. So there can be deadlocks. Inside `f1` you run new task and call `f2.get()`, which blocks until `f2` is finished. But if `auto f2 = async(...)` chose the same thread in which `f1` runs, then you have deadlock and your program should not finish. If it does, then this isn't the case. – nicolai Oct 10 '18 at 09:02

1 Answers1

0

On Windows std::async(...) sits on top of thread pool. So there can be deadlocks. Inside f1 you run new task and call f2.get(), which blocks until f2 is finished. But if auto f2 = async(...) chose the same thread in which f1 runs, then you have deadlock and your program should not finish. If it does, then this isn't the case.

UPDATE

Please, read about Microsoft implementation of std::async here. It says:

The C++ standard states that if policy is launch::async, the function creates a new thread. However the Microsoft implementation is currently non-conforming. It obtains its threads from the Windows ThreadPool, which in some cases may provide a recycled thread rather than a new one. This means that the launch::async policy is actually implemented as launch::async|launch::deferred

There is another answer, which reveals a particular feature of Microsoft's std::async implementation:

  • It limits the total number of background threads it uses, after which a call to std::async will block until a thread becomes free. On my machine, this number is 768.

So, I assume, if you have only one thread in ThreadPool, then the call to std::async from inside of a task will deadlock. Probably it is your case, taking into account your UPD3.

I really recommend to read the mentioned answer, so you could understand why Microsoft's std::async is different and how to use it correctly.

nicolai
  • 1,140
  • 9
  • 17
  • changing inner async to f2 = thread( lambda ); f2.join(); does the work. Is std::thread behaves differently from std::async? – Fl0 Oct 10 '18 at 09:06
  • When the same thread in which f1 runs is selected call to `f2.get()` will never lead to a deadlock, rather it will be executed synchronously. With your logic creating and getting values from async tasks from inside of other async tasks would lead to random deadlocks making async completely unusable... – user7860670 Oct 10 '18 at 09:06
  • Not sure I follow this. If both callbacks run on the same thread, how can there possibly be a deadlock between them? – Lightness Races in Orbit Oct 10 '18 at 09:54
  • Im explicitly specialize launch::async flag to start another thread immediately. It should not be a problem to start a thread from another thread... – Fl0 Oct 10 '18 at 10:07
  • @LightnessRacesinOrbit the point is, that `f2.get()` gets blocked until `f2` is finished. But `f2` will never finish because the thread is blocked. – nicolai Oct 10 '18 at 10:08
  • 1
    @nicolai That's a really silly implementation if so. If `f2` is destined for the same thread, `f2.get()` should simply invoke the callback without blocking at all. That's how it works when async decides not to use a worker thread. Why would it block waiting for something it wants to do on its own thread? Silly! – Lightness Races in Orbit Oct 10 '18 at 10:12
  • @VTT > With your logic creating and getting values from async tasks from inside of other async tasks would lead to random deadlocks making async completely unusable... Yes, standard tasking system can't be used safely. There is talk from Sean Parent, see http://sean-parent.stlab.cc/presentations/2017-01-18-concurrency/2017-01-18-concurrency.pdf. – nicolai Oct 10 '18 at 10:13
  • 1
    @nicolai And which part of the linked document covers this case? `async` with `launch::async` is required to behave as if a new thread has been spawned, and the thread executing `f1` becomes ready to execute `f2` when `f2.get()` is called. This should work regardless of whether implementation uses thread pools, spawns new threads or runs everything in main thread. – user7860670 Oct 10 '18 at 10:41