...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
For any given T
, the type flyweight<T>
maintains some class-wide or static data that needs to be properly
initialized before the class can be used. The internal machinery of
Boost.Flyweight guarantees that static data initialization
takes place automatically before the first use of the particular
flyweight<T>
instantiation in the program, and in
any case always during the so-called dynamic initialization phase
of the program startup sequence. Although this is not strictly
required by the C++ standard, in current practice dynamic initialization
is completed before main()
begins.
So, for all practical purposes, static data initialization is performed
before main()
or before the first pre-main()
usage of the class, for instance if we declare a global
static flyweight<T>
object. This covers the vast
majority of usage cases in a transparent manner, but there are
some scenarios where the automatic static data initialization
policy of Boost.Flyweight can fail:
// global thread pool class thread_pool { public: thread_pool() { for(int i=0;i<100;++i)p[i]=shared_ptr<thread>(new thread(thread_fun)); } private: static void thread_fun() { // uses flyweight<std::string> } array<shared_ptr<thread>,100> p; }; static thread_pool thpool; int main() { ...
The global pool of the example launches several threads, each of which
internally uses flyweight<std::string>
.
Static data initialization can potentially be executed twice concurrently
if two threads happen to collide on the first usage of
flyweight<std::string>
: Boost.Flyweight initialization
does not consider thread safety. So, we need to explicitly take care of
static data initialization in a thread safe context before launching
the threads:
class thread_pool { public: thread_pool() { flyweight<std::string>::init(); for(int i=0;i<100;++i)p[i]=shared_ptr<thread>(new thread(thread_fun)); } ...
The static member function init
is not thread safe, either: in our particular
example it just happens to be called in a single threaded environment.
When concurrency can happen, flyweight<T>::init
must
be properly synchronized by the programmer by using some mutual exclusion
mechanisms of her own.
The following is another example where the default static initialization provided by Boost.Flyweight can fail:
static std::vector<flyweight<std::string> > v; int main() { // use v }
In some environments, the program above fails at termination time with something like the following:
Assertion failed: count()==0, file c:\boost\flyweight\refcounted.hpp, line 55
What is the problem? Although the type of v
involves
flyweight<std::string>
, constructing v
as an empty vector
need not create any flyweight object proper; so,
it is perfectly possible that the static initialization of
flyweight<std::string>
happens after the construction
of v
; when this is the case, the static destruction of
the associated factory will occur before v
's
destruction, leaving the vector with dangling flyweights.
Again, the solution consists in explicitly forcing the static instantiation
of flyweight<std::string>
before v
is
created. Here, calling
the function flyweight<std::string>::init
is a little
cumbersome, so we can resort to the utility type
flyweight<std::string>::initializer
to do that job for us:
// equivalent to calling flyweight<std::string>::init() static flyweight<std::string>::initializer fwinit; static std::vector<flyweight<std::string> > v; int main() { // use v; no dangling flyweights at termination now }
Revised August 11th 2008
© Copyright 2006-2008 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)