May 01, 2007
Developing Lightweight, Statically Initializable C++ MutexesThreadsafe initialization of SingletonsVladimir Kliatchko
The novel synchronization mechanism Vladimir presents here comes in handy for a wide range of applications.
Vladimir is a senior architect and head of application infrastructure at Bloomberg LP. He can be contacted at vkliatchko@bloomberg.net.
While working on a solution for a fairly narrow and specific problem, I discovered a new synchronization mechanism. This mechanism not only provided a novel and effective solution to my problem, but turns out to have a number of properties making it useful for a wide range of important applications.
My original problem had to do with the implementation of Singletons. The issues with Singletons involve the order and time of their initialization and destruction, as well as the thread safety of these operations. Despite the multitude of techniques developed to deal with these issues, there does not seem to be one universal solutionlet alone a simple one.
The particular Singleton I was working on had to be located in a low-level library, which was used by many applications and other libraries. This constraint ruled out the use of a C++ file-scope variable as a mechanism for initializing the Singleton because there is no way to ensure that initialization of such an object is done properly before its first use. To avoid the active collaboration that would result from explicit initialization, I concluded that the Singleton had to be initialized on first access. The Meyers Singleton pattern (Listing One) fits this requirement. When this pattern is employed, the Singleton is initialized the first time it is used, and automatically destroyed when the process terminates. A word of caution: It is sometimes necessary not only to ensure that a Singleton is destroyed at process termination, but also to establish a particular relative order; for my Singleton, such was not the case.
class Singleton {
Singleton();
~Singleton();
public:
static Singleton& instance();
};
Listing One
This implementation of a Singleton leaves the issue of thread safety unresolved. If multiple threads enter the instance routine simultaneously and if this routine has never been executed before, various problems may occur, depending on the compiler and platform. For example, the constructor of my Singleton may be executed multiple times or not at all. Alternatively, the constructor may be executed exactly once, which is correct, but one of the instance routines may return before the construction is completedagain, leading to unpredictable and hard-to-debug failures. As you'd expect, you can eliminate this race condition by using one of a number of various synchronization mechanisms. Listing Two, for example, employs a mutex. In Listing Two, I did not show how the mutex variable is initialized. The mutex itself is a Singleton! If this mutex has a nontrivial constructor or destructor, we have a chicken-and-egg problem.
class Singleton {
Singleton();
~Singleton();
static Singleton *s_singleton;
static void init();
public:
static Singleton& instance();
};
Singleton *Singleton::s_singleton = 0;
void Singleton::init() {
static Singleton singleton;
s_singleton = &singleton;
}
Singleton& Singleton::instance() {
lock(&mutex);
if (!s_singleton) {
init();
}
unlock(&mutex);
return *s_singleton;
}
Listing Two
|
|
||||||||||||||||||||||||||||||
|
|
|
|