Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unhandled exception when using std::mutex instead of boost::mutex

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?

like image 903
Robert Hegner Avatar asked Jan 06 '15 14:01

Robert Hegner


2 Answers

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!
}
like image 183
Constantin Galbenu Avatar answered Oct 23 '22 16:10

Constantin Galbenu


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.

like image 25
Chris Desjardins Avatar answered Oct 23 '22 15:10

Chris Desjardins