ASPN ActiveState Programmer Network
ActiveState
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups


Recent Messages
List Archives
About the List
List Leaders
Subscription Options

View Subscriptions
Help

View by Topic
ActiveState
.NET Framework
Open Source
Perl
PHP
Python
Tcl
Web Services
XML & XSLT

View by Category
Database
General
SOAP
System Administration
Tools
User Interfaces
Web Programming
XML Programming


MyASPN >> Mail Archive >> boost
boost
[boost] singleton design plan
by Jason Hise other posts by this author
Jul 28 2005 1:35PM messages near this date
Re: [boost] Interest in an ODBC library? | Re: [boost] singleton design plan
SOURCE This message is an in depth description of my current design plan for a 
new singleton library, which I hope to complete and schedule for review 
by boost in the coming year.  I would appreciate any comments, 
questions, or suggestions regarding this plan before I begin work on the 
code.  Specifically, I would like to know if there are any important 
issues that this design fails to address, or difficulties with the 
interface that would turn away potential users.

(My old singleton library, which was not accepted by boost, is available 
at http://tinyurl.com/6qvrd.  It was rejected primarily because it 
failed to adequately address concerns related to multi threading and dlls.)

Thus far, my high level design plan looks like this:

The creator_policy defines fundamental pointer, const_pointer, and 
reference types.  These hold references to a creator policy instance 
which maintains the singleton internally, so that the pointer types can 
always see and access the state of the single instance.  This way, 
pointers to the singleton instance are not invalidated when the state of 
the singleton changes.  The creator_policy defines create, replace, and 
destroy methods, as well as methods to generate instances of the various 
pointer types.  Create only creates the instance if it does not already 
exist, destroy only destroys the instance if it does exist, and replace 
makes sure any existing instance is destroyed before attempting 
creation.  Thus far, I hope to support the following creators:

--------------------------------------------------------------------------------

in_place_creator
creates/destroys inside an aligned_storage POD

interface:

create  ( [ctor params] );
replace ( [ctor params] );
destroy (  );

pros: no memory allocation/deallocation ever required
      constructor params can be easily specified

cons: automatic creation, when it occurs, always uses the default 
constructor
      singleton cannot be created as a derived type

useful for simple, user defined singleton classes

--------------------------------------------------------------------------------

source_sink_creator
owns fixed source and sink functors, specified with template parameters

interface:

create  (  );
replace (  );
destroy (  );

pros: source can return a derived type
      automatic creation uses source rather than default construction

cons: source and sink cannot be set or changed at runtime
      constructor arguments cannot be specified by calling code

useful for singleton types provided by untouchable code, which may
require calling special functions to get pointers to instances

--------------------------------------------------------------------------------

dynamic_source_sink_creator
owns dynamic source and sink functors

interface:

create  ( [source[, sink]] );
replace ( [source[, sink]] );
destroy ( [source[, sink]] );

pros: source can return a derived type
      automatic creation uses source rather than default construction
      source and sink can optionally be changed at run time
      source and sink can change functor types when they are set
      (example: from function pointer to lambda expression)

cons: specifying constructor parameters to pass is non-trivial
      dynamic memory allocation and virtual functions must be used to 
store the functors
      in order to allow for different functor types at runtime, thus 
creation is more
      expensive and can potentially fail

useful for a singleton which needs to take the forms of unspecified 
derived types
and is frequently destroyed and created via some type of factory

--------------------------------------------------------------------------------

The lifetime_policy owns and wraps the creator policy, and provides 
pointer and reference types which wrap those belonging to the creator.  
It also provides functions to generate instances of the various pointer 
types, and provides a function to access the creator_policy instance.  
The unmanaged lifetime simply typedefs the underlying pointer and 
reference types, and thus results in no overhead.  The 
longevity_lifetime and lifo_lifetime behave similarly, but handle 
scheduling of automated destruction when any of the pointer generation 
methods are called.  The dependency_lifetime will wrap the internal 
pointer types to perform reference counting (the reference count itself 
will be stored in the lifetime_policy instance).  The timeout_lifetime 
will manage a separate thread that releases the instance when it 
finishes counting down.  It will also wrap the internal pointer types to 
reset the timer when a pointer is dereferenced, and reset and lock the 
timer while any references exist.

