[interprocess][trunk][windows] named_mutex doesn't work whithin a DLL

Environment: MSVC++ 9.0. I create the following DLL: ***bip_dll.cpp*** include <boost/interprocess/sync/named_mutex.hpp> using namespace boost::interprocess; named_mutex mutex(open_or_create,"foo"); __declspec(dllexport) void foo(){} ***bip_dll.cpp*** and use it inside the following executable: ***test.cpp*** __declspec(dllimport) void foo(); int main() { foo(); } ***test.cpp*** The program hangs on DLL loading time when creation of mutex in bip_dll.cpp is attempted. In particular, hanging occurs inside the invocation of get_wmi_class_attribute(strValue, L"Win32_OperatingSystem", L"LastBootUpTime"); in line 1554 of boost/interprocess/detail/win32_api.hpp, when control hits lines 1479 and ff: if( 0 != pIWbemLocator->ConnectServer( bstrNamespace, // Namespace 0, // Userid 0, // PW 0, // Locale 0, // flags 0, // Authority 0, // Context &pWbemServices ) ) { ... This is related to ticket https://svn.boost.org/trac/boost/ticket/4606 . Thank you, Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

El 06/09/2010 17:11, Joaquin M Lopez Munoz escribió:
The program hangs on DLL loading time when creation of mutex in bip_dll.cpp is attempted. In particular, hanging occurs inside the invocation of
get_wmi_class_attribute(strValue, L"Win32_OperatingSystem", L"LastBootUpTime");
The problem is that I don't have any clue on how to solve this. I've googled around and there seems to be similar cases, but no explanation solution, maybe there are some restrictions when loading a dll?. Just to have a bit more infomration, which Win32 OS are you using, XP, Vista, 7? And we can't even put a decent timeout since according to http://msdn.microsoft.com/en-us/library/aa391769%28VS.85%29.aspx the alternative is to have a timeout of *2 minutes*. I really don't know why thi is happening, but I don't think this has an easy fix. BTW this function was necessary to obtain a bootup time that does not change with hibernation or clock adjusments, previous bootup timestamp function was a huge source of problems for Interprocess users. Best, Ion

El 06/09/2010 19:27, Ion Gaztañaga escribió:
El 06/09/2010 17:11, Joaquin M Lopez Munoz escribió:
The program hangs on DLL loading time when creation of mutex in bip_dll.cpp is attempted.
You get the same problem if the call is made inside foo? (just to discard dll loading issues. Best, Ion

Ion Gaztañaga <igaztanaga <at> gmail.com> writes:
El 06/09/2010 19:27, Ion Gaztañaga escribió:
El 06/09/2010 17:11, Joaquin M Lopez Munoz escribió:
The program hangs on DLL loading time when creation of mutex in bip_dll.cpp is attempted.
You get the same problem if the call is made inside foo? (just to discard dll loading issues.
If the call is made inside foo, things work just fine. It is definitely an issue with using IWbemLocator::ConnectServer on DLL loading time. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

Ion Gaztañaga <igaztanaga <at> gmail.com> writes:
El 06/09/2010 17:11, Joaquin M Lopez Munoz escribió:
The program hangs on DLL loading time when creation of mutex in bip_dll.cpp is attempted. In particular, hanging occurs inside the invocation of
get_wmi_class_attribute(strValue, L"Win32_OperatingSystem", L"LastBootUpTime");
The problem is that I don't have any clue on how to solve this. I've googled around and there seems to be similar cases, but no explanation solution, maybe there are some restrictions when loading a dll?. Just to have a bit more infomration, which Win32 OS are you using, XP, Vista, 7?
System is: Microsoft Windows XP Media Center Edition Version 2002 SP 2 There's some evidence on the net (Google for it) that IWbemLocator::ConnectServer hangs when used on DLL loading time, though no definite confirmation or available workarounds. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

El 06/09/2010 19:55, Joaquin M Lopez Munoz escribió:
System is:
Microsoft Windows XP Media Center Edition Version 2002 SP 2
There's some evidence on the net (Google for it) that IWbemLocator::ConnectServer hangs when used on DLL loading time, though no definite confirmation or available workarounds.
Thanks, I plan to implement mutex using native CreateMutex soon but this problem will appear then if we try to use shared memory in DLL loading (and this includes managed_shared_memory in Windows!). I'm afraid I have no easy fix for this, because I have no other way to obtain a reliable bootup timestamp (which I need to obtain kernel lifetime for shared memory). I think you could (until we fix this) use native windows mutexes (and maybe windows_managed_shared_memory) in intermodule_holder. There is another advantage: the mutex/shared memory will cleanup automatically on process exit or crash. Best, Ion

On 6 sep 2010, at 19.27, Ion Gaztañaga wrote:
The problem is that I don't have any clue on how to solve this. I've googled around and there seems to be similar cases, but no explanation solution, maybe there are some restrictions when loading a dll?. Just to have a bit more infomration, which Win32 OS are you using, XP, Vista, 7?
I believe this is due to the DLL loader lock mechanism. Basically, you are not supposed to call anything that might (implicitly) load another DLL from the DllMain function. Google for "dll loader lock". The first link, "Best practices for creating DLLs" is a Word document describing the problem in detail. -- /Johan.

On 09/06/2010 02:41 PM, Johan Lindvall wrote:
On 6 sep 2010, at 19.27, Ion Gaztañaga wrote:
The problem is that I don't have any clue on how to solve this. I've googled around and there seems to be similar cases, but no explanation solution, maybe there are some restrictions when loading a dll?. Just to have a bit more infomration, which Win32 OS are you using, XP, Vista, 7?
I believe this is due to the DLL loader lock mechanism.
Basically, you are not supposed to call anything that might (implicitly) load another DLL from the DllMain function.
Google for "dll loader lock". The first link, "Best practices for creating DLLs" is a Word document describing the problem in detail.
While there are very few things you can safely do while under the loader lock on Windows, one of the things you can do is (believe it or not) create a new thread. But you can't wait on it to finish, of course. So a workaround I've used in the past for DLLs that really needed nontrivial startup code was to create a separate thread to run it. Before starting the thread, acquire a reference on the DLL via LoadLibrary. This prevents the DLL from being unloaded while the thread is running code in it. Somehow arrange for that reference to be released after the startup thread terminates, or it may be acceptable in specific cases to just never unload (e.g. the DLL is known to be a link-time dependency of the EXE). - Marsh

Hi Joaquin, I've been working on a boost::flyweights::intermodule_holder_class-like intermodule_singleton class. It's in early design state. This class is doing some lazy variable and shared memory initialization, so maybe it can be useful to implement intermodule_holder. It's in trunk, in boost/interprocess/detail/intermodule_singleton.hpp. It's not optimized for windows (it uses file locks and kernel lifetime shared memory, emulating a unix process, it would be much more lightweight using native windows process-lifetime resources) but I think you should try it. I've not tested in DLL-s. You have a test (intermodule_singleton.cpp) in boost/interprocess/test that shows some use cases. Best, Ion

Ion Gaztañaga <igaztanaga <at> gmail.com> writes:
Hi Joaquin,
I've been working on a boost::flyweights::intermodule_holder_class-like intermodule_singleton class. It's in early design state. This class is doing some lazy variable and shared memory initialization, so maybe it can be useful to implement intermodule_holder. It's in trunk, in boost/interprocess/detail/intermodule_singleton.hpp. It's not optimized for windows (it uses file locks and kernel lifetime shared memory, emulating a unix process, it would be much more lightweight using native windows process-lifetime resources) but I think you should try it. I've not tested in DLL-s.
You have a test (intermodule_singleton.cpp) in boost/interprocess/test that shows some use cases.
Hi Ion, I've tried replacing my intermodule_holder code with a simple wrapper around boost::interprocess:detail::intermodule_singleton and unfortunately the problem persists, hitting at the very same line of code with IWbemLocator::ConnectServer. Setting LazyInit to either true or false does nor make a difference. Call stack follows:
intermod_holder_dll.dll!boost::interprocess::winapi:: get_wmi_class_attribute(std::basic_string<wchar_t, std::char_traits<wchar_t>,std::allocator<wchar_t>
& strValue="", const wchar_t * wmi_class=0x100537a8, const wchar_t * wmi_class_var=0x100537dc) Line 1488 C++ intermod_holder_dll.dll!boost::interprocess::winapi:: get_last_bootup_time(std::basic_string<wchar_t,std:: char_traits<wchar_t>,std::allocator<wchar_t> & strValue="") Line 1554 + 0x13 bytes C++
intermod_holder_dll.dll!boost::interprocess::winapi:: get_last_bootup_time(std::basic_string<char,std:: char_traits<char>,std::allocator<char>
& str="") Line 1562 + 0x9 bytes C++
intermod_holder_dll.dll!boost::interprocess::detail:: get_bootstamp(std::basic_string<char,std::char_traits< char>,std::allocator<char>
& s="C:\Documents and Settings\All Users\Application Data/boost_interprocess/", bool add=true) Line 41 + 0x9 bytes C++
intermod_holder_dll.dll!boost::interprocess::detail:: tmp_folder(std::basic_string<char,std::char_traits< char>,std::allocator<char>
& tmp_name="C:\Documents and Settings\All Users\Application Data/boost_interprocess/") Line 108 + 0xb bytes C++
intermod_holder_dll.dll!boost::interprocess::detail:: intermodule_singleton_helpers::remove_old_gmem() Line 162 + 0x9 bytes C++ intermod_holder_dll.dll!boost::interprocess::detail:: intermodule_singleton_common<0>::initialize_shm() Line 733 C++ intermod_holder_dll.dll!boost::interprocess::detail:: intermodule_singleton_common<0>::initialize_singleton_logic( void * & ptr=0x00000000, volatile unsigned int & this_module_singleton_initialized=1, void * (boost::interprocess::basic_managed_shared_memory<char, boost::interprocess::rbtree_best_fit<boost::interprocess:: mutex_family,boost::interprocess::offset_ptr<void>,0>, boost::interprocess::iset_index> &)* constructor=0x1001b72f) Line 832 C++ intermod_holder_dll.dll!boost::interprocess::detail:: intermodule_singleton<boost::flyweights::detail:: flyweight_core<boost::flyweights::detail:: default_value_policy<std::basic_string<char,std:: char_traits<char>,std::allocator<char>
,boost::mpl::na,boost::flyweights::refcounted,boost::
flyweights::hashed_factory<boost::mpl::na,boost::mpl::na, boost::mpl::na,0>,boost::flyweights::simple_locking, boost::flyweights::intermodule_holder>::holder_arg,1>::get() Line 894 + 0x14 bytes C++ intermod_holder_dll.dll!boost::flyweights::detail:: flyweight_core<boost::flyweights::detail:: default_value_policy<std::basic_string<char,std:: char_traits<char>,std::allocator<char>
,boost::mpl::na,boost::flyweights::refcounted,boost::
flyweights::hashed_factory<boost::mpl::na,boost::mpl::na, boost::mpl::na,0>,boost::flyweights::simple_locking, boost::flyweights::intermodule_holder>::init() Line 111 + 0x5 bytes C++ intermod_holder_dll.dll!`dynamic initializer for 'boost::flyweights::detail::flyweight_core<boost::flyweights:: detail::default_value_policy<std::basic_string<char,std:: char_traits<char>,std::allocator<char>
,boost::mpl::na,boost::flyweights::refcounted,boost::
flyweights::hashed_factory<boost::mpl::na,boost::mpl::na, boost::mpl::na,0>,boost::flyweights::simple_locking, boost::flyweights::intermodule_holder>::static_initializer''() Line 218 + 0x23 bytes C++ msvcr80d.dll!1020235a() [Frames below may be incorrect and/or missing, no symbols loaded for msvcr80d.dll] intermod_holder_dll.dll!_CRT_INIT(void * hDllHandle=0x10000000, unsigned long dwReason=1, void * lpreserved=0x0013fd30) Line 313 + 0xf bytes C intermod_holder_dll.dll!__DllMainCRTStartup(void * hDllHandle=0x10000000, unsigned long dwReason=1, void * lpreserved=0x0013fd30) Line 489 + 0x11 bytes C intermod_holder_dll.dll!_DllMainCRTStartup(void * hDllHandle=0x10000000, unsigned long dwReason=1, void * lpreserved=0x0013fd30) Line 459 + 0x11 bytes C ntdll.dll!7c90118a() ntdll.dll!7c91cb82() ntdll.dll!7c90d80a() ntdll.dll!7c923460() ntdll.dll!7c921874() ntdll.dll!7c913f89() ntdll.dll!7c92176f() ntdll.dll!7c919358() ntdll.dll!7c922dfe() ntdll.dll!7c90e457() Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

The lazy init does not work because somehow get() is being called in a static object (static_initializer()->init()), so it's lazily but *too early* instantiated. If intermodule_singleton is used as a singleton outside static classes in dlls, then I guess there is no problem. In the future I'll change intermodule_singleton to use windows_managed_shared_memory so that we can avoid any call to COM objects. According to microsoft documentation, we shouldn't even call C runtime memory management functions in DllMain, which is really frightening. And in Windows 2000 we can't even create a named pipe or other named object (so we can't create named shared memory). If you need an static object to use a intermodule_singleton, then we can replace the static object with another intermodule_singleton. Best, Ion

Ion Gaztañaga escribió:
The lazy init does not work because somehow get() is being called in a static object (static_initializer()->init()), so it's lazily but *too early* instantiated.
This is aspect of Boost.Flyweight is by design, instantiation of the singleton class is made during the program dynamic initialization phase so as to avoid concurrency problems to a large extent (see http://tinyurl.com/38jknra ).
If intermodule_singleton is used as a singleton outside static classes in dlls, then I guess there is no problem.
No solution for my problem :-(
In the future I'll change intermodule_singleton to use windows_managed_shared_memory so that we can avoid any call to COM objects.
That'd be terrific. Do you have a roadmap for this?
According to microsoft documentation, we shouldn't even call C runtime memory management functions in DllMain, which is really frightening.
I've always found this particular restriction a little hard to justify (and really never saw a crash attributable to this): in the doc you refere to it reads: "The library loader. DLLs often have complex interdependencies that implicitly define the order in which they must be loaded. The library loader efficiently analyzes these dependencies, calculates the correct load order, and loads the DLLs in that order." and also "You should never perform the following tasks from within DllMain: [...] 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." Supposedly the library loader analysis should lead in every case to CRT DLL (which does not depend on any user-created DLL) being loaded *before* any user-created DLL, shouldn't it? Why then the caveat? Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

El 10/09/2010 8:13, joaquin@tid.es escribió:
In the future I'll change intermodule_singleton to use windows_managed_shared_memory so that we can avoid any call to COM objects.
I thought it was no urgent, but I've just changed my mind ;-) Let's see if I can push it in a month.
Supposedly the library loader analysis should lead in every case to CRT DLL (which does not depend on any user-created DLL) being loaded *before* any user-created DLL, shouldn't it? Why then the caveat?
Yes, it should. I don't think CRT is a problem in practice. Otherwise I guess millions of C++ programs compiled into dlls wouldn't work. Thanks for the report, Ion

Ion Gaztañaga escribió:
El 10/09/2010 8:13, joaquin@tid.es escribió:
In the future I'll change intermodule_singleton to use windows_managed_shared_memory so that we can avoid any call to COM objects.
I thought it was no urgent, but I've just changed my mind ;-) Let's see if I can push it in a month.
That's fantastic. Thank you for the effort, and for the wonderful Boost.Interprocess. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

El 10/09/2010 8:13, joaquin@tid.es escribió:
Ion Gaztañaga escribió:
The lazy init does not work because somehow get() is being called in a static object (static_initializer()->init()), so it's lazily but *too early* instantiated.
This is aspect of Boost.Flyweight is by design, instantiation of the singleton class is made during the program dynamic initialization phase so as to avoid concurrency problems to a large extent (see http://tinyurl.com/38jknra ).
Threading shouldn't be a problem, because lazy initialization is serialized through mutexes (even the non-lazy initialization, because non-lazy is just using lazy initialization in a static object). Two threads calling get() shouldn't lead to a race condition when obtaining a pointer to the singleton. Further concurrent usage should be of course protected by the user. Best, Ion

Ion Gaztañaga escribió:
El 10/09/2010 8:13, joaquin@tid.es escribió:
Ion Gaztañaga escribió:
The lazy init does not work because somehow get() is being called in a static object (static_initializer()->init()), so it's lazily but *too early* instantiated.
This is aspect of Boost.Flyweight is by design, instantiation of the singleton class is made during the program dynamic initialization phase so as to avoid concurrency problems to a large extent (see http://tinyurl.com/38jknra ).
Threading shouldn't be a problem, because lazy initialization is serialized through mutexes (even the non-lazy initialization, because non-lazy is just using lazy initialization in a static object). Two threads calling get() shouldn't lead to a race condition when obtaining a pointer to the singleton. Further concurrent usage should be of course protected by the user.
I understand this would allow me to avoid program dynamic initialization in the particular case of intermodule_holder, but Boost.Flyweight handles other types of holders which are not synchronized, so I cannot adop this on a general basis :-) Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
participants (5)
-
Ion Gaztañaga
-
Joaquin M Lopez Munoz
-
joaquin@tid.es
-
Johan Lindvall
-
Marsh Ray