First up, apologies if this is the wrong board but it seemed the closest fit.
I'm using MSVC 2008 Express with the NI DAQmx ANSI C library to perform some analogue output using a USB-6009. I am creating a thread to handle the generation of the signal as it is on a fixed timebase. My main thread runs the user interface. I have found that I get intermittent deadlocks in release mode while the NI libraries are being loaded (in the second thread) and I use some MSVCRT functions (on the main thread). My test code is attached as "deadlock2.cpp".
I have used WinDbg to try to find the cause of the deadlock. The stack traces of my two threads are attached as t1_stack.txt and t2_stack.txt.
It appears that the localtime() function from the MSVCRT uses a lock when it is called for the first time and then goes to lock the Windows DLL loader lock. At the same time the NI libraries (or at least mxs libraries) are being loaded so the DLL loader lock is being held. The mxsutils library uses getcwd() which appears to try to lock something in the MSVCRT and so my two threads are now deadlocked.
I can probably work around this by calling the localtime() and the NI DAQmx functions before I spawn my second thread so that the DLLs are already loaded into the process. However, I have no guarantees of this continuing to work if anything changes in the future, and if there are any locks going on then having the DllMain() called for a thread attach may still cause a deadlock. If my findings are correct is it likely that NI would fix the DLLs to not try to do anything complex in their DllMain()?
According to NI Measurement and Automation Explorer I'm running v9.3.5f2 of the DAQmx libraries. I'm downloading the latest version now to try but it will take some time.
Thank you for the detailed and complete bug report. I was able to reproduce the problem with NI-DAQmx 9.6 using the code you posted. I reported it to the appropriate R&D team as CAR #366538.
I agree with your analysis of the problem. Both getcwd() and the first call to __tzset() acquire _ENV_LOCK, which Microsoft has documented as the "lock for environment variables". __tzset() also calls GetTimeZoneInformation(), which may load additional libraries, as you have shown.
Here are a couple more potential workarounds, but they're not great:
Link to the static version of the CRT (/MT instead of /MD). This would link a separate copy of the CRT lock table into your program, and DAQmx would continue to use MSVCR90.dll's lock table. They would no longer use the same _ENV_LOCK.
Build using a different version of Microsoft Visual C++ (e.g. 2005 or 2010). MSVCR80.dll, MSVCR90.dll, and MSVCR100.dll own separate copies of the CRT lock table.
Either way, it doesn't eliminate the fact that mxsutils calls into the CRT while holding the loader lock, and that CRT function acquires another lock.