Unhandled exception when using std::mutex instead of boost::mutex
Asked Answered
P

4

11

I try to get rid of some of the boost dependencies in my code and instead use the new C++11 features (Visual Studio 2013).

In one of my components I used boost::mutex together with boost::lock_guard<boost::mutex> and everything worked fine. When I use std::mutex together with std::lock_guard<std::mutex> instead, I get the following error when returning from main().

Unhandled exception at 0x7721E3BE (ntdll.dll) in GrabberTester.exe: 0xC0000005: Access violation reading location 0xA6A6B491.

The real project is quite complex and it's therefore difficult to provide a full working code example to reproduce this problem. In my real project the mutexes are used in a shared library which is loaded at runtime (but which should already be unloaded by the time I'm returning from main()).

My questions are:

  • Are boost::mutex and std::mutex designed to behave absolutely the same?
  • If not, what are the differences? What do I need to keep in mind when using std::mutex instead of boost::mutex?
  • In the shared library I'm creating threads with the boost::thread framework. Could it be that std::mutex can only be used with std::threads and is incompatible with boost::threads?

Edit:

One more thing I noticed: When I unload the dynamically loaded shared library this takes some time. (The DLL accesses hardware and it takes some time to shut everything down cleanly). When I switch to std::mutex however it looks like the DLL can be unloaded almost immediately, but the program then crashes when returning from main(). I have the impression that the problem with std::mutex is specifically in the context of a DLL.

Edit 2:

Both the application and the DLL are freshly built in Debug configuration with the v120 toolset and statically linked with the runtime library (/MTd).

Edit 3:

Below you can find the callstack. The exception seems to come from somewhere in the driver. Only by accident I figured out that it has to do with which implementation of mutex I use.

ntdll.dll!7721e3be()
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
ntdll.dll!7721e023()
kernel32.dll!76b014ad()
msvcr100.dll!71b0016a()
PS1080.dll!oniDriverDestroy() Line 29
OpenNI2.dll!oni::implementation::DeviceDriver::~DeviceDriver() Line 95
OpenNI2.dll!oni::implementation::Context::shutdown() Line 324
OpenNi2Grabber.dll!openni::OpenNI::shutdown() Line 2108
OpenNi2Grabber.dll!GrabberSingletonImpl::~GrabberSingletonImpl() Line 46
OpenNi2Grabber.dll!`GrabberSingletonImpl::getInstance'::`2'::`dynamic atexit destructor for 'inst''()
OpenNi2Grabber.dll!doexit(int code, int quick, int retcaller) Line 628
OpenNi2Grabber.dll!_cexit() Line 448
OpenNi2Grabber.dll!_CRT_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 169
OpenNi2Grabber.dll!__DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 399
OpenNi2Grabber.dll!_DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 340
ntdll.dll!7722b990()
ntdll.dll!77249bad()
ntdll.dll!77249a4f()
kernel32.dll!76b079ed()
GrabberTester.exe!__crtExitProcess(int status) Line 776
GrabberTester.exe!doexit(int code, int quick, int retcaller) Line 678
GrabberTester.exe!exit(int code) Line 417
GrabberTester.exe!__tmainCRTStartup() Line 264
GrabberTester.exe!mainCRTStartup() Line 165
kernel32.dll!76b0338a()
ntdll.dll!7722bf32()
ntdll.dll!7722bf05()

Edit 4:

Maybe this is a bug in the OpenNI2 SDK which can be observed only under these very specific conditions. So I added the openni tag to this question. But still the question remains: why does it work with boost::mutex but not with std::mutex?

Paediatrician answered 6/1, 2015 at 14:9 Comment(7)
As far as I am away there is no significant differences between stl and boost implementations. The only reason I've ever seen a mutex do this is when one thread locks and another attempts to unlock. I would make sure I'm using lock_guards for all instances possible and investigate all other uses with lots of synchronised debug output.Synopsis
I know that some of the VS2013 implementations of the standard threading facilities were buggy in various aspects. No idea if mutex is part of that though.Keikokeil
Thanks ChrisWard1000 for your input. I'm locking/unlocking the mutex only by using lock_guards, so I should be safe in that regard.Paediatrician
Any chance you're somehow mixing different runtime versions?Burleigh
or didn't do a complete rebuild? (i.e. some object files, DLLs and whatnot still using old code)Coniology
Post at least the stack trace of the crash.Wedlock
Thanks for your comments. I updated the answer to provide the additional information.Paediatrician
C
5

The problem most likely is static init hell, I recently went through almost this same thing. Here is what is going down:

  1. You have a static mutex (could be a member of a class that is static).
  2. The doexit() code starts cleaning up your static stuff.
  3. The mutex is destroyed somewhere in doexit()
  4. Something uses the mutex after it has been destroyed, often times in a destructor.

The problem is that you don't really know the order of destruction for static objects. So if you have:

static std::mutex staticMutex;

void someFunction()
{
    std::unique_lock<std::mutex> lock(staticMutex);
    doSomethingAwesome();
}

....

StaticObjA::~StaticObjA()
{
    someFunction();
}

Then your static mutex CAN already be deleted/destroyed/deadbeef when ~StaticObjA() is invoked. The problem is exacerbated when the objects are defined in different compilation units (i.e. defined in different files).

My recommendation for solving is to try to reduce your dependence on static objects, you could try to have 1 static object that takes care of construction/destruction of everything else, so that you can control the order of events. Or just don't use statics at all.

Centigram answered 3/10, 2015 at 14:44 Comment(1)
"The problem is exacerbated when the objects are defined in different compilation units" The problem shouldn't even exist unless they are defined in different compilation units. The initialization order within a single translation unit is well-defined. Also, the constructor for std::mutex is constexpr, so a global std::mutex should be initialized before any global objects that require dynamic initialization, and therefore should be destroyed after those objects. I don't think Microsoft's implementation uses constexpr and so doesn't meet that requirement.Nara
G
3

I had a similar problem when my code attempted to lock twice the same mutex: a function acquired the lock then called another function that was trying to acquire a lock on the same global/static mutex.

mutex queueMutex;

void f1()
{
    lock_guard<mutex> guard(queueMutex);
    f2();
}

void f2()
{
    lock_guard<mutex> guard(queueMutex); //unhandled exception!
}
Gratuitous answered 6/6, 2016 at 7:35 Comment(3)
I think this is simply bad design, and not a problem with std::mutexJeddy
I agree with you, @JeddyGratuitous
use recursive_mutex in such scenarioCeyx
C
0

I add the same problem and what solved is ... a full manual cleanup + rebuild ! Verify the deletion of all .obj,.dll,.lib files.

Coign answered 16/2, 2015 at 15:44 Comment(1)
You were lucky. It didn't help me at least.Deepdyed
A
0

Do not use std::mutex (or recursive_mutex, ...) from Mircosoft!!!! I had similar problems. I'm using VS2012.

If you use std::mutex in a dll, you must not unload this dll. Because you get a undefined behavior. (in my case 0xC0000005: Access violation reading location ....). Or you can't unload the dll. ( may "?runtime?" increments load counter. Double FreeLibrary() unloads the dll )

Argentinaargentine answered 28/1, 2016 at 13:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.