From e99e352e2c3edf30a5b8b9ce8b57b8904c186c09 Mon Sep 17 00:00:00 2001 From: Jeff Hill Date: Wed, 19 Nov 2008 17:20:15 +0000 Subject: [PATCH] redesigned this facility for simplicity, clarity, and robustness --- src/libCom/cxxTemplates/epicsSingleton.h | 248 +++++++++++------- .../cxxTemplates/epicsSingletonMutex.cpp | 69 +++-- 2 files changed, 190 insertions(+), 127 deletions(-) diff --git a/src/libCom/cxxTemplates/epicsSingleton.h b/src/libCom/cxxTemplates/epicsSingleton.h index 372a372cb..81f3fdf38 100644 --- a/src/libCom/cxxTemplates/epicsSingleton.h +++ b/src/libCom/cxxTemplates/epicsSingleton.h @@ -21,123 +21,189 @@ #include #include "shareLib.h" -#include "epicsMutex.h" -#include "epicsGuard.h" -#include "epicsThread.h" #include "epicsAssert.h" -#include "compilerDependencies.h" + +class epicsShareClass SingletonUntyped { +public: + SingletonUntyped (); + ~SingletonUntyped (); + typedef void * ( * PBuild ) (); + void incrRefCount ( PBuild ); + typedef void ( * PDestroy ) ( void * ); + void decrRefCount ( PDestroy ); + void * pInstance () const; +private: + void * _pInstance; + size_t _refCount; + SingletonUntyped ( const SingletonUntyped & ); + SingletonUntyped & operator = ( const SingletonUntyped & ); +}; // This class exists for the purpose of avoiding file scope // object chicken and egg problems. It implements thread safe // lazy initialization. To avoid locking overhead retain a -// copy of the epicsSingleton::reference for future use. The -// class referenced by epicsSingleton::reference is _not_ -// destroyed by ~epicsSingleton() because this would introduce -// additional file scope chicken and egg problems. +// copy of the epicsSingleton :: reference for future use. template < class TYPE > class epicsSingleton { public: - epicsSingleton (); - ~epicsSingleton (); - - // inline mf def for class within a template required by visual c++ 7 class reference { public: - reference ( TYPE & tIn ): - instance ( tIn ) - { - } - - ~reference () - { - } - - TYPE * operator -> () - { - return & this->instance; - } - - const TYPE * operator -> () const - { - typename epicsSingleton::reference & ref = - const_cast < typename epicsSingleton::reference & > ( *this ); - return ref.operator -> (); - } - - TYPE & operator * () - { - return * this->operator -> (); - } - - const TYPE & operator * () const - { - return * this->operator -> (); - } - - private: - TYPE & instance; + reference ( epicsSingleton & ); + reference ( const reference & ); + ~reference (); reference & operator = ( const reference & ); + TYPE * operator -> (); + const TYPE * operator -> () const; + TYPE & operator * (); + const TYPE & operator * () const; + private: + epicsSingleton * _pSingleton; + reference (); // disabled }; - - // lock overhead every time these are called - typename epicsSingleton::reference getReference (); - const typename epicsSingleton::reference getReference () const; - + epicsSingleton () {} + // mutex lock/unlock pair overhead incured + // when either of these are called + reference getReference (); + const reference getReference () const; private: - TYPE * pSingleton; + SingletonUntyped _singletonUntyped; + static void * _build (); + static void _destroy ( void * ); epicsSingleton ( const epicsSingleton & ); epicsSingleton & operator = ( const epicsSingleton & ); }; template < class TYPE > -inline epicsSingleton::epicsSingleton () : - pSingleton ( 0 ) +inline epicsSingleton < TYPE > :: reference :: + reference ( epicsSingleton & es ): + _pSingleton ( & es ) { + es._singletonUntyped. + incrRefCount ( & epicsSingleton < TYPE > :: _build ); } template < class TYPE > -inline epicsSingleton::~epicsSingleton () +inline epicsSingleton < TYPE > :: reference :: + reference ( const reference & ref ) : + _pSingleton ( ref._pSingleton ) { - // Deleting the singelton in the destructor causes problems when - // there are file scope objects that reference the singleton in - // their destructors. Since this class's purpose is to avoid these - // sorts of problems then clean up is left to other classes. -} - -// SUN PRO generates warnings unless it sees an implementation -#if defined ( __SUNPRO_CC ) && __SUNPRO_CC < 0x550 - // SUN PRO 5.4 generates bogus warnings unless it sees an implementation - template < class TYPE > - inline epicsSingleton::epicsSingleton ( - const epicsSingleton & ) - { - assert ( 0 ); - } - template < class TYPE > - inline epicsSingleton & epicsSingleton::operator = - ( const epicsSingleton & ) - { - assert ( 0 ); - } -#endif - -epicsShareFunc epicsMutex & epicsSingletonPrivateMutex (); - -// borland 5.5 is unable to build this function optimized if it is inline -template < class TYPE > -typename epicsSingleton::reference epicsSingleton::getReference () -{ - { - epicsGuard < epicsMutex > guard ( epicsSingletonPrivateMutex() ); - if ( ! this->pSingleton ) { - this->pSingleton = new TYPE; - } - } - return reference ( * this->pSingleton ); + assert ( _pSingleton ); + _pSingleton->_singletonUntyped. + incrRefCount ( & epicsSingleton < TYPE > :: _build ); } template < class TYPE > -inline const typename epicsSingleton::reference epicsSingleton::getReference () const +inline epicsSingleton < TYPE > :: reference :: + ~reference () +{ + assert ( _pSingleton ); + _pSingleton->_singletonUntyped. + decrRefCount ( & epicsSingleton < TYPE > :: _destroy ); +} + +template < class TYPE > +typename epicsSingleton < TYPE > :: reference & + epicsSingleton < TYPE > :: reference :: + operator = ( const reference & ref ) +{ + if ( _pSingleton != ref._pSingleton ) { + assert ( _pSingleton ); + _pSingleton->_singletonUntyped. + decrRefCount ( epicsSingleton < TYPE > :: _destroy ); + _pSingleton = ref._pSingleton; + assert ( _pSingleton ); + _pSingleton->_singletonUntyped. + incrRefCount ( & epicsSingleton < TYPE > :: _build ); + } + return *this; +} + +template < class TYPE > +inline TYPE * + epicsSingleton < TYPE > :: reference :: + operator -> () +{ + assert ( _pSingleton ); + return reinterpret_cast < TYPE * > + ( _pSingleton->_singletonUntyped.pInstance () ); +} + +template < class TYPE > +inline const TYPE * + epicsSingleton < TYPE > :: reference :: + operator -> () const +{ + assert ( _pSingleton ); + return reinterpret_cast < const TYPE * > + ( _pSingleton->_singletonUntyped.pInstance () ); +} + +template < class TYPE > +inline TYPE & + epicsSingleton < TYPE > :: reference :: + operator * () +{ + return * this->operator -> (); +} + +template < class TYPE > +inline const TYPE & + epicsSingleton < TYPE > :: reference :: + operator * () const +{ + return * this->operator -> (); +} + +inline SingletonUntyped :: SingletonUntyped () : + _pInstance ( 0 ), _refCount ( 0 ) +{ +} + +inline void * SingletonUntyped :: pInstance () const +{ + return _pInstance; +} + +inline SingletonUntyped :: ~SingletonUntyped () +{ + // we dont assert fail on non-zero _refCount + // and or non nill _pInstance here because this + // is designed to tolarate situations where + // file scope epicsSingleton objects (which + // theoretically dont have storage lifespan + // issues) are deleted in a non-determanistic + // order +# if 0 + assert ( _refCount == 0 ); + assert ( _pInstance == 0 ); +# endif +} + +template < class TYPE > +void * epicsSingleton < TYPE > :: _build () +{ + return new TYPE (); +} + +template < class TYPE > +void epicsSingleton < TYPE > :: + _destroy ( void * pDestroyTypeless ) +{ + TYPE * pDestroy = + reinterpret_cast < TYPE * > ( pDestroyTypeless ); + delete pDestroy; +} + +template < class TYPE > +inline typename epicsSingleton < TYPE > :: reference + epicsSingleton < TYPE > :: getReference () +{ + return reference ( * this ); +} + +template < class TYPE > +inline const typename epicsSingleton < TYPE > :: reference + epicsSingleton < TYPE > :: getReference () const { epicsSingleton < TYPE > * pConstCastAway = const_cast < epicsSingleton < TYPE > * > ( this ); diff --git a/src/libCom/cxxTemplates/epicsSingletonMutex.cpp b/src/libCom/cxxTemplates/epicsSingletonMutex.cpp index 3de3e32e6..4d72b2c53 100644 --- a/src/libCom/cxxTemplates/epicsSingletonMutex.cpp +++ b/src/libCom/cxxTemplates/epicsSingletonMutex.cpp @@ -15,52 +15,49 @@ * */ +#include + #define epicsExportSharedSymbols +#include "epicsMutex.h" +#include "epicsGuard.h" +#include "epicsThread.h" #include "epicsSingleton.h" -extern "C" void epicsSingletonMutexOnce ( void * pParm ); +#ifndef SIZE_MAX +# define SIZE_MAX UINT_MAX +#endif -class epicsShareClass epicsSingletonMutex { -public: - epicsSingletonMutex (); - ~epicsSingletonMutex (); - epicsMutex & get (); -private: - epicsThreadOnceId onceFlag; - epicsMutex * pMutex; - static void once ( void * ); - friend void epicsSingletonMutexOnce ( void * pParm ); -}; +static epicsThreadOnceId epicsSigletonOnceFlag ( EPICS_THREAD_ONCE_INIT ); +static epicsMutex * pEPICSSigletonMutex = 0; -epicsSingletonMutex::epicsSingletonMutex () : - onceFlag ( EPICS_THREAD_ONCE_INIT ), pMutex ( 0 ) +extern "C" void SingletonMutexOnce ( void * /* pParm */ ) { + // This class exists for the purpose of avoiding file scope + // object chicken and egg problems. Therefore, pEPICSSigletonMutex + // is never destroyed. + pEPICSSigletonMutex = new epicsMutex; } -epicsSingletonMutex::~epicsSingletonMutex () +void SingletonUntyped :: incrRefCount ( PBuild pBuild ) { - delete this->pMutex; + epicsThreadOnce ( & epicsSigletonOnceFlag, SingletonMutexOnce, 0 ); + epicsGuard < epicsMutex > + guard ( *pEPICSSigletonMutex ); + assert ( _refCount < SIZE_MAX ); + if ( _refCount == 0 ) { + _pInstance = ( * pBuild ) (); + } + _refCount++; } -void epicsSingletonMutex::once ( void * pParm ) +void SingletonUntyped :: decrRefCount ( PDestroy pDestroy ) { - epicsSingletonMutex *pSM = static_cast < epicsSingletonMutex * > ( pParm ); - pSM->pMutex = new epicsMutex; -} - -extern "C" void epicsSingletonMutexOnce ( void * pParm ) -{ - epicsSingletonMutex::once ( pParm ); -} - -epicsMutex & epicsSingletonMutex::get () -{ - epicsThreadOnce ( & this->onceFlag, epicsSingletonMutexOnce, this ); - return * this->pMutex; -} - -epicsMutex & epicsSingletonPrivateMutex () -{ - static epicsSingletonMutex mutex; - return mutex.get (); + assert ( _refCount > 0 ); + epicsGuard < epicsMutex > + guard ( *pEPICSSigletonMutex ); + _refCount--; + if ( _refCount == 0 ) { + ( *pDestroy ) ( _pInstance ); + _pInstance = 0; + } }