I have an application that loads a DLL with a class that handles jobs in a queue. In order to keep it thread safe, a mutex is locked whenever the queue is modified. When the application exits and the destructor is called, the mutex is locked in order to clear the queue.
However, when I load this DLL in Python, create an instance of the object, and call exit()
(in Python) an exception is thrown when the mutex tries to lock:
Microsoft Visual Studio C Runtime Library has detected a fatal error in python.exe.
I have simplified the destructor down to just creating a mutex locally and trying to lock it, and can still reproduce the issue:
QueueHandler::~QueueHandler(void)
{
mutex mut; // in reality, this is a member of the class and there are actual operations between lock and unlock
mut.lock(); // exception here
mut.unlock();
}
If I take my unmodified code and simply remove the lock around the queue operation, it works fine.
Here is the seemingly relevant section of the call stack:
KernelBase.dll!RaiseException() Unknown
msvcr120.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Line 154 C++
msvcr120.dll!Concurrency::details::SchedulerBase::SchedulerBase(const Concurrency::SchedulerPolicy & policy) Line 149 C++
msvcr120.dll!Concurrency::details:: SchedulerBase::CreateWithoutInitializing(const Concurrency::SchedulerPolicy & policy) Line 285 C++
msvcr120.dll!Concurrency::details:: SchedulerBase::CreateContextFromDefaultScheduler() Line 571 C++
msvcr120.dll!Concurrency::details::SchedulerBase::CurrentContext() Line 404 C++
[Inline Frame] msvcr120.dll!Concurrency::details::LockQueueNode::{ctor (unsigned int) Line 619 C++
msvcr120.dll!Concurrency::critical_section::lock() Line 1031 C++
msvcp120.dll!mtx_do_lock(_Mtx_internal_imp_t * * mtx, const xtime * target) Line 67 C++
--> MyApplication.dll!QueueHandler::~QueueHandler() Line 106 C++
MyApplication.dll!_CRT_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 416 C
MyApplication.dll!__DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 522 C
ntdll.dll!LdrShutdownProcess() Unknown
ntdll.dll!RtlExitUserProcess() Unknown
msvcr100.dll!doexit(int code, int quick, int retcaller) Line 621 C
python27.dll!000000001e13be65() Unknown
...
python27.dll!000000001e043494() Unknown
python.exe!000000001d00119e() Unknown
Questions:
- If this code works when I exit my app normally (close the GUI), why would it be different when I
exit()
from Pyton? - Is there a "more correct" way to exit from Python?
- Could this be related to the type of mutex/lock being used?
- Do I even need to lock the section with the queue operations in my destructor? Or is it okay to delete the objects in the queue without a lock?
Edit: MCVE: QueueHandlerApp - Run the app or run script.py to demonstrate the issue.