Hi all,
Boost.Container has needed separate compilation for about a decade due to two
small utilities. The extended allocators (the dlmalloc-derived heap) needed it
since late 2013, and the polymorphic memory resources (PMR) since they were
added in 2015. On the develop branch for 1.93 I've found a way to make it
header-only again, because I think it greatly improves the usability of the library.
Since this might cause some problems to users, I'd like to explain a bit how
this was done in case some problems are detected and users need some hints to
debug it.
The need for a compiled library came from globals that were needed for two reasons:
* The dlmalloc heap used by the extended allocators. This includes its arena,
data structures, mutex, and tuning parameters. Memory allocated through one
module (DLL) must be freeable through another (across DLLs).
* The polymorphic memory resource singletons. This includes the default-resource
slot that set_default_resource() writes and get_default_resource() reads and the
global new_delete_resource of null_memory_resource.
A compiled library provides this automatically (when properly compiled an
linked/loaded), but a header-only library has some problems. Every DLL (on
Windows) compiles its own copy. boost::container::pmr::vector allocated in the
DLL and destroyed would return its memory to the wrong heap.
In Boost 1.93, a private new utility (detail/intermodule_globals.hpp) provides a
class (dtl::intermodule_globals<T, Options>) that returns a reference to a
single object of type T for each T/Options pair.
The object is shared by every module in the process. It is constructed on
zero-initialized storage by whichever module reaches it first with some
configurable options (like a phoenix singleton for those globals that need to be
available to the very end of the program, like a heap allocator). The dlmalloc
globals and the PMR globals are the only two users inside the library.
On ELF and Mach-O the global object is implemented as a static data member of a
class template, with default visibility (so that the loader unifies all the
instances from each .so and .elf).
On windows and Cygwin, since PE/COFF has no equivalent symbol merging, a shared
memory segment is created where a incremental (linear) hash map is built to find
singletons by name and those singleton objects are also constructed in that
shared memory segmented (accesible by all the DLLs that need access to globals).
Boost.Interprocess has a similar utility but it relies on non-trivial
Interprocess data structures), it's design is not optimal and I didn't want to
make Boost.Container dependant on Boost.Interprocess (that would be a circular
dependency). Probably Interprocess will use a similar utility for its
module-shared globals in the future if the Boost.Container transition goes
reasonably well.
Some limitations:
* On ELF, STB_GNU_UNIQUE makes the shared object that defines the symbol
non-unloadable (e.g. to avoid crashes when calling virtual functions that live
on that module).
* On windows a similar mechanism (module pinning) is used to avoid unloading the
module if the corresponding option is used.
* The Windows registry is a fixed-size arena. It is sized (the minimum shared
memory is 64KB) for far more singletons than the library uses, so there is some
memory waste (in practice, only the virtual address space is reserved, only
really used memory is committed).
I hope most existing code should continue to work.
* BOOST_CONTAINER_DECL still exists and expands to nothing.
* BOOST_CONTAINER_DYN_LINK, BOOST_CONTAINER_STATIC_LINK,
BOOST_CONTAINER_SOURCE and BOOST_CONTAINER_NO_LIB are still accepted.
* BOOST_CONTAINER_HEADER_ONLY has been removed, both as a CMake option and
as a macro.
I did not add a stub library, not sure how useful is to have it, I think other
Boost compiled to header transitions did not add the stub library (Boost.system,
on the other hand, provided one for a long time).
Some superproject changes will be needed due to this change, Peter Dimov has
already identified some changes. If some other Boost libraries or tests are
broken due to this header-only transition, let me know so I can help solving them.
Thanks,
Ion