Boost logo

Boost :

Subject: Re: [boost] Is there interest in an alternative to the Singletonanti-pattern?
From: Gruenke, Matt (mgruenke_at_[hidden])
Date: 2011-06-30 16:40:53


On June 28, 2011 03:29, Ben Robinson wrote:

> I have reimplemented Singularity using the scoped_ptr<T>. After
> further research, I am convinced the volatile keyword was useless,
> given that all accesses to the shared pointer were fenced by
> acquisition of a boost::mutex.

The problem with using shared or scoped pointer and boost::mutex is that
you're depending on order-of-construction. For a singleton to be safely
used pre-main, this is not an option.

I have previously implemented my own generic singleton template, which
depends only on static initializers. Since it didn't need to be
cross-platform, I could take advantage of PTHREAD_MUTEX_INITIALIZER and
employ lazy construction w/ double-checked locking (where 'volatile' can
be useful). You can further optimize double-checked locking (for GCC),
using "__attribute__ ((const,nothrow))" to eliminate some redundant
calls to get the singleton from within the same scope.

A singleton is declared as:

  static singleton<T> Single_inst = SINGLETON_INIT;

Where:

  #define SINGLETON_INIT { NULL, { PTHREAD_MUTEX_INITIALIZER } }

It does have a destructor, which does the same job as
boost::scoped_ptr's.

The singleton template supports two initialization approaches. The
first is roughly:

  T *Get_single() __attribute__ ((const,nothrow))
  {
    T *s = Single_inst.get();
    if (!s) s = Single_inst.init(new T());
    return s;
  }

This assumes you don't mind multiple T's being constructed, in the rare
case that multiple threads are competing for first access
(singleton<T>::init() will reliably delete any superfluous ones).

The second defers creating T until the init lock has been obtained
(which only succeeds for the first caller):

  T *Get_single() __attribute__ ((const,nothrow))
  {
    T *s = Single_inst.get();
    if (!s)
    {
      if (Single_inst.try_init_lock())
      {
        s = Single_inst.init_locked(new T());
      }
      else s = Single_inst.get_blocking();
    }
    return s;
  }

The single<T> member functions should be pretty self-explanatory. The
reason for the second get method is that try_init_lock() can fail if the
init lock is currently held, though the pointer still might not yet be
valid. Therefore, you need a method that reads the value only after
obtaining the mutex. In both of initialization sequences,
singleton<T>::get() simply returns the value of the enclosed pointer.

Obviously, Get_single() can be a static member function of singleton<T>.
In most cases, there's no reason to do otherwise. However, I recommend
at least giving users the option to initialize from the outside, as it
has the advantage of facilitating more elaborate initialization
sequences (e.g. if a few things need to be configured on T, before
passing it to singleton<T>::init_locked(); or if T * is actually
returned by a function, rather than directly constructed).

Cheers,
Matthew A. Gruenke


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