Fwd: [Thread] Solution to conflict with MFC?

Hi Thread maintainers, Anthony speaks about a patch to make this work. Does anyone know where it is? And could it be applied? Thanks -Thorsten -------- Original Meddelelse -------- Subject: [Thread] Solution to conflict with MFC? Date: Tue, 26 Apr 2011 13:28:56 +0200 From: Christian Larsen <contact@dword.dk> Reply-To: boost-users@lists.boost.org To: boost-users@lists.boost.org Newsgroups: gmane.comp.lib.boost.user Hello, I know this has been asked before, but I could not find a good solution. I'm experiencing problems with an application using both MFC and Boost.Thread. Specifically the assertion ASSERT(AfxGetModuleState() != AfxGetAppModuleState()); in AfxCoreInitModule() fails. I link Boost.Thread statically into a DLL in my project. From previous posts to this list and elsewhere, e.g. http://lists.boost.org/boost-users/2009/04/46929.php, I see that the issue is caused by setting _pRawDllMain in tss_pe.cpp. Commenting this line does "solve" the problem, but I would rather not have to hack the Boost source code this way. It seems there is a patch somewhere for this problem (referred to in http://lists.boost.org/boost-users/2009/04/47015.php), but it appears it never made it into the source. Is there any chance of this issue being solved in a better way? Best regards, Christian Larsen

Thorsten Ottosen <thorsten.ottosen@dezide.com> writes:
Anthony speaks about a patch to make this work. Does anyone know where it is? And could it be applied?
It is more of a hack than a patch: remove the use of _pRawDllMain from tss_pe.cpp, and add a new object file outside the boost thread library that contains a _pRawDllMain that calls both the MFC and Boost initialization functions. The user must explicitly add that object file to their project. Anthony -- Author of C++ Concurrency in Action http://www.stdthread.co.uk/book/ just::thread C++0x thread library http://www.stdthread.co.uk Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976

Den 27-04-2011 17:13, Anthony Williams skrev:
Thorsten Ottosen<thorsten.ottosen@dezide.com> writes:
It is more of a hack than a patch: remove the use of _pRawDllMain from tss_pe.cpp, and add a new object file outside the boost thread library that contains a _pRawDllMain that calls both the MFC and Boost initialization functions. The user must explicitly add that object file to their project.
Hm. That does sound like a hack. But at least it doesn't require users to modify boost source code whenever they upgrade boost. Is there any possibility that the initialization of this pointer in tss_pe.cpp can be made dynamic based on some parameter passed to the code? It would be really cool if we could just say #define BOOST_THREAD_DISABLED_DLL_CALLBACK 1 #include <boost/thread.hpp> in the code that needs it. AFAICR, we can have global data in headers as long as it is inside some template. -Thorsten

Thorsten Ottosen <thorsten.ottosen@dezide.com> writes:
Den 27-04-2011 17:13, Anthony Williams skrev:
Thorsten Ottosen<thorsten.ottosen@dezide.com> writes:
It is more of a hack than a patch: remove the use of _pRawDllMain from tss_pe.cpp, and add a new object file outside the boost thread library that contains a _pRawDllMain that calls both the MFC and Boost initialization functions. The user must explicitly add that object file to their project.
Hm. That does sound like a hack. But at least it doesn't require users to modify boost source code whenever they upgrade boost.
Is there any possibility that the initialization of this pointer in tss_pe.cpp can be made dynamic based on some parameter passed to the code? It would be really cool if we could just say
#define BOOST_THREAD_DISABLED_DLL_CALLBACK 1 #include <boost/thread.hpp>
in the code that needs it. AFAICR, we can have global data in headers as long as it is inside some template.
For the DLL callback to be called automatically, there must be an extern "C" global variable called _pRawDllMain, which must be statically initialized to point to the callback function. I don't see how you could put that in a header without risking multiple definition errors --- the header that initialized it would have to be included exactly once across the whole project. Failure to include it anywhere would mean that the init function would not be linked. I haven't tried it, but I think that if the application defines boost::tss_cleanup_implemented() then tss_pe.cpp will not get linked from the library, so the DLL callback will not be linked, and the MFC one should therefore work. Of course, you will then need to ensure that boost::on_thread_exit() is called when every thread exits to avoid leaking memory and/or resources. Anthony -- Author of C++ Concurrency in Action http://www.stdthread.co.uk/book/ just::thread C++0x thread library http://www.stdthread.co.uk Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976

