Boost logo

Boost :

From: Peter Dimov (pdimov_at_[hidden])
Date: 2001-09-25 10:11:41


A simple assignment from Threading 101: implementing a thread-safe
singleton.

Assume that the single-threaded case looks like this:

class X
{
public:
    static X & get();
};

X & X::get()
{
    static X x;
    return x;
}

POSIX implementation:

static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

X & X::get()
{
    pthread_mutex_lock(&m);
    static X x;
    pthread_mutex_unlock(&m);
    return x;
}

Naive translation to Boost.Threads:

static boost::mutex m;

X & X::get()
{
    boost::mutex::scoped_lock lock(m);
    static X x;
    return x;
}

This has a subtle bug. The static boost::mutex may not have been initialized
yet - the initialization order is undefined. The usual idiom for tackling
this problem is the singleton pattern, which is exactly what we're trying to
build!

Perhaps the alternative POSIX implementation will give us some ideas:

static pthread_once_t m_once = PTHREAD_ONCE_INIT;
static pthread_mutex_t m;

void m_init()
{
    pthread_mutex_init(&m, NULL);
}

X & X::get()
{
    pthread_once(&m_once, m_init);
    pthread_mutex_lock(&m);
    static X x;
    pthread_mutex_unlock(&m);
    return x;
}

Here's a translation:

static boost::once_flag m_once = boost::once_init;
static boost::mutex * pm = 0; // can't use auto_ptr due to dynamic init
order

void m_init()
{
    pm = new boost::mutex;
}

X & X::get()
{
    boost::call_once(&m_once, m_init);
    boost::mutex::scoped_lock lock(*pm);
    static X x;
    return x;
}

This has an even subtler bug. (IMO - I'm not an expert at threading!) No
mutex protects pm, so according to POSIX memory visibility rules a thread
can pass through call_once, see that another thread has already executed
m_once, and fetch the old value of pm, i.e. zero.

Comments? Is the flaw in Boost.Threads or in my limited understanding?

--
Peter Dimov
Multi Media Ltd.

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk