[thread] 1.41.0 regression

Hi, The thread_data_base constructor is defined in a header file. Function make_external_thread_data() is defined in a .cpp file. This can lead to a situation when different libraries contain the definitions of these functions. The example below explains how this can happen and why this causes problems. The new version of struct thread_data_base contains an std::map member. The size of an std::map object depends on if libboost_thread.so was build with or w/o _GLIBCXX_DEBUG. libboost_thread.so contains make_external_thread_data(). This function allocates an instance of thread_data_base. Due to an std::map member the size of the allocated object depends on _GLIBCXX_DEBUG. There is a library (e.g. libuser.so) which uses the Boost.Thread library. Since the thread_data_base constructor is defined in a header file libuser.so can contain its definition. Again, the size of thread_data_base object, that the thread_data_base constructor from libuser.so expects, depends on if libuser.so was built with _GLIBCXX_DEBUG defined or not. An application cannot link agains both libboost_thread.so and libuser.so if these libraries were built with different _GLIBCXX_DEBUG definitions (or different alignment, etc). While loading the application the dynamic linker will use the definition of thread_data_base constructor either from libuser.so be or from libboost_thread.so. Whichever it finds first. Can both the thread_data_base constructor and make_external_thread_data() reside either in a .cpp file or in a header? BR, Dmitry

Dmitry Goncharov <dgoncharov@unison.com> writes:
The thread_data_base constructor is defined in a header file. Function make_external_thread_data() is defined in a .cpp file. This can lead to a situation when different libraries contain the definitions of these functions. The example below explains how this can happen and why this causes problems.
It always causes problems if your program has two incompatible definitions of the same structure. It's a violation of the One Definition Rule.
There is a library (e.g. libuser.so) which uses the Boost.Thread library. Since the thread_data_base constructor is defined in a header file libuser.so can contain its definition. Again, the size of thread_data_base object, that the thread_data_base constructor from libuser.so expects, depends on if libuser.so was built with _GLIBCXX_DEBUG defined or not.
An application cannot link agains both libboost_thread.so and libuser.so if these libraries were built with different _GLIBCXX_DEBUG definitions (or different alignment, etc). While loading the application the dynamic linker will use the definition of thread_data_base constructor either from libuser.so be or from libboost_thread.so. Whichever it finds first.
Can both the thread_data_base constructor and make_external_thread_data() reside either in a .cpp file or in a header?
That won't make any difference. All the thread structures that are defined in header files must have the same layout in the app as in the thread library, otherwise you will either get linker errors or (worse) runtime errors due to incorrect structure offsets. The solution is to use the same compiler flags when you build your application as when you build the boost thread library, to ensure that the structures have the same layout. 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

Anthony Williams wrote:
That won't make any difference. All the thread structures that are defined in header files must have the same layout in the app as in the thread library, otherwise you will either get linker errors or (worse) runtime errors due to incorrect structure offsets.
Agree.
The solution is to use the same compiler flags when you build your application as when you build the boost thread library, to ensure that the structures have the same layout.
The same flags, etc are not feasible. Many users have their boost libraries prebuilt by the distromakers. When someone distributes a library which uses Boost.Thread the users have to build it with the same flags the Boost.Thread library was built. They don't know which flags Boost.Thread was built with. Having both the thread_data_base constructor and make_external_thread_data() reside either in a .cpp file or in a header eliminates the issue. Are there reasons not to? BR, Dmitry

Dmitry Goncharov <dgoncharov@unison.com> writes:
Anthony Williams wrote:
That won't make any difference. All the thread structures that are defined in header files must have the same layout in the app as in the thread library, otherwise you will either get linker errors or (worse) runtime errors due to incorrect structure offsets.
Agree.
The solution is to use the same compiler flags when you build your application as when you build the boost thread library, to ensure that the structures have the same layout.
The same flags, etc are not feasible. Many users have their boost libraries prebuilt by the distromakers. When someone distributes a library which uses Boost.Thread the users have to build it with the same flags the Boost.Thread library was built. They don't know which flags Boost.Thread was built with.
Having both the thread_data_base constructor and make_external_thread_data() reside either in a .cpp file or in a header eliminates the issue. Are there reasons not to?
This doesn't eliminate the issue, it just masks it. If the definition of std::map varies depending on compiler settings then this will affect more than just the constructor of thread_data_base. It will affect all uses of thread_data_base, such as the derived classes created when a boost::thread object is constructed to start a thread: the debug version of std::map has additional members, so the layout of the derived classes will be different depending on the setting of the macro. If you wish to use the _GLIBCXX_DEBUG macro then you need to ensure that your entire project, including any shared libraries that use Standard C++ Library classes in their interface (such as boost.thread), are built using that flag. Yes, that means you have to compile boost.thread from source in this case, and can't use the system supplied one, but the same applies to other shared libraries. 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
participants (2)
-
Anthony Williams
-
Dmitry Goncharov