Den 27-04-2011 18:21, Anthony Williams skrev:
Thorsten Ottosen<thorsten.ottosen@dezide.com> writes:
Den 27-04-2011 17:13, Anthony Williams skrev:
Thorsten Ottosen<thorsten.ottosen@dezide.com> writes:
Is there any possibility that the initialization of this pointer in tss_pe.cpp can be made dynamic based on some parameter passed to the code? It would be really cool if we could just say
#define BOOST_THREAD_DISABLED_DLL_CALLBACK 1 #include<boost/thread.hpp>
in the code that needs it. AFAICR, we can have global data in headers as long as it is inside some template.
For the DLL callback to be called automatically, there must be an extern "C" global variable called _pRawDllMain, which must be statically initialized to point to the callback function.
I don't see how you could put that in a header without risking > multiple definition errors --- the header that initialized it would have to be included exactly once across the whole project. Failure to include it anywhere would mean that the init function would not be linked.
I searched the source directory to locate this variable. Is extern BOOL (WINAPI * const _pRawDllMain)(HANDLE, DWORD,LPVOID); not just a declaration of a variable located somewhere else? If so, we only need to put the initialization of this variable in a header.
I haven't tried it, but I think that if the application defines boost::tss_cleanup_implemented() then tss_pe.cpp will not get linked from the library, so the DLL callback will not be linked, and the MFC one should therefore work. Of course, you will then need to ensure that boost::on_thread_exit() is called when every thread exits to avoid leaking memory and/or resources.
yeah, that won't work well. Then it's far better to include a seperate .cpp file. -Thorsten

Thorsten Ottosen <thorsten.ottosen@dezide.com> writes:
Den 27-04-2011 18:21, Anthony Williams skrev:
Thorsten Ottosen<thorsten.ottosen@dezide.com> writes:
Den 27-04-2011 17:13, Anthony Williams skrev:
Thorsten Ottosen<thorsten.ottosen@dezide.com> writes:
Is there any possibility that the initialization of this pointer in tss_pe.cpp can be made dynamic based on some parameter passed to the code? It would be really cool if we could just say
#define BOOST_THREAD_DISABLED_DLL_CALLBACK 1 #include<boost/thread.hpp>
in the code that needs it. AFAICR, we can have global data in headers as long as it is inside some template.
For the DLL callback to be called automatically, there must be an extern "C" global variable called _pRawDllMain, which must be statically initialized to point to the callback function.
I don't see how you could put that in a header without risking > multiple definition errors --- the header that initialized it would have to be included exactly once across the whole project. Failure to include it anywhere would mean that the init function would not be linked.
I searched the source directory to locate this variable. Is
extern BOOL (WINAPI * const _pRawDllMain)(HANDLE, DWORD,LPVOID);
not just a declaration of a variable located somewhere else?
As you've written it, it would be. In the real code there's actually an initializer: extern BOOL (WINAPI * const _pRawDllMain)(HANDLE, DWORD, LPVOID)=&dll_callback; which means that it is a const variable with external linkage.
If so, we only need to put the initialization of this variable in a header.
But doing so would yield multiple initialization errors.
I haven't tried it, but I think that if the application defines boost::tss_cleanup_implemented() then tss_pe.cpp will not get linked from the library, so the DLL callback will not be linked, and the MFC one should therefore work. Of course, you will then need to ensure that boost::on_thread_exit() is called when every thread exits to avoid leaking memory and/or resources.
yeah, that won't work well. Then it's far better to include a seperate .cpp file.
Anyone care to try the attached file? Anthony -- Author of C++ Concurrency in Action http://www.stdthread.co.uk/book/ just::thread C++0x thread library http://www.stdthread.co.uk Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976

