Intel® oneAPI Threading Building Blocks
Ask questions and share information about adding parallelism to your applications when using this threading library.
2463 Discussions

Feature request / bugfix: intel scheduler loader-lock in dllmain



I'm using the TBB scheduler by way of the graph flow api. The code i'm using makes native -> managed calls. Normally this isn't an issue, except in certain circumstances which include doing so from a DLLMain. Unfortunately it appears the tbb scheduler is invoked from DLLMain, which trips loader lock warnings. It's possible it's not catastrophic and generally ignorable, but there is a small chance of a deadlock depending on what the code does.

I don't see any way to work around the issue besides ignoring the locker lock warnings, which isn't optimal. Here's a stack trace for example (with code above it that calls a Continue Node process function, which in turn does a native -> managed call):

     tbb_debug.dll!tbb::internal::custom_scheduler<tbb::internal::IntelSchedulerTraits>::local_wait_for_all(tbb::task & parent, tbb::task * child) Line 517    C++
     tbb_debug.dll!tbb::internal::generic_scheduler::cleanup_master(bool blocking_terminate) Line 1317    C++
     tbb_debug.dll!tbb::internal::governor::auto_terminate(void * arg) Line 222    C++
     tbb_debug.dll!tbb::internal::governor::terminate_auto_initialized_scheduler() Line 139    C++
     tbb_debug.dll!DllMain(void * __formal, unsigned long reason, void * lpvReserved) Line 264    C++

There's even an Intel blog post talking about why this is a bad practice:


Specifically, it lists these unsafe operations from a DLLMain, which would apply in this case from the TBB Scheduler (and makes it a larger issue than just calling into managed code):

Call LoadLibrary or LoadLibraryEx (either directly or indirectly). This can cause a deadlock or a crash.

Call GetStringTypeA, GetStringTypeEx, or GetStringTypeW (either directly or indirectly). This can cause a deadlock or a crash.

Synchronize with other threads. This can cause a deadlock.

Acquire a synchronization object that is owned by code that is waiting to acquire the loader lock. This can cause a deadlock.

Initialize COM threads by using CoInitializeEx Under certain conditions, this function can call LoadLibraryEx.

Call the registry functions. These functions are implemented in Advapi32.dll. If Advapi32.dll is not initialized before your DLL, the DLL can access uninitialized memory and cause the process to crash.

Call CreateProcess. Creating a process can load another DLL.

Call ExitThread. Exiting a thread during DLL detach can cause the loader lock to be acquired again, causing a deadlock or a crash.

Call CreateThread. Creating a thread can work if you do not synchronize with other threads, but it is risky.

Create a named pipe or other named object (Windows 2000 only). In Windows 2000, named objects are provided by the Terminal Services DLL. If this DLL is not initialized, calls to the DLL can cause the process to crash.

Use the memory management function from the dynamic C Run-Time (CRT). If the CRT DLL is not initialized, calls to these functions can cause the process to crash.

Call functions in User32.dll or Gdi32.dll. Some functions load another DLL, which may not be initialized.

Use managed code.


Does anyone know of a workaround to make the scheduler not invoke from DLLMain? Even a simple thread spinup for the stack starting at terminate_auto_initialized_scheduler() would solve any possible issues with this, though i realize that may be possible with TBB's internal architecture.

Thanks in advance.

0 Kudos
0 Replies