call_once, headers and shared libraries

This came up in the floating point utilities review without a good answer, but it's a general problem that's in need of one, so let me try again.... Suppose we have some global data that needs to be initialised, the obvious answer is to use boost::call_once to ensure that it gets initialised exactly once. But... what happens if this is used in a header to initialise some static variable inside a template instance, and that template is used in more than one shared library/dll? At least on Windows I'm assuming that each dll gets it's own instance of the template, and we end up with two different versions of the static data with unpleasant results. Other than "don't do that", does anyone have a design or usage pattern that sidesteps this issue? This isn't a hypothetical question either, Spirit is using call once like this inside object_with_id_base<TagT, IdT>::acquire_object_id and perhaps elsewhere as well. It's also a fundamental requirement for custom iostream manipulators to be able to call std::ios_base::xalloc exactly once and store the returned ID for future reference by all subsequent calls, regardless of which shared library or dll they may be in. Incidentally both Boost.Fusion and Boost.Tuples call std::ios_base::xalloc without a call_once guard (so are not thread safe?), and presumably have the multiple-instantiations in different modules problem as well. Looks for a cunning answer yours, John.

Hi John,
De: boost-bounces@lists.boost.org [boost-bounces@lists.boost.org] En nombre de John Maddock [john@johnmaddock.co.uk] Enviado el: domingo, 02 de marzo de 2008 14:12 Para: Boost mailing list; rade@maths.lth.se Asunto: [boost] call_once, headers and shared libraries
This came up in the floating point utilities review without a good answer, but it's a general problem that's in need of one, so let me try again....
Suppose we have some global data that needs to be initialised, the obvious answer is to use boost::call_once to ensure that it gets initialised exactly once. But... what happens if this is used in a header to initialise some static variable inside a template instance, and that template is used in more than one shared library/dll? At least on Windows I'm assuming that each dll gets it's own instance of the template, and we end up with two different versions of the static data with unpleasant results. Other than "don't do that", does anyone have a design or usage pattern that sidesteps this issue?
Boost.Flyweight's intemodule_holder is designed to solve precisely this issue. More info on this component at: http://svn.boost.org/svn/boost/sandbox/flyweight/libs/flyweight/doc/tutorial... Component code: http://svn.boost.org/svn/boost/sandbox/flyweight/boost/flyweight/intermodule... svn.boost.org seems to be down at the moment. You can download a Boost.Flyweight package from http://www.boost-consulting.com/vault/index.php?&direction=0&order=&directory=Patterns Best, Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

I discussed Joaquin's solution with John, in a private mail. John told me that Joaquin's solution is sound, but depends on Boost.Interprocess. It is a very clever solution. And it is the first solution I have ever seen to this problem. But to me it seems undesirable to have a light-weight library, such as the floating point utilities, depend on a heavy-weight library, such as Boost.Interprocess. So I think that I'll refrain from adding manipulators to the facet classes in the floating point utilities library. It is a bit alarming that other Boost libraries have defined manipulators using methods that will fail when passing iostream objects between different DLL's on Windows. --Johan JOAQUIN M. LOPEZ MUÑOZ wrote:
Hi John,
De: boost-bounces@lists.boost.org [boost-bounces@lists.boost.org] En nombre de John Maddock [john@johnmaddock.co.uk] Enviado el: domingo, 02 de marzo de 2008 14:12 Para: Boost mailing list; rade@maths.lth.se Asunto: [boost] call_once, headers and shared libraries
This came up in the floating point utilities review without a good answer, but it's a general problem that's in need of one, so let me try again....
Suppose we have some global data that needs to be initialised, the obvious answer is to use boost::call_once to ensure that it gets initialised exactly once. But... what happens if this is used in a header to initialise some static variable inside a template instance, and that template is used in more than one shared library/dll? At least on Windows I'm assuming that each dll gets it's own instance of the template, and we end up with two different versions of the static data with unpleasant results. Other than "don't do that", does anyone have a design or usage pattern that sidesteps this issue?
Boost.Flyweight's intemodule_holder is designed to solve precisely this issue. More info on this component at:
http://svn.boost.org/svn/boost/sandbox/flyweight/libs/flyweight/doc/tutorial...
Component code:
http://svn.boost.org/svn/boost/sandbox/flyweight/boost/flyweight/intermodule...
svn.boost.org seems to be down at the moment. You can download a Boost.Flyweight package from
http://www.boost-consulting.com/vault/index.php?&direction=0&order=&directory=Patterns
Best,
Joaquín M López Muñoz Telefónica, Investigación y Desarrollo _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Johan Råde wrote:
I discussed Joaquin's solution with John, in a private mail. John told me that Joaquin's solution is sound, but depends on Boost.Interprocess. It is a very clever solution. And it is the first solution I have ever seen to this problem.
But to me it seems undesirable to have a light-weight library, such as the floating point utilities, depend on a heavy-weight library, such as Boost.Interprocess.
Sounds like this component should extracted from Interprocess, if possible, and made available to libraries as a standalone component. According to your description, many libraries are in need of using it to avoid subtle bugs. Sebastian Redl

On 02/03/2008, John Maddock <john@johnmaddock.co.uk> wrote:
This came up in the floating point utilities review without a good answer, but it's a general problem that's in need of one, so let me try again....
Suppose we have some global data that needs to be initialised, the obvious answer is to use boost::call_once to ensure that it gets initialised exactly once. But... what happens if this is used in a header to initialise some static variable inside a template instance, and that template is used in more than one shared library/dll? At least on Windows I'm assuming that each dll gets it's own instance of the template, and we end up with two different versions of the static data with unpleasant results. Other than "don't do that", does anyone have a design or usage pattern that sidesteps this issue?
This isn't a hypothetical question either, Spirit is using call once like this inside object_with_id_base<TagT, IdT>::acquire_object_id and perhaps elsewhere as well.
It's also a fundamental requirement for custom iostream manipulators to be able to call std::ios_base::xalloc exactly once and store the returned ID for future reference by all subsequent calls, regardless of which shared library or dll they may be in. Incidentally both Boost.Fusion and Boost.Tuples call std::ios_base::xalloc without a call_once guard (so are not thread safe?), and presumably have the multiple-instantiations in different modules problem as well.
Looks for a cunning answer yours, John.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
vc 7.1 and above offers __declspec(selectany) This used by Atl to ensure only one Atl module gets created I believe. Not used it myself though... -- The truth is out there. Usually in header files.

Simon Hammett wrote:
vc 7.1 and above offers __declspec(selectany) This used by Atl to ensure only one Atl module gets created I believe. Not used it myself though...
According to the docs this just creates the object in a COMDAT section - that will merge multiple instances in one module, but not across modules as far as I know? John.

On 03/03/2008, John Maddock <john@johnmaddock.co.uk> wrote:
Simon Hammett wrote:
vc 7.1 and above offers __declspec(selectany) This used by Atl to ensure only one Atl module gets created I believe. Not used it myself though...
According to the docs this just creates the object in a COMDAT section - that will merge multiple instances in one module, but not across modules as far as I know?
Yup. Just had a quick play and it's useless as far as dlls go. :( I knew there was a reason I stick with static libs...
participants (5)
-
JOAQUIN M. LOPEZ MUÑOZ
-
Johan Råde
-
John Maddock
-
Sebastian Redl
-
Simon Hammett