Am 27.04.2011 18:21, schrieb Anthony Williams:
I don't see how you could put that in a header without risking multiple definition errors --- the header that initialized it would have to be included exactly once across the whole project. Failure to include it anywhere would mean that the init function would not be linked.
Would __declspec (selectany) be of any help? Peter

Would __declspec (selectany) be of any help?
From http://msdn.microsoft.com/en-US/library/5tkz6s71%28v=VS.80%29.aspx "A global data item can normally be initialized only once in an EXE or DLL project. selectany can be used in initializing global data defined by headers, when the same header appears in more than one source file. selectany is available in both the C and C++ compilers." I guess this could be exactly what we need. -Thorsten

Hi, In MSVC __declspec (selectany) cannot be used for functions but it's possible to emulate using "__declspec(noinline) inline" (http://blogs.msdn.com/b/freik/archive/2005/10/26/485276.aspx). Seems to produce same mangled name as normal _cdecl. The workaround from Anthony Williams works well to make MFC work with boost::thread but it should be included only once per project. For boost linked dynamically, the following file allows inclusion in any number of files in the project. Actually, I included it in StdAfx.h. Did not give it extensive testing but seems to work both on Windows CE / Windows Mobile and Win32 builds. Comments are welcome. #ifdef _MSC_VER #if !defined(_LIB) && (defined(_AFXEXT) || defined(_USRDLL) || defined(_WINDLL)) // DLLs only #include <boost/thread/thread.hpp> #include <boost/thread/detail/tss_hooks.hpp> // Use custom entry point to call into boost::thread hooks // It's safe to call hooks before global data initialization by CRTL because boost::thread overrides RawDllMain which is also called earlier than CRTL's DLLMain. #pragma comment(linker, "/ENTRY:tps_entry") extern "C" BOOL WINAPI _DllMainCRTStartup( HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved ); // "__declspec(noinline) inline" is used to get __declspec(selectany) flag // because __declspec(selectany) cannot be applied to functions directly // Could use template for that but then mangled name should be designated as entry point extern "C" __declspec(noinline) inline BOOL WINAPI tps_entry( HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved ) { // here define DLL hook (and EXE hook below) switch (dwReason) { case DLL_PROCESS_ATTACH: boost::on_process_enter(); break; case DLL_THREAD_ATTACH: boost::on_thread_exit(); break; case DLL_THREAD_DETACH: boost::on_thread_exit(); break; case DLL_PROCESS_DETACH: boost::on_process_exit(); break; } return _DllMainCRTStartup(hDllHandle, dwReason, lpreserved); } // Special protocol to indicate to boost::thread that we did support custom tss cleanup hooks // Use of "__declspec(noinline) inline" allows multiple definition across the project as results in __declspec(selectany) flag namespace boost { __declspec(noinline) inline void tss_cleanup_implemented() { } } // Just to force inclusion of custom entry point to obj. Template is used to get __declspec(selectany) flag. // Inline functions are not compiled in; even under debug. template <typename> int tps_dummy_reserved() { return &boost::tss_cleanup_implemented && &tps_entry; } template int tps_dummy_reserved<int>(); #endif // DLLs only #endif -- View this message in context: http://boost.2283326.n4.nabble.com/Fwd-Thread-Solution-to-conflict-with-MFC-... Sent from the Boost - Dev mailing list archive at Nabble.com.
participants (5)
-
Anthony Williams
-
Christian Larsen
-
Peter
-
Thorsten Ottosen
-
Vyacheslav Lanovets