"Joshua Maurice" <> wrote in message
news:11bae170-d413-482a-8dff-...
[...]
> PPS: All windows standard mutexes have runtime init, so you cannot do
> either of these approaches without significant modifications, or
> without rolling your own mutex built on some of the atomic primitives
> like test and swap. See:
> http://www.ddj.com/cpp/199203083?pgno=7
> for a good discussion on how to do this.
> Alternatively, use a namespace scope variable to force construction
> before main (or before dlopen returns for static init of a dll) to
> "guarantee" correctness as demonstrated in countless posts in this
> thread.
Windows has everything one needs in order to create a 100% correct DCL
pattern. The following code will work with Windows, modulo bugs of course
because I just hacked it together:
__________________________________________________ ____________________
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <sstream>
#include <cassert>
#include <exception>
#include <cstdio>
class win_dcl_mutex
{
HANDLE m_mutex;
private:
static std::string prv_get_name()
{
std:

stringstream name;
name << "DCL_MUTEX_" << GetCurrentProcessId();
return name.str();
}
public:
win_dcl_mutex() throw()
: m_mutex(CreateMutex(NULL, TRUE, prv_get_name().c_str()))
{
if (! m_mutex)
{
assert(m_mutex);
std::unexpected();
}
else if (GetLastError() == ERROR_ALREADY_EXISTS)
{
if (WaitForSingleObject(m_mutex, INFINITE) !=
WAIT_OBJECT_0)
{
assert(m_mutex);
CloseHandle(m_mutex);
std::unexpected();
}
}
}
~win_dcl_mutex() throw()
{
if (! ReleaseMutex(m_mutex))
{
assert(m_mutex);
CloseHandle(m_mutex);
std::unexpected();
}
if (! CloseHandle(m_mutex))
{
assert(m_mutex);
std::unexpected();
}
}
};
template<typename T, unsigned T_id = 0>
class win_non_destroying_singleton
{
static T* prv_load(T* volatile* pptr)
{
T* ptr1 = *pptr;
if (! ptr1)
{
ptr1 = (T*)InterlockedCompareExchangePointer(
(void* volatile*)pptr, NULL, NULL);
}
if (ptr1)
{
// does `InterlockedCompareExchange()' peform a
// membar on failure? here is a funny work-around:
T* ptr2 = (T*)InterlockedExchangePointer(
(void* volatile*)pptr, ptr1);
if (ptr1 != ptr2)
{
assert(ptr1 == ptr2);
std::unexpected();
}
}
return ptr1;
}
static T* prv_store(T* volatile* pptr, T* ptr1)
{
assert(ptr1);
T* ptr2 = (T*)InterlockedExchangePointer(
(void* volatile*)pptr, ptr1);
if (ptr2)
{
assert(! ptr2);
std::unexpected();
}
return ptr1;
}
public:
static T& instance()
{
static T* g_instance = NULL;
T* local = prv_load(&g_instance);
if (! local)
{
win_dcl_mutex lock;
if (! (local = g_instance))
{
local = prv_store(&g_instance, new T());
}
}
return *local;
}
};
int
main()
{
{
int& x1 = win_non_destroying_singleton<int>::instance();
int& x2 = win_non_destroying_singleton<int>::instance();
}
return 0;
}
__________________________________________________ ____________________
BTW, I had to code the `win_non_destroying_singleton<T>:

rv_load()'
function that way because Microsoft does not document whether or not
`InterlockedCompareExchange()' performs a memory barrier on the failure
case. If it did, then it could simply look like this:
__________________________________________________ ____________________
static T* prv_load(T* volatile* pptr)
{
return (T*)InterlockedCompareExchangePointer(
(void* volatile*)pptr, NULL, NULL);
}
__________________________________________________ ____________________
Also, if you are using a recent version of MSVC (e.g., I think it's 8 or
higher), then the class can look like:
__________________________________________________ ____________________
template<typename T, unsigned T_id = 0>
struct win_non_destroying_singleton
{
static T& instance()
{
static T* volatile g_instance = NULL;
T* local = g_instance;
if (! local)
{
win_dcl_mutex lock;
if (! (local = g_instance))
{
local = g_instance = new T();
}
}
return *local;
}
};
__________________________________________________ ____________________
This is because those versions of MSVC have acquire/release semantics for
loading/storing from/to volatile variables on certain platforms (e.g.,
PowerPC)...