The threading_policy owns and serializes access to the lifetime_policy.  
It also has a function to get access to the lifetime_policy instance, 
and provides pointer, const_pointer, and reference types which wrap and 
forward to the lifetime's corresponding types.  For the single_threaded 
policy, these pointer types are simply typedefs of the lifetime's 
pointer types, resulting in no overhead, and the function through which 
the lifetime_policy instance is accessed just returns a raw pointer to 
it.  For the multi_threaded policy, pointer types perform locking before 
forwarding to lifetime pointer types, and the reference type acts as a 
lock throughout it's existence.  The mutex is stored inside the 
threading_policy instance.  The function which returns a pointer to the 
lifetime_policy instance returns a smart pointer which serializes access.

A storage_policy determines how instances of the threading_policy are 
stored (and because these instances contain instances of the other 
policies, this indirectly controls how all of the policy instances are 
stored).  static_storage stores policy instances directly as global 
variables, which is fine for singletons that are not required to have a 
specific lifetime with regard to other singletons.  These global 
variables will be set up in such a way that it will be guaranteed that 
any automatic destruction takes place before the policy bundle is 
destroyed.  group_storage allows multiple singletons to have their 
policies stored in a master group.  No policies in this group are 
destroyed until all automated destruction that will be performed by any 
members of the group has completed.  This way, if destructors of some of 
the singletons in this group trigger re-creation of other singletons in 
the same group, the policies needed to recreate the singletons are 
guaranteed to still exist.  dll_storage requires linking with 
singleton.dll, and stores instances in a special exported manager 
singleton to ensure unique singleton instances across dll boundaries.  
All policy instances of this type are guaranteed to be destroyed only 
after all automated destruction has taken place.

A singleton class exists, which takes a singleton type and a name tag as 
template parameters.  The name will have a default type called 
'unnamed'.  This name parameter enables using multiple singleton 
instances of the same type.  Policies are traits of the name specified, 
so using the default name 'unnamed' results in using the default 
policies.  Policies can be specified by declaring a name as an undefined 
struct, and then specializing the desired policy traits for that name.

The singleton class contains three pointer types, which wrap those of 
the threading policy.  These are strong_pointer, lazy_pointer, and 
weak_pointer.  strong_pointer attempts automatic creation of the 
singleton instance if it does not exist when the pointer is first 
created, and also attempts automatic creation if needed any time it is 
dereferenced.  lazy_pointer only attempts automatic creation when 
dereferenced, and weak_pointer will never create the instance 
automatically.  For the lifo_lifetime, a strong_pointer can be used to 
specify a dependency of some singleton B on some singleton A by having B 
own a strong_pointer to A.  This will ensure that A is created before B 
and destroyed after B.

Const versions of these three pointer types will also exist.  Creation, 
destruction, and non-const member functions will not be allowed to be 
called through these const pointer types.  A single reference type will 
exist which will be the result of dereferencing any pointer type via the 
unary * operator.  const_pointers return const qualified references.  
Pointers only perform temporary locking (if the threading policy 
supports locking) when a member function is called via the ->  operator 
or when a create or destroy operation is invoked.  References lock the 
singleton throughout their entire lifetime, and thus can be used to make 
a series of operations on the singleton instance atomic.  A reference 
provides an implicit conversion to a raw reference of the singleton's 
type (which comes back const if the reference is const).  Create and 
destroy can only be called on non-const references.

Typical code will access a singleton instance by creating one of the 
above pointer types and calling member functions through it via the ->  
operator.  Ex:

singleton < MyType, SomeName > ::lazy_pointer ptr;
ptr-> DoSomething (  );

--------------------------------------------------------------------------------

I plan to sneak an improved version of the null developed by Scott 
Meyers in with this code in a header called boost/null.hpp, and use it 
in the singleton's implementation.  It will be defined as follows:

namespace boost
{
    const class
    {
    private:
        void operator & (  ) const;

    public:
        #ifndef BOOST_NO_MEMBER_TEMPLATES

        // provide conversion to any pointer type
        template < typename Class, typename Type > 
        operator Type Class::* (  ) const { return 0; }

        template < typename Type > 
        operator Type * (  ) const { return 0; }

        #else

        //provide conversion to int if member templates are not supported
        operator int (  ) const { return 0; }

        #endif//BOOST_NO_MEMBER_TEMPLATES

    } null;
}

#undef NULL
#define NULL (::boost::null)

--------------------------------------------------------------------------------

Any and all comments or suggestions on this plan are welcomed.  
Potential problems are much easier to address at design time than after 
the code is written.

-Jason


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Thread:
Jason Hise
Martin Wille
Jason Hise
Martin Wille
Eric Niebler
Eric Niebler
Jason Hise

Privacy Policy | Email Opt-out | Feedback | Syndication
© ActiveState Software Inc. All rights reserved