You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
714 lines
20 KiB
714 lines
20 KiB
/*
|
|
* S I N G L T O N . H
|
|
*
|
|
* Singleton (per-process global) classes
|
|
*
|
|
* Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
|
|
*/
|
|
|
|
#ifndef _SINGLTON_H_
|
|
#define _SINGLTON_H_
|
|
|
|
#include <caldbg.h>
|
|
|
|
// ========================================================================
|
|
//
|
|
// TEMPLATE CLASS Singleton
|
|
//
|
|
// Use this template to implement classes that can only have one instance.
|
|
// NOTE: For ref-counted or on-demand global objects, see below
|
|
// (RefCountedGlobal and OnDemandGlobal).
|
|
//
|
|
// The Singleton template provides the following:
|
|
//
|
|
// o a common memory layout for singleton classes which
|
|
// allows template folding to reduce overall code size.
|
|
//
|
|
// o an instantiation mechanism that verifies (asserts)
|
|
// that only instance of your class ever exists.
|
|
//
|
|
// o asserts to catch any code which attempts to use
|
|
// your class when it's not initialized.
|
|
//
|
|
// To use this template, declare your class like this:
|
|
//
|
|
// class YourClass : private Singleton<YourClass>
|
|
// {
|
|
// //
|
|
// // Declare Singleton as a friend of YourClass
|
|
// // if YourClass's constructor is private (which it
|
|
// // should be since YourClass is a singleton and
|
|
// // should not be allowed to arbitrarily create
|
|
// // instances of it).
|
|
// //
|
|
// friend class Singleton<YourClass>;
|
|
//
|
|
// //
|
|
// // YourClass private members. Since the 'staticness'
|
|
// // of YourClass is provided entirely by this template,
|
|
// // you do not need to declare your members as 'static'
|
|
// // and you should use standard Hungarian class member
|
|
// // naming conventions (e.g. m_dwShoeSize).
|
|
// //
|
|
// [...]
|
|
//
|
|
// public:
|
|
// //
|
|
// // Public declarations for YourClass. Among these should
|
|
// // be static functions to init and deinit YourClass which
|
|
// // would call CreateInstance() and DestroyInstance()
|
|
// // respectively. Or, you could just expose these functions
|
|
// // directly to clients of YourClass with using declarations:
|
|
// //
|
|
// // using Singleton<YourClass>::CreateInstance;
|
|
// // using Singleton<YourClass>::DestroyInstance;
|
|
// //
|
|
// // Similarly, YourClass will probably have additional
|
|
// // static methods which access or operate on the
|
|
// // singleton instance. These will call Instance()
|
|
// // to get at the global instance. Or, though it's
|
|
// // not recommended from an encapsulation standpoint,
|
|
// // you could expose the global instance directly to
|
|
// // clients with:
|
|
// //
|
|
// // using Singleton<YourClass>::Instance;
|
|
// //
|
|
// [...]
|
|
// };
|
|
//
|
|
template<class _X>
|
|
class Singleton
|
|
{
|
|
//
|
|
// Space for the sole instance
|
|
//
|
|
static BYTE sm_rgbInstance[];
|
|
|
|
//
|
|
// Pointer to the instance
|
|
//
|
|
static _X * sm_pInstance;
|
|
|
|
public:
|
|
// STATICS
|
|
//
|
|
|
|
//
|
|
// Create the single, global instance of class _X.
|
|
//
|
|
static _X& CreateInstance()
|
|
{
|
|
//
|
|
// This actually calls Singleton::new()
|
|
// (defined below), but calls to new()
|
|
// must always be unqualified.
|
|
//
|
|
return *(new _X());
|
|
}
|
|
|
|
//
|
|
// Destroy the single, global instance of class _X.
|
|
//
|
|
static VOID DestroyInstance()
|
|
{
|
|
//
|
|
// This actually calls Singleton::delete()
|
|
// (defined below), but calls to delete()
|
|
// must always be unqualified.
|
|
//
|
|
if (sm_pInstance)
|
|
delete sm_pInstance;
|
|
}
|
|
|
|
//
|
|
// Provide access to the single, global instance of class _X.
|
|
//
|
|
static BOOL FIsInstantiated()
|
|
{
|
|
return !!sm_pInstance;
|
|
}
|
|
|
|
static _X& Instance()
|
|
{
|
|
Assert( sm_pInstance );
|
|
|
|
return *sm_pInstance;
|
|
}
|
|
|
|
//
|
|
// Singleton operator new and operator delete "allocate"
|
|
// space for the object in static memory. These must be
|
|
// defined public for syntactic reasons. Do NOT call them
|
|
// directly!! Use CreateInstance()/DestroyInstance().
|
|
//
|
|
static void * operator new(size_t)
|
|
{
|
|
Assert( !sm_pInstance );
|
|
|
|
//
|
|
// Just return a pointer to the space
|
|
// in which to instantiate the object.
|
|
//
|
|
sm_pInstance = reinterpret_cast<_X *>(sm_rgbInstance);
|
|
return sm_pInstance;
|
|
}
|
|
|
|
static void operator delete(void *, size_t)
|
|
{
|
|
Assert( sm_pInstance );
|
|
|
|
//
|
|
// Since nothing was done to allocate space
|
|
// for the instance, we don't do anything
|
|
// here to free it.
|
|
//
|
|
sm_pInstance = NULL;
|
|
}
|
|
|
|
};
|
|
|
|
//
|
|
// Space for the sole instance of class _X
|
|
//
|
|
template<class _X>
|
|
BYTE Singleton<_X>::sm_rgbInstance[sizeof(_X)] = {0};
|
|
|
|
//
|
|
// Pointer to the instance
|
|
//
|
|
template<class _X>
|
|
_X * Singleton<_X>::sm_pInstance = NULL;
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS _Empty
|
|
//
|
|
// A completely empty, but instantiatable class. Use the _Empty class
|
|
// to get around the syntactic inability to instantiate anything of
|
|
// type void (or VOID).
|
|
//
|
|
// In retail builds, _Empty has the same memory footprint and code
|
|
// impact as void -- none.
|
|
//
|
|
// See the RefCountedGlobal template below for a usage example.
|
|
//
|
|
class _Empty
|
|
{
|
|
// NOT IMPLEMENTED
|
|
//
|
|
_Empty( const _Empty& );
|
|
_Empty& operator=( const _Empty& );
|
|
|
|
public:
|
|
_Empty() {}
|
|
~_Empty() {}
|
|
};
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// TEMPLATE CLASS RefCountedGlobal
|
|
//
|
|
// Use this template as boilerplate for any class that encapsulates
|
|
// a global, refcounted initialization/deinitialization process.
|
|
//
|
|
// This template maintains proper refcounting and synchronization when
|
|
// there are multiple threads trying to initialize and deinitialize
|
|
// references at the same time. And it does so without a critical
|
|
// section.
|
|
//
|
|
// To use this template, declare your class like this:
|
|
//
|
|
// class YourClass : private RefCountedGlobal<YourClass>
|
|
// {
|
|
// //
|
|
// // Declare Singleton and RefCountedGlobal as friends
|
|
// // of YourClass so that they can call your private
|
|
// // initialization functions.
|
|
// //
|
|
// friend class Singleton<YourClass>;
|
|
// friend class RefCountedGlobal<YourClass>;
|
|
//
|
|
// //
|
|
// // Private declarations for YourClass
|
|
// //
|
|
// [...]
|
|
//
|
|
// //
|
|
// // Failable initialization function. This function
|
|
// // should perform any failable initialization of the
|
|
// // instance of YourClass. It should return TRUE
|
|
// // if initialization succeeds, and FALSE otherwise.
|
|
// // If YourClass does not have any initialization that
|
|
// // can fail then you should implement this function inline
|
|
// // to just return TRUE.
|
|
// //
|
|
// BOOL FInit();
|
|
//
|
|
// public:
|
|
// //
|
|
// // Public declarations for YourClass. Among these should
|
|
// // be static functions to init and deinit YourClass. These
|
|
// // functions would call DwInitRef() and DeinitRef() respectively.
|
|
// // Or, you could just expose DwInitRef() and DeinitRef()
|
|
// // directly to clients of YourClass with using declarations:
|
|
// //
|
|
// // using RefCountedGlobal<YourClass>::DwInitRef;
|
|
// // using RefCountedGlobal<YourClass>::DeinitRef;
|
|
// //
|
|
// [...]
|
|
// };
|
|
//
|
|
// If YourClass::FInit() succeeds (returns TRUE), then DwInitRef()
|
|
// returns the new refcount. If YourClass::FInit() fails (returns
|
|
// FALSE), then DwInitRef() returns 0.
|
|
//
|
|
// See \cal\src\inc\memx.h for sample usage.
|
|
//
|
|
// If YourClass::FInit() requires initialization parameters, you can
|
|
// still use the RefCountedGlobal template. You just need to provide
|
|
// your parameter type in the template instantiation and declare your
|
|
// FInit() to take a const reference to a parameter of that type:
|
|
//
|
|
// class YourClass : private RefCountedGlobal<YourClass, YourParameterType>
|
|
// {
|
|
// //
|
|
// // Declare Singleton and RefCountedGlobal as friends
|
|
// // of YourClass so that htey can call your private
|
|
// // initialization functions.
|
|
// //
|
|
// // Note the added parameter type to the RefCountedGlobal
|
|
// // declaration.
|
|
// //
|
|
// friend class Singleton<YourClass>;
|
|
// friend class RefCountedGlobal<YourClass, YourParameterType>;
|
|
//
|
|
// //
|
|
// // Private declarations for YourClass
|
|
// //
|
|
// [...]
|
|
//
|
|
// //
|
|
// // Failable initialization function. This function
|
|
// // now takes a const ref to the initialization parameters.
|
|
// //
|
|
// BOOL FInit( const YourParameterType& initParam );
|
|
//
|
|
// public:
|
|
// //
|
|
// // Public declarations for YourClass
|
|
// //
|
|
// [...]
|
|
// };
|
|
//
|
|
// See \cal\src\httpext\entry.cpp for an example of this usage.
|
|
//
|
|
template<class _X, class _ParmType = _Empty>
|
|
class RefCountedGlobal : private Singleton<_X>
|
|
{
|
|
//
|
|
// The object's reference count.
|
|
//
|
|
static LONG sm_lcRef;
|
|
|
|
//
|
|
// Member template that generates an appropriately-typed,
|
|
// (inline) function that calls _X::FInit with initialization
|
|
// parameters.
|
|
//
|
|
template<class _P> static BOOL
|
|
FInit( const _P& parms ) { return Instance().FInit( parms ); }
|
|
|
|
//
|
|
// Specialization of the above member template for
|
|
// the _Empty parameter type, which calls _X::FInit
|
|
// without initialization parameters.
|
|
//
|
|
static BOOL FInit( const _Empty& ) { return Instance().FInit(); }
|
|
|
|
protected:
|
|
|
|
//
|
|
// Set of states which describe the object's state
|
|
// of initialization. The object's state is
|
|
// STATE_UNKNOWN while it is being initialized or
|
|
// deinitialized.
|
|
//
|
|
enum
|
|
{
|
|
STATE_UNINITIALIZED,
|
|
STATE_INITIALIZED,
|
|
STATE_UNKNOWN
|
|
};
|
|
|
|
static LONG sm_lState;
|
|
|
|
//
|
|
// Expose access to the single instance of class _X
|
|
//
|
|
using Singleton<_X>::Instance;
|
|
|
|
//
|
|
// Expose operator new and operator delete from
|
|
// the Singleton template so that they will be
|
|
// used rather than the default new and delete
|
|
// to "allocate" space for the instance of class _X.
|
|
//
|
|
using Singleton<_X>::operator new;
|
|
using Singleton<_X>::operator delete;
|
|
|
|
static BOOL FInitialized()
|
|
{
|
|
return sm_lState == STATE_INITIALIZED;
|
|
}
|
|
|
|
static LONG CRef()
|
|
{
|
|
return sm_lcRef;
|
|
}
|
|
|
|
public:
|
|
static DWORD DwInitRef( const _ParmType& parms )
|
|
{
|
|
LONG lcRef;
|
|
|
|
//
|
|
// Assert the invariant condition that we never have a
|
|
// reference without the state being initialized.
|
|
//
|
|
Assert( sm_lState != STATE_INITIALIZED || sm_lcRef >= 1 );
|
|
|
|
//
|
|
// Add the reference for the instance we're about
|
|
// to initialize. Doing this now simplifies the
|
|
// code below at the expense of having to decrement
|
|
// if first time initialization (FInit()) fails.
|
|
// The only thing critical to the design is that
|
|
// at any point, when sm_lState is STATE_INITIALIZED,
|
|
// sm_lcRef is at least 1.
|
|
//
|
|
lcRef = InterlockedIncrement( &sm_lcRef );
|
|
Assert( lcRef > 0 );
|
|
|
|
//
|
|
// Don't proceed until the object is initialized.
|
|
//
|
|
while ( sm_lState != STATE_INITIALIZED )
|
|
{
|
|
//
|
|
// Simultaneously check whether initialization has
|
|
// started and, if it has not, start it.
|
|
//
|
|
LONG lStatePrev = InterlockedCompareExchange(
|
|
&sm_lState,
|
|
STATE_UNKNOWN,
|
|
STATE_UNINITIALIZED );
|
|
|
|
//
|
|
// If we're starting first time initialization,
|
|
// then create and initialize the sole instance.
|
|
//
|
|
if ( lStatePrev == STATE_UNINITIALIZED )
|
|
{
|
|
CreateInstance();
|
|
|
|
// This calls our private member template FInit()
|
|
// (declared above), which in turn calls _X::Finit()
|
|
// with the appropriate parameters.
|
|
//
|
|
if ( FInit( parms ) )
|
|
{
|
|
sm_lState = STATE_INITIALIZED;
|
|
break;
|
|
}
|
|
|
|
// We failed to init.
|
|
// Tear down now.
|
|
//
|
|
|
|
Assert( lcRef == 1 );
|
|
Assert( sm_lState == STATE_UNKNOWN );
|
|
|
|
// Let go of our ref on the object.
|
|
// Destroy the object.
|
|
// And LAST, set the state to UNINITIALIZED.
|
|
// NOTE: This will let the next caller through the
|
|
// InterlockedCompare above.
|
|
//
|
|
InterlockedDecrement( &sm_lcRef );
|
|
DestroyInstance();
|
|
sm_lState = STATE_UNINITIALIZED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// If first time initialization is in progress on
|
|
// another thread, then get out of the way so
|
|
// it can finish.
|
|
//
|
|
//$OPT We should probably spin rather than Sleep()
|
|
//$OPT on multi-proc machines on the assumption that
|
|
//$OPT we only get here on a processor other than
|
|
//$OPT the one which is doing the initialization
|
|
//$OPT and we don't want to invite a task switch
|
|
//$OPT by calling Sleep() while we are waiting
|
|
//$OPT for initialization to complete.
|
|
//
|
|
if ( lStatePrev == STATE_UNKNOWN )
|
|
Sleep(0);
|
|
}
|
|
|
|
//
|
|
// At this point, there must be at least
|
|
// one initialized reference.
|
|
//
|
|
Assert( sm_lState == STATE_INITIALIZED );
|
|
Assert( sm_lcRef > 0 );
|
|
|
|
return static_cast<DWORD>(lcRef);
|
|
}
|
|
|
|
static VOID DeinitRef()
|
|
{
|
|
//
|
|
// At this point, there must be at least
|
|
// one initialized reference.
|
|
//
|
|
Assert( sm_lState == STATE_INITIALIZED );
|
|
Assert( sm_lcRef > 0 );
|
|
|
|
//
|
|
// Remove that reference. If it is the last
|
|
// then deinit the object.
|
|
//
|
|
if ( 0 == InterlockedDecrement( &sm_lcRef ) )
|
|
{
|
|
//
|
|
// After releasing the last reference, declare that
|
|
// the object is in an unknown state. This prevents
|
|
// other threads trying to re-initialize the object
|
|
// from proceeding until we're done.
|
|
//
|
|
sm_lState = STATE_UNKNOWN;
|
|
|
|
//
|
|
// There is a tiny window between decrementing the
|
|
// refcount and changing the state where another
|
|
// initialization could have come through. Test this
|
|
// by rechecking the refcount.
|
|
//
|
|
if ( 0 == sm_lcRef )
|
|
{
|
|
//
|
|
// If the refcount is still zero, then no
|
|
// initializations happened before we changed
|
|
// states. At this point, if an initialization
|
|
// starts, it will block until we change state,
|
|
// so it is safe to actually destroy the instance.
|
|
//
|
|
DestroyInstance();
|
|
|
|
//
|
|
// Once the object has been deinitialized, update
|
|
// the state information. This unblocks any
|
|
// initializations waiting to happen.
|
|
//
|
|
sm_lState = STATE_UNINITIALIZED;
|
|
}
|
|
else // refcount is now non-zero
|
|
{
|
|
//
|
|
// If the refcount is no longer zero, then an
|
|
// initialization happened between decrementing
|
|
// the refcount above and entering the unknown
|
|
// state. When that happens, DO NOT deinit --
|
|
// there is now another valid reference somewhere.
|
|
// Instead, just restore the object's state to let
|
|
// other references proceed.
|
|
//
|
|
sm_lState = STATE_INITIALIZED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Assert the invariant condition that we never have a
|
|
// reference without the state being initialized.
|
|
//
|
|
Assert( sm_lState != STATE_INITIALIZED || sm_lcRef >= 1 );
|
|
}
|
|
|
|
// This provides a no-parameter version of DwInitRef
|
|
// for clients that do not need any parameters in FInit().
|
|
//
|
|
static DWORD DwInitRef()
|
|
{
|
|
_Empty e;
|
|
|
|
return DwInitRef( e );
|
|
}
|
|
};
|
|
|
|
template<class _X, class _ParmType>
|
|
LONG RefCountedGlobal<_X, _ParmType>::sm_lcRef = 0;
|
|
|
|
template<class _X, class _ParmType>
|
|
LONG RefCountedGlobal<_X, _ParmType>::sm_lState = STATE_UNINITIALIZED;
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// TEMPLATE CLASS OnDemandGlobal
|
|
//
|
|
// Use this template to implement a global object which should be
|
|
// initialized on its first use ("on demand") and then explicitly
|
|
// deinitialized once if it was ever used.
|
|
//
|
|
// The most common usage is with global objects that ideally are never
|
|
// used or for which up-front initialization is prohibitively
|
|
// expensive. However, once the object has been initialied, it should
|
|
// remain initialized (to avoid having to initialize it again) until
|
|
// it is explicitly deinitialized.
|
|
//
|
|
// !!! OnDemandGlobal DOES NOT provide refcounting functionality to
|
|
// consumers -- It uses refcounting internally, but is not intended
|
|
// to expose it to callers. If you want refcounting, use RefCountedGlobal.
|
|
// If you call DeinitIfUsed(), the instance WILL be destroyed if it
|
|
// exists. In particular, calling code MUST ensure that DeinitIfUsed()
|
|
// is not called while any other thread is inside FInitOnFirstUse().
|
|
// Failure to do so can cause FInitOnFirstUse() to return with the
|
|
// object uninitialized.
|
|
//
|
|
// Usage:
|
|
//
|
|
// class YourClass : private OnDemandGlobal<YourClass>
|
|
// {
|
|
// //
|
|
// // Declare Singleton and RefCountedGlobal as friends
|
|
// // of YourClass so that they can call your private
|
|
// // creation/initialization functions.
|
|
// //
|
|
// friend class Singleton<YourClass>;
|
|
// friend class RefCountedGlobal<YourClass>;
|
|
//
|
|
// //
|
|
// // Private declarations for YourClass
|
|
// //
|
|
// [...]
|
|
//
|
|
// //
|
|
// // Failable initialization function. This function
|
|
// // should perform any failable initialization of the
|
|
// // instance of YourClass. It should return TRUE
|
|
// // if initialization succeeds, and FALSE otherwise.
|
|
// // If YourClass does not have any initialization that
|
|
// // can fail then you should implement this function inline
|
|
// // to just return TRUE.
|
|
// //
|
|
// BOOL FInit();
|
|
//
|
|
// public:
|
|
// //
|
|
// // Public declarations for YourClass. Among these should
|
|
// // be static functions to init and deinit YourClass. These
|
|
// // functions would call FInitOnFirstUse() and DeinitIfUsed()
|
|
// // respectively. Or, you could just expose FInitOnFirstUse()
|
|
// // and DeinitIfUsed() directly to clients of YourClass with
|
|
// // using declarations:
|
|
// //
|
|
// // using RefCountedGlobal<YourClass>::FInitOnFirstUse;
|
|
// // using RefCountedGlobal<YourClass>::DeinitIfUsed;
|
|
// //
|
|
// [...]
|
|
// };
|
|
//
|
|
// FInitOnFirstUse() can be called any number of times from any
|
|
// thread, including simultaneously from multiple threads, but
|
|
// DeinitIfUsed() should only ever be called once, and then
|
|
// only when no other threads are calling FInitOnFirstUse().
|
|
//
|
|
// OnDemandGlobal takes care of tracking whether YourClass was
|
|
// ever actually used so that DeinitIfUsed() is safe to call
|
|
// even if YourClass was never used (hence the name).
|
|
//
|
|
// See \cal\src\_davprs\eventlog.cpp for sample usage.
|
|
//
|
|
template<class _X, class _ParmType = _Empty>
|
|
class OnDemandGlobal : private RefCountedGlobal<_X, _ParmType>
|
|
{
|
|
protected:
|
|
//
|
|
// Expose access to the single instance of class _X
|
|
//
|
|
using RefCountedGlobal<_X, _ParmType>::Instance;
|
|
|
|
//
|
|
// Expose operator new and operator delete from
|
|
// the Singleton template (via RefCountedGlobal)
|
|
// so that they will be used rather than the
|
|
// default new and delete to "allocate" space
|
|
// for the instance of class _X.
|
|
//
|
|
using RefCountedGlobal<_X, _ParmType>::operator new;
|
|
using RefCountedGlobal<_X, _ParmType>::operator delete;
|
|
|
|
public:
|
|
static BOOL FInitOnFirstUse( const _ParmType& parms )
|
|
{
|
|
DWORD dwResult = 1;
|
|
|
|
if ( STATE_INITIALIZED != sm_lState )
|
|
{
|
|
// Add a reference to the object. If this is the first
|
|
// reference, RefCountedGlobal will call _X::FInit()
|
|
// to initialize the object.
|
|
//
|
|
dwResult = DwInitRef( parms );
|
|
|
|
// If this was not the first reference, then release
|
|
// the reference we just added. We only want one
|
|
// reference left around by the time DeinitIfUsed()
|
|
// so that the DeinitRef() called from there will
|
|
// actually destroy the object.
|
|
//
|
|
if ( dwResult > 1 )
|
|
DeinitRef();
|
|
}
|
|
|
|
// Return success/failure (dwResult == 0 --> failure)
|
|
//
|
|
return !!dwResult;
|
|
}
|
|
|
|
static BOOL FInitOnFirstUse()
|
|
{
|
|
_Empty e;
|
|
|
|
return FInitOnFirstUse( e );
|
|
}
|
|
|
|
static VOID DeinitIfUsed()
|
|
{
|
|
//
|
|
// If the object was never initialized (i.e. there was
|
|
// never a reference to it), then do nothing.
|
|
// Otherwise, deinit the object.
|
|
//
|
|
if ( FInitialized() )
|
|
{
|
|
//
|
|
// Make sure there is EXACTLY one reference.
|
|
// Zero references indicates a bug in setting the
|
|
// initialization state. More than one reference
|
|
// most likely indicates that OnDemandGlobal is
|
|
// being used where RefCountedGlobal is needed.
|
|
//
|
|
Assert( CRef() == 1 );
|
|
|
|
DeinitRef();
|
|
}
|
|
}
|
|
};
|
|
|
|
#endif // _SINGLTON_H_
|