Number of async/futures in C++11
Asked Answered
I

4

12

I'm trying a program:

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

int foo() {
  return 0;
}

int main(int argc, char* argv[]) {
  for (auto i = 0L; i < 10000; ++i) {
    auto f = std::async(foo);
    f.get();
  }
  return 0;
}

Compiler VS11 x64.

Build:

cl /EHsc /Zi async.cpp && async

For me this program crashes. I suspect, there is a limit in the number of futures physically running at the same time. If I reduce the number of iterations to a couple of orders, it works.

So, two questions:

  1. Is there a limit of actually running futures in C++11?

  2. Why does this code crash at all? If I explicitly do "get()" immediately after "async()", it must complete the future before the next iteration, which means only one future runs at a time.

UPDATE

I've simplified the code to:

#include <future>

int main(int argc, char* argv[]) {
  for (auto i = 0L; i < 1000000; ++i) {
    auto f = std::async([](){ return 0; });
    f.get();
  }
  return 0;
}

And it still crashes for me. It doesn't throw, I've checked that. But now I have a visible stack trace:

async.exe!_Mtx_unlock(_Mtx_internal_imp_t * * mtx) Line 229  C++
async.exe!std::_Mtx_unlockX(_Mtx_internal_imp_t * * _Mtx) Line 84  C++
async.exe!std::_Mutex_base::unlock() Line 47  C++
async.exe!std::unique_lock<std::mutex>::~unique_lock<std::mutex>() Line 284  C++
async.exe!std::_Associated_state<int>::_Set_value(int && _Val, bool _At_thread_exit) Line 358  C++
async.exe!std::_Packaged_state<int __cdecl(void)>::_Call_immediate() Line 569  C++
async.exe!std::_Async_state<int>::`std::U_Nil::ain::ain'::`3'::<lambda_A200A86DFF9A63A1>::operator()() Line 700  C++
async.exe!??$_ApplyX@X@?$_Callable_obj@V<lambda_A200A86DFF9A63A1>@?2???$?0V?$_Bind@$0A@XV<lambda_23AC5A2FBB53FD4D>@?5?main@U_Nil@std@@U23@U23@U23@U23@U23@U23@@std@@@?$_Async_state@H@std@@QEAA@$$QEAV?$_Bind@$0A@XV<lambda_23AC5A2FBB53FD4D>@?5?main@U_Nil@std@@U23@U23@U23@U23@U23@U23@@3@@Z@$0A@@std@@QEAAXXZ() Line 420  C++
async.exe!?_Do_call@?$_Func_impl@U?$_Callable_obj@V<lambda_A200A86DFF9A63A1>@?2???$?0V?$_Bind@$0A@XV<lambda_23AC5A2FBB53FD4D>@?5?main@U_Nil@std@@U23@U23@U23@U23@U23@U23@@std@@@?$_Async_state@H@std@@QEAA@$$QEAV?$_Bind@$0A@XV<lambda_23AC5A2FBB53FD4D>@?5?main@U_Nil@std@@U23@U23@U23@U23@U23@U23@@3@@Z@$0A@@std@@V?$allocator@V?$_Func_class@XU_Nil@std@@U12@U12@U12@U12@U12@U12@@std@@@2@XU_Nil@2@U42@U42@U42@U42@U42@U42@@std@@UEAAXXZ() Line 217  C++
async.exe!std::_Func_class<void,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::operator()() Line 486  C++
async.exe!`Concurrency::details::_MakeVoidToUnitFunc'::`3'::<lambda_25D33530A43E1C90>::operator()() Line 1056  C++
async.exe!std::_Callable_obj<`Concurrency::details::_MakeVoidToUnitFunc'::`3'::<lambda_25D33530A43E1C90>,0>::_ApplyX<Concurrency::details::_Unit_type>() Line 420  C++
async.exe!std::_Func_impl<std::_Callable_obj<`Concurrency::details::_MakeVoidToUnitFunc'::`3'::<lambda_25D33530A43E1C90>,0>,std::allocator<std::_Func_class<Concurrency::details::_Unit_type,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> >,Concurrency::details::_Unit_type,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::_Do_call() Line 217  C++
async.exe!std::_Func_class<Concurrency::details::_Unit_type,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::operator()() Line 486  C++
async.exe!`Concurrency::details::_Task_impl<Concurrency::details::_Unit_type>::_ScheduleTask'::`3'::<lambda_7D9BCD859405C05B>::operator()() Line 325  C++
async.exe!Concurrency::details::_PPLTaskHandle<`Concurrency::details::_Task_impl<Concurrency::details::_Unit_type>::_ScheduleTask'::`3'::<lambda_7D9BCD859405C05B> >::operator()() Line 72  C++
async.exe!Concurrency::details::_UnrealizedChore::_InvokeBridge<Concurrency::details::_PPLTaskHandle<`Concurrency::details::_Task_impl<Concurrency::details::_Unit_type>::_ScheduleTask'::`3'::<lambda_7D9BCD859405C05B> > >(Concurrency::details::_PPLTaskHandle<`Concurrency::details::_Task_impl<Concurrency::details::_Unit_type>::_ScheduleTask'::`3'::<lambda_7D9BCD859405C05B> > * _PChore) Line 4190  C++
async.exe!Concurrency::details::_UnrealizedChore::_UnstructuredChoreWrapper(Concurrency::details::_UnrealizedChore * pChore) Line 275  C++
async.exe!Concurrency::details::_PPLTaskChore::_DeletingChoreWrapper(Concurrency::details::_UnrealizedChore * pChore) Line 78  C++
async.exe!Concurrency::details::InternalContextBase::ExecuteChoreInline(Concurrency::details::WorkItem * pWork) Line 1600  C++
async.exe!Concurrency::details::InternalContextBase::Dispatch(Concurrency::DispatchState * pDispatchState) Line 1704  C++
async.exe!Concurrency::details::FreeThreadProxy::Dispatch() Line 191  C++
async.exe!Concurrency::details::ThreadProxy::ThreadProxyMain(void * lpParameter) Line 173  C++
kernel32.dll!0000000076df652d()  Unknown
ntdll.dll!0000000076f2c521()  Unknown

and threads:

Unflagged       1864    0   Worker Thread   ntdll.dll thread    ntdll.dll!0000000076f518ca  Normal
Unflagged       10964   0   Main Thread Main Thread async.exe!do_signal Normal
Unflagged       7436    0   Worker Thread   ntdll.dll thread    ntdll.dll!0000000076f52c1a  Normal
Unflagged       10232   0   Worker Thread   async.exe!Concurrency::details::ThreadProxy::ThreadProxyMain    async.exe!Concurrency::details::ThreadProxy::SuspendExecution   Normal
Unflagged   >   10624   0   Worker Thread   async.exe!Concurrency::details::ThreadProxy::ThreadProxyMain    async.exe!_Mtx_unlock   Normal
Unflagged       4756    0   Worker Thread   async.exe!Concurrency::details::ThreadProxy::ThreadProxyMain    async.exe!Concurrency::details::ThreadProxy::SuspendExecution   Normal
Unflagged       11100   0   Worker Thread   async.exe!Concurrency::details::ThreadProxy::ThreadProxyMain    async.exe!Concurrency::details::InternalContextBase::WaitForWork    Normal
Unflagged       6440    0   Worker Thread   async.exe!Concurrency::details::ThreadProxy::ThreadProxyMain    async.exe!std::vector<std::pair<void (__cdecl*)(void * __ptr64),void * __ptr64>,std::allocator<std::pair<void (__cdecl*)(void * __ptr64),void * __ptr64> > >::_Tidy Normal

I'm using VS 11.0.40825.2 PREREL.

Impish answered 24/4, 2012 at 15:48 Comment(14)
I don't believe this invocation launches another thread, its just a delayed execution. E.g., foo should get called when the line f.get() is executed.Actinouranium
The debugger should help you to understand why the code crashes. I would try that before assuming the compiler is broken.Heisser
By default, you get delayed execution, so foo() gets evaluated each time you call get().Faradmeter
@Faradmeter Hm, clang++ seems to execute immediately in a separate thread.Colvert
The async call tells the implementation that you want the function to be run asynchronously. It is an implementation detail whether a thread is launched, a thread is used from a thread pool or nothing happens and the call to get (or any other member of the future triggers the actual evaluation of the future)Orban
@SteveTownsend: Well, the code seems fairly simple and C++11 compliant. If it crashes (sign undefined behavior) and the code is valid, then there is an issue with the implementation (compiler + runtime). That is, unless I have failed to see that the code is incorrect. I agree that there is value in understanding why it is broken.Orban
Unfortunatelly when VS picks up the crash, the stack information has no meaningful details, just a long list of addresses and the disassembler output also has no symbols at all to figure out where is it.Impish
@David - agreed, I just hate to see "I suspect" in a question where the debugger would it seems allow more definitive info to be givenHeisser
Sorry, it runs fine on GCC 4.7, both with and without asynchronous launching.Faradmeter
@Impish - is that with Debug or Release? Try Debug if not done already. Can you add output to count the number of iterations before failure? Does adding exception handling here make any difference?Heisser
@Steve Townsend - Debug. When I add "std::cout << i << std::endl;" to the loop to figure out where is the limit, it runs fine and prints the numbers. Maybe this printing changes timing, and as a side effect it doesn't crash anymore.Impish
@Impish : Sounds like a race condition inside of ConcRT. I'd file a bug report...Evanish
I don't get any crash for this. VS11 x64, release build, Same source and command line.Caryl
None of you are using VS2011, because it hasn't released yet. Indicate which pre-release build you're running, for this to be at all meaningful.Mat
C
4
  1. Obviously there are implementation limits, just like there's a limit to how big an array can be. std::async can signal an error 'resource_unavailable_try_again' if the launch policy is lauch::async and the system couldn't start a new thread. But you're not getting this error.

  2. The program shouldn't crash and doesn't for me (VS11 x64, release build, same source and command line).

    I believe that even without .get() the program would not have more than one asynchronous operation going at once. You assign the future to a local variable the future is destroyed each loop iteration, forcing the asynchronous operation to complete before you start another one in the next loop iteration.

Caryl answered 24/4, 2012 at 18:16 Comment(0)
L
1

Try to enclose your code in main() with a try-catch and check if it throws an std::exception. This might give a hint. Besides that, C++11 in VS is still beta.

Lobate answered 29/4, 2012 at 15:28 Comment(1)
And beta is when bug reports are most valuable.Mat
C
1
  1. No, Standards doesn't say limitation about thread support library (include thread, future, etc.).

  2. It depends on implementation quality of thread support library and the underlying API. As you say f.get() waits for the task complation (this behavior required C++ Standard). When the library implementation may NOT reuse resources such as underlying API's thread handle, it MAY cause lack of system resouce and program crash. It's a quality of library implementation.

Caseous answered 2/5, 2012 at 7:38 Comment(0)
P
0

If you're using g++ with the std=c++11 option, make sure you have a recent version and link with pthread.

For example, on the CodingGround the default compilation that you get (with the Compile button) is:

g++ -std=c++11 -o main *.cpp 

and it fails with "what(): Unknown error -1". But you can manually add -lpthread to the compilation command:

g++ -std=c++11 -o main -lpthread *.cpp

and it will work just fine.

Podium answered 17/3, 2017 at 6:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.