
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