5

I've encounter a bug in Visual Studio 11 Developer Preview, at least I think it is a bug and reported it, but I'm interested whether someone know a workaround.

When I use std::thread class to create more then one thread it causes application to crash. Sometimes it throws exception, sometimes it causes access violation and sometimes it works. Code that reproduces the bug looks like this:

#include <iostream>
#include <thread>
#include <vector>

#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
 std::vector<std::thread*> threads;
 for(int i = 0; i < 10; i++)
 {
   threads.push_back(new std::thread([i]
     {
       /*std::cout << "thread " << i << std::endl;*/
       /* whatever else that is thread safe, or even an empty lambda */
     }));
 }

 for(int i = 0; i < 10; i++)
 {
   threads[i]->join();
   delete threads[i];
 }

 return 0;
}

It doesn't matter whether the static or dynamic CRT libraries are used (all of them are multi-threaded)..

bug report

Stacktrace (thrown exception, unlocking unowned mutex):

test.exe!_NMSG_WRITE(int rterrnum) Line 217 C
test.exe!abort() Line 62    C
test.exe!_Thrd_abort(const char * msg) Line 111 C
test.exe!_Mtx_unlock(_Mtx_internal_imp_t * * mtx) Line 206  C++
test.exe!_Save_state(_Mtx_internal_imp_t * * mtx, _Mtx_state * state) Line 266  C++
test.exe!do_wait(_Cnd_internal_imp_t * * cond, _Mtx_internal_imp_t * * mtx, const xtime * target) Line 103  C
test.exe!_Cnd_wait(_Cnd_internal_imp_t * * cond, _Mtx_internal_imp_t * * mtx) Line 198  C
test.exe!std::_Cnd_waitX(_Cnd_internal_imp_t * * _Cnd, _Mtx_internal_imp_t * * _Mtx) Line 94    C++
test.exe!std::_Pad::_Launch(_Thrd_imp_t * _Thr) Line 97 C++
test.exe!??$_Launch@V?$_Bind@$0A@XV<lambda_1B7F0477D0C0EDFD>@?4?wmain@U_Nil@std@@U23@U23@U23@U23@U23@U23@@std@@@std@@YAXPAU_Thrd_imp_t@@ABV?$_Bind@$0A@XV<lambda_1B7F0477D0C0EDFD>@?4?wmain@U_Nil@std@@U23@U23@U23@U23@U23@U23@@0@@Z(_Thrd_imp_t * _Thr, const std::?$_Bind@$0A@XV<lambda_1B7F0477D0C0EDFD>@?4?wmain@U_Nil@std@@U23@U23@U23@U23@U23@U23@ & _Tg) Line 207    C++
test.exe!main::main(wmain::__l5::<lambda_1B7F0477D0C0EDFD> _Fx) Line 47 C++
test.exe!wmain(int argc, wchar_t * * argv) Line 17  C++
test.exe!__tmainCRTStartup() Line 238   C
test.exe!wmainCRTStartup() Line 168 C
kernel32.dll!76d7339a() Unknown
[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]  
ntdll.dll!77cc9ef2()    Unknown
ntdll.dll!77cc9ec5()    Unknown

Stacktrace (access violation):

test.exe!_Mtx_unlock(_Mtx_internal_imp_t * * mtx) Line 218  C++
test.exe!std::_Mtx_unlockX(_Mtx_internal_imp_t * * _Mtx) Line 84    C++
test.exe!std::_Pad::_Release() Line 105 C++
test.exe!?_Run@?$_LaunchPad@V?$_Bind@$0A@XV<lambda_1B7F0477D0C0EDFD>@?4?wmain@U_Nil@std@@U23@U23@U23@U23@U23@U23@@std@@@std@@CAIPAV12@@Z(std::?$_LaunchPad@V?$_Bind@$0A@XV<lambda_1B7F0477D0C0EDFD>@?4?wmain@U_Nil@std@@U23@U23@U23@U23@U23@U23@@std@@ * _Ln) Line 195  C++
test.exe!?_Go@?$_LaunchPad@V?$_Bind@$0A@XV<lambda_1B7F0477D0C0EDFD>@?4?wmain@U_Nil@std@@U23@U23@U23@U23@U23@U23@@std@@@std@@UAEIXZ() Line 187   C++
test.exe!_Call_func(void * _Data) Line 52   C++
test.exe!_callthreadstartex() Line 308  C
test.exe!_threadstartex(void * ptd) Line 291    C
kernel32.dll!76d7339a() Unknown
[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]  
ntdll.dll!77cc9ef2()    Unknown
ntdll.dll!77cc9ec5()    Unknown

Thanks!

James McNellis
  • 348,265
  • 75
  • 913
  • 977
Mladen Janković
  • 7,867
  • 3
  • 22
  • 26
  • Is `std::cout` supposed to be thread safe? – Nicol Bolas Feb 23 '12 at 21:03
  • Even if you remove it, it still crashes. I just put it there as an example. – Mladen Janković Feb 23 '12 at 21:06
  • 1
    Since we are talking about C++11, cout should be thread safe on byte level and not cause a crash. See http://stackoverflow.com/questions/6374264/is-cout-synchronized-thread-safe – Correa Feb 23 '12 at 21:16
  • I have a hunch something in your `/* whatever else that is thread safe */` isn't thread safe. – Fred Larson Feb 23 '12 at 21:21
  • I'm pretty sure that empty lambda, which also causes crash, is thread-safe :) – Mladen Janković Feb 23 '12 at 21:31
  • 1
    @fredlarson: the exact code above (with an empty lambda body) causes an accvio in VS2011. – Nate Kohl Feb 23 '12 at 21:32
  • Ok, it didn't say anything about an empty lambda when I posted my comment. – Fred Larson Feb 23 '12 at 21:39
  • Assuming you run it from the debugger, what's the callstack? You can use resolve symbols from Microsoft's symbol server to get a proper callstack. – MSN Feb 23 '12 at 21:43
  • I tought about posting stacktrace, but it crashes on lot of different places and in different ways, my guess it is caused by race condition. I'll post stak trace of most common crash. – Mladen Janković Feb 23 '12 at 21:45
  • Even a simple `void f() {} ... std::thread t(f);` doesn't seem to work in VC11 right now. – Nate Kohl Feb 23 '12 at 21:48
  • What are your compiler options? I.e., which version of the CRT are you compiling against? (Multithreaded DLL, singlethreaded static lib, etc.?) – MSN Feb 23 '12 at 21:56
  • @MSN: I tried all multi-threaded (static and DLLs, debug and release) libraries. Btw. single-threaded libs are removed long time ago. – Mladen Janković Feb 23 '12 at 22:02
  • Is the lambda expression going out of scope too soon (doesn't it have to be explicitly pinned with something that reference counts for this to be valid?) – holtavolt Feb 23 '12 at 22:13
  • std::thread's constructor makes a copy of provided object (in this case lambda) so the object used by the thread doesn't go out of the scope. – Mladen Janković Feb 23 '12 at 22:19
  • What if you delete the threads after they've all joined ? (Won't prevent the bug but may help you work around it) – J.N. Feb 23 '12 at 22:46

2 Answers2

6

This is a known issue. See the following Connect bug:

std::thread crashes with error "f:\dd\vctools\crt_bld\self_x86\crt\src\thr\mutex.cpp(206): unlock of unowned mutex"

In the comments of that bug report, Stephan says, "We've fixed it, and the fix will be available in VC11." (Whether the fix will be present in the VC11 Beta, I do not know. We'll find out next week.)

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • if we can go by Herb's post, he says we can use VC11 compiler and it's features for production code. The above problem should have been fixed in Beta. http://herbsutter.com/2012/02/23/vc11-beta-on-feb-29/ – Jagannath Feb 24 '12 at 10:05
  • 1
    @Jagannath what Herb says is not that it is production-ready (if it was, it would've been released today), but that you are *allowed* to use it for production code. With some earlier releases, the license specifically forbade using pre-release versions commercially. With VC11, you're allowed to use it just like you'd use the final version. But it *is* not the final version – jalf Jul 14 '12 at 19:03
0

I haven't had any problems with threads in the VS11 dev preview. The following works for me. I must be using slightly different build settings because I had to remove the references to _TCHAR. If it's not a new bug since the dev preview then maybe messing around with the build settings will help. The scratch project I'm using should mostly be the default. The only thing I remember change was manually disabling and deleting the precompiled header stuff, because unchecking that check box in the project creation wizard never does anything.

#include <iostream>
#include <thread>
#include <vector>

#include <Windows.h>

int main(int argc, char* argv[])
{
 std::vector<std::thread*> threads;
 for(int i = 0; i < 10; i++)
 {
   threads.push_back(new std::thread([i]
     {
       std::cout << "thread " << i << std::endl;
       /* whatever else that is thread safe, or even an empty lambda */
     }));
 }

 for(int i = 0; i < 10; i++)
 {
   threads[i]->join();
   delete threads[i];
 }

 return 0;
}

Also you don't need pointers. You can use std::vector<std::thread> (or std::array) because std::thread is movable.

std::array<std::thread,10> threads;
for(int i = 0; i < threads.size(); i++)
    threads[i] = std::thread([i] { std::cout << "thread " << i << std::endl; });

for(int i = 0; i < 10; i++)
    threads[i].join();
bames53
  • 86,085
  • 15
  • 179
  • 244