The C++ implementations for most multi-threaded platforms have a fundamental flaw. Static storage duration variables with function scope are not constructed in a thread-safe manner.
Consider the following code:
struct T { T() ; } ; void f() { static T t ; }
In most C++ implementations, the compiler emits object code that, on
execution of the definition of the variable t
, tests a
secret compiler-generated flag in the static initialised data area of
the program to see whether it is zero, and if it is zero constructs
t
and sets the flag to another value. The intention is
that since the flag is initialised to zero, the first time that
execution passes through the definition of t
that variable
is constructed and the flag is modified to prevent construction
happening a second time.
This is the classic simplistic implementation of the Singleton Pattern.
One does not have to know too much about multiple-threaded programming
to realise that such code is not thread safe at all. It is wrong for a
C++ compiler generating a multiple-threaded program to emit such
thread-unsafe code. The compiler is creating a program that will not
work properly. If two threads of execution were to enter the function
f()
concurrently, a wide range of undesirable effects could
occur, from t
being constructed twice to t
being used by one thread before it has been fully constructed by the
other.
Watcom C++, IBM VisualAge C++, Microsoft Visual C++, MetaWare High C++, EMX C++, GNU C++, and Borland C++ all use this mechanism. (These are the only C++ compilers I have checked. I have not checked Comeau C++, Digital Mars C++, or any other C++ compilers, however there is good reason to suspect that they, too, erroneously use this same incorrect mechanism when compiling multiple-threaded programs, given how widespread its use is.) These implementations of the C++ language are all poor quality in this respect. They all purport to be able to generate multiple-threaded programs, yet the behind-the-scenes code that they generate is not actually thread safe.
POSIX standards provide the pthread_once()
function (also
documented in version 2 of the Single Unix Specification) precisely
so that programmers can implement the Singleton Pattern correctly.
Behind-the-scenes code generated by C++ compilers that employs the
Singleton Pattern should use it, too. The code that C++ compilers
should emit would
pthread_once_t
object as the
secret flag; and
pthread_once()
, passing as the callback
function a pointer to the code that calls the constructor for
t
.
Platforms that do not implement the C language bindings to the POSIX
threads API would, of course, have to come up with their own
pthread_once_t
data type and work out their own
interlocking mechanism appropriate for the platform.
(They must ensure that they do not copy
Intel's daft mistake of mandating the incorrect and flawed
"Double-Checked Locking" pattern,
a well known anti-pattern that simply won't work, no matter how much
one tweaks it.)
But the overall solution remains the same.
Several C++ implementations provide programmers with options to specify whether multiple-threaded programs are being compiled. For example:
Such implementations can of course switch between using (the internals
of) pthread_once()
and using the thread-unsafe mechanism
described earlier, according to whether the programmer has specified
that he/she is compiling a multiple-threaded or a single-threaded
program.
If you have one of the C++ implementations mentioned, or if you discover that your C++ implementation also has this flaw, contact the manufacturer and ask for it to be corrected, so that in multiple-threaded programs the code that the compiler generates to initialise function scope variables with static storage duration is thread safe.
If you are a manufacturer of such a C++ compiler, please fix your compiler.