Boost logo

Boost :

Subject: Re: [boost] [Boost.utility]
From: Matthew Herrmann (matthew.herrmann_at_[hidden])
Date: 2010-01-27 05:06:17


On 27/01/2010, at 8:44 AM, Frank Mori Hess wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On Tuesday 26 January 2010, GMan wrote:
>> Take your best singleton class, but remove the bit where it intrudes and
>> forces only a single instantiation. Call this "global", which provides the
>> same thing as a singleton but allows other instantiations of the class. My
>> argument was that this class is more worth boost's time than the singleton
>> class. The reason being, users of global could always re-introduce the
>> single-instantiation behavior, but don't have to. This provides more
>> flexibility.
>
> If you peel off the singleton aspect of singletons, you're left with global
> state and lazy initialization. A facility that supports thread-safe lazy
> initialization might be useful, but why tie it to global state (which is of
> more dubious value)?

Sometimes the globalness and the lazy initialization are inextricably tied together. Though see rarely any benefit in enforced singletons. If it really is a singleton, then lock a global resource, like a file on an in-memory filesystem (like /dev/shm/clunky_database_lock). Much more robust and conceptually simple. (Singletons can be defeated just by running two copies of a program.)

A practically useful (ie very low overhead, safe and uncomplicated) singleton design I've used is the following:

struct X
{
};

/** Get the single instance of x */
inline X& get_x()
{
    static X* x = NULL;
    if ( unlikely(!x) ) x = new X();
    return *x;
}

// ensure the object has been initialized before main was called
namespace {
struct X_maker
{
     X_maker() { get_x(); }
};
X_maker X_maker_instance;
}

It has the following benefits:

- No race conditions (see limitations though)
- Lock-free, inlined, optimizable code (no memory fences or volatiles)
- The code is easy to understand
- Solves the static initialization order fiasco
- No hairy destruction order problems

It has the following limitations:

- You need the OS to reclaim your object's resources on app close, the destructor is not called.
- Do not spawn any threads before main, or you will lose the thread safety.

Luckily, my operating system does close file descriptors, sockets, and deallocates memory, so in the real world, this works very well indeed.

I never have any evil static destruction races where a static object logs a message to a singleton logger that has been already destroyed by a "too-clever-by-half" singleton implementation, causing unreproducible segfaults.

My 5c,
Matthew


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