
Background: Since the first version of my singleton library was rejected, I have been designing a new singleton library from the ground up. I feel that this is necessary because the first version was not designed with the concerns of threading in mind, and such concerns cannot easily be addressed efficiently as an afterthought. The new design should hopefully be able to handle threading effectively without introducing any overhead for the single threaded model. Question: With the new design, I am adding a policy to control exactly what happens if creation is attempted while the singleton instance exists. The options thus far include throwing an exception, doing nothing, or destroying and recreating the singleton, depending on the policy used. Does it make sense to have a similar policy for what happens when attempting to destroy an already destroyed instance? I can think of a few options that would make sense, such as doing nothing, throwing an exception, asserting, or creating the instance so that it can be re-destroyed, but I do not know if these would actually be useful enough to justify a point of customization. Thoughts? -Jason

On Sat, 16 Jul 2005 14:21:34 -0400, Jason Hise wrote
Background: Since the first version of my singleton library was rejected,
Happy to hear you're still working on this :-)
I have been designing a new singleton library from the ground up. I feel that this is necessary because the first version was not designed with the concerns of threading in mind, and such concerns cannot easily be addressed efficiently as an afterthought.
This sounds a bit drastic. I'm sorry I didn't have time to follow the review in detail, but I would imagine that much of the interface was perfectly fine.
The new design should hopefully be able to handle threading effectively without introducing any overhead for the single threaded model.
Excellent.
Question: With the new design, I am adding a policy to control exactly what happens if creation is attempted while the singleton instance exists. The options thus far include throwing an exception, doing nothing, or destroying and recreating the singleton, depending on the policy used.
I can't imagine wanting a client wanting an exception if a 'race condition' with multiple threads leads to several clients needing the singleton. I also don't think destroy and recreate makes much sense -- that would invalidate the reference of the first client. Why not just simplify and return the already created singleton in all cases?
Does it make sense to have a similar policy for what happens when attempting to destroy an already destroyed instance? I can think of a few options that would make sense, such as doing nothing, throwing an exception, asserting, or creating the instance so that it can be re-destroyed, but I do not know if these would actually be useful enough to justify a point of customization.
Seems doubtful to me. Jeff

Jeff Garland wrote:
Happy to hear you're still working on this :-)
Thanks :)
I have been designing a new singleton library from the ground up. I feel that this is necessary because the first version was not designed with the concerns of threading in mind, and such concerns cannot easily be addressed efficiently as an afterthought.
This sounds a bit drastic. I'm sorry I didn't have time to follow the review in detail, but I would imagine that much of the interface was perfectly fine.
The old version was perfectly usable, but by redesigning after everything I learned the first time I have been able to make significant organizational improvements, simplifications, and interface enhancements. I didn't start over just because of threading concerns.
Question: With the new design, I am adding a policy to control exactly what happens if creation is attempted while the singleton instance exists. The options thus far include throwing an exception, doing nothing, or destroying and recreating the singleton, depending on the policy used.
I can't imagine wanting a client wanting an exception if a 'race condition' with multiple threads leads to several clients needing the singleton. I also don't think destroy and recreate makes much sense -- that would invalidate the reference of the first client. Why not just simplify and return the already created singleton in all cases?
Create, destroy, and access to member functions through smart pointers are locked, so a destroy followed by a recreate should not invalidate the pointer in another thread (based on their location in my code, destroy and create would be completed as one atomic step). When used by the other thread, the pointer would automatically forward to whichever instance exists. It could be useful to destroy and recreate a singleton for any number of reasons, but for one example consider a graphics engine. If the full screen application is minimized and then expanded again all graphics card resources would need to be reacquired. This is could be most naturally handled by destroying and recreating a singleton in charge of the display.
Does it make sense to have a similar policy for what happens when attempting to destroy an already destroyed instance? I can think of a few options that would make sense, such as doing nothing, throwing an exception, asserting, or creating the instance so that it can be re-destroyed, but I do not know if these would actually be useful enough to justify a point of customization.
Seems doubtful to me.
I tend to agree. Although at first I thought that the destruction of an already destroyed instance was similar semantically to a double delete (and hence should throw an exception) I now no longer think that it makes sense to regard double destruction as a programming error, especially in multi-threaded contexts. Thanks for the comments, hopefully the first version of the new singleton library will be ready for release before this summer is out. -Jason

On Sun, 17 Jul 2005 18:01:38 -0400, Jason Hise wrote
Jeff Garland wrote:
The old version was perfectly usable, but by redesigning after everything I learned the first time I have been able to make significant organizational improvements, simplifications, and interface enhancements. I didn't start over just because of threading concerns.
Ok, good.
Create, destroy, and access to member functions through smart pointers are locked, so a destroy followed by a recreate should not invalidate the pointer in another thread (based on their location in
Ok, but the other client has a pointer to the old singleton, no?
my code, destroy and create would be completed as one atomic step). When used by the other thread, the pointer would automatically forward to whichever instance exists.
Smart pointer isn't going to do this, unless you have some special singleton pointer that somehow knows to reset itself or always calls 'instance' behind the scenes...
It could be useful to destroy and recreate a singleton for any number of reasons, but for one example consider a graphics engine. If the full screen application is minimized and then expanded again all graphics card resources would need to be reacquired. This is could be most naturally handled by destroying and recreating a singleton in charge of the display.
Ok, but I have to say that singleton seems like a terrible approach your particular use case. The 'Graphics Engine' singleton should just release and reaquire the resources without direct client access or recreating of the singleton itself. By far and away the most usual case for singletons is create once (often before main) and destruct last (often in atexit).
I tend to agree. Although at first I thought that the destruction of an already destroyed instance was similar semantically to a double delete (and hence should throw an exception) I now no longer think that it makes sense to regard double destruction as a programming error, especially in multi-threaded contexts.
Yep.
Thanks for the comments, hopefully the first version of the new singleton library will be ready for release before this summer is out.
Look forward to it. Jeff

Jason Hise writes:
I am adding a policy to control exactly what happens if creation is attempted while the singleton instance exists. The options thus far include throwing an exception, doing nothing, or destroying and recreating the singleton, depending on the policy used.
Could you post some source code that illustrates what you mean by "creating" an instance? The usual approach to initializing static instances is call_once() from Boost.Thread; so I am curious to know how your approach compares to that.
Does it make sense to have a similar policy for what happens when attempting to destroy an already destroyed instance?
A failed assertion would be my preferred way to handle this condition; I don't see the need for a policy class. Peter

Peter Simons wrote:
Jason Hise writes:
I am adding a policy to control exactly what happens if creation is attempted while the singleton instance exists. The options thus far include throwing an exception, doing nothing, or destroying and recreating the singleton, depending on the policy used.
Could you post some source code that illustrates what you mean by "creating" an instance?
singleton_ptr < Type > ptr; // smart pointer to single instance ptr.create ( /*ctor params here*/ ); // creates the instance ptr.create ( /*ctor params here*/ ); // instance already exists, what should happen? ptr->SomeMemberFunctionOfType ( 42 ); // perform an action on the singleton
The usual approach to initializing static instances is call_once() from Boost.Thread; so I am curious to know how your approach compares to that.
I will have to look up call_once before I will be able to compare it with my approach.
Does it make sense to have a similar policy for what happens when attempting to destroy an already destroyed instance?
A failed assertion would be my preferred way to handle this condition; I don't see the need for a policy class.
Others have expressed (perhaps not publicly) that their preferred method would be that secondary destructions are simply no-ops, rather than being errors or asserting at all. Such conflicting interests make it seem like a policy is indeed necessary. Or perhaps just multiple destroy functions (destroy_if_exists, destroy_assert_if_null). -Jason

Jason Hise writes:
singleton_ptr < Type > ptr; ptr.create ( /*ctor params here*/ ); ptr.create ( /*ctor params here*/ );
What happens when two threads call create() at the same time?
I will have to look up call_once before I will be able to compare it with my approach.
My guess is that you will need call_once() as part of your implementation for it to be thread-safe (because of the race-condition mentioned above).
Others have expressed (perhaps not publicly) that their preferred method would be that secondary destructions are simply no-ops, rather than being errors or asserting at all. Such conflicting interests make it seem like a policy is indeed necessary.
Well, having a policy mechanism won't hurt. I'm all for it. ;-) Peter

"Peter Simons" wrote:
Others have expressed (perhaps not publicly) that their preferred method would be that secondary destructions are simply no-ops, rather than being errors or asserting at all. Such conflicting interests make it seem like a policy is indeed necessary.
Well, having a policy mechanism won't hurt. I'm all for it. ;-)
Adding one more policy reduces number of people who will use a template. This is equivalent of adding one more parameter to a function and seeing people writing their own homemade version. /Pavel

Pavel Vozenilek wrote:
"Peter Simons" wrote:
Others have expressed (perhaps not publicly) that their preferred method would be that secondary destructions are simply no-ops, rather than being errors or asserting at all. Such conflicting interests make it seem like a policy is indeed necessary.
Well, having a policy mechanism won't hurt. I'm all for it. ;-)
Adding one more policy reduces number of people who will use a template. This is equivalent of adding one more parameter to a function and seeing people writing their own homemade version.
/Pavel
Except that I plan to use a different policy mechanism with the new version, so that client code need only concern itself with relevant policies. Client code will be able to specialize specific policies for a given category, and then just choose a category for each singleton, like so: struct MySingletonCategory; // must provide specializations in singleton namespace namespace boost { namespace singleton { // the following specifies that multi threading will be used for singletons which use MySingletonCategory template < > struct threading_policy < MySingletonCategory > { typedef multi_threaded type; }; // no other specializations provided, so defaults for other policies are used automatically // now tie our specific singleton type to this category template < > struct category < MySingletonType > { typedef MySingletonCategory type; }; } } // close namespace void test ( ) { singleton_ptr < MySingletonType > ptr; // no policies are specified here } Granted, defining a category like so is a bit more work, but client code can completely ignore policies that it doesn't care about, and reuse useful categories that it has defined for multiple different singleton types. Perhaps macros could be used to make this a cleaner process for client code. With this model, introducing many policies shouldn't cause any problems, and policies can even be added retroactively without breaking existing code. -Jason

Except that I plan to use a different policy mechanism with the new version, so that client code need only concern itself with relevant policies. Client code will be able to specialize specific policies for a given category, and then just choose a category for each singleton, like so:
struct MySingletonCategory;
// must provide specializations in singleton namespace namespace boost { namespace singleton {
// the following specifies that multi threading will be used for singletons which use MySingletonCategory template < > struct threading_policy < MySingletonCategory > { typedef multi_threaded type; };
This should be called *trait* not a policy. Policy is type orthogonal. And btw I don't think traits-based solution will work here. I most definitely may want different threading policies for the same singleton type in different circumstances. Did you consider NTP? Gennadiy

David Greene wrote:
Gennadiy Rozental wrote:
Did you consider NTP?
I am unfamiliar with that acronym, what is NTP?
Named template parameters
Speaking of which, I have an implementation that I've been meaning to vet with the group. I'll get it into the sandbox tonight.
I got home and realized that I still have quite a bit of documenting to do. In order to make this more immediately useful to Jason and others, I'll get something into the vault this weekend. -Dave

David A. Greene wrote:
I got home and realized that I still have quite a bit of documenting to do. In order to make this more immediately useful to Jason and others, I'll get something into the vault this weekend.
That does sound useful, I look forward to playing with it. In the mean time, what do people think of the following solution? namespace boost { namespace singleton { struct name { // typedef all default policies here }; typedef name unnamed; } template < typename Type, typename Name = singleton::unnamed > class singleton_ptr { /*...*/ }; } The name acts as both the policy mechanism and as a unique identifier to allow multiple singletons of the same type to exist. Client code can define a name by deriving from singleton::name and typedefing any policies needed, allowing the defaults for the others to be inherited from the base class. Ex: using namespace boost; struct score : public singleton::name { typedef singleton::dependency_lifetime lifetime_policy; // leave defaults for other policies }; struct lives : public singleton::name { typedef singleton::dependency_lifetime lifetime_policy; // leave defaults for other policies }; typedef singleton_ptr < int, score > score_ptr; typedef singleton_ptr < int, lives > lives_ptr; -Jason

"David A. Greene" <greened@obbligato.org> writes:
David Greene wrote:
Gennadiy Rozental wrote:
Did you consider NTP?
I am unfamiliar with that acronym, what is NTP?
Named template parameters
Speaking of which, I have an implementation that I've been meaning to vet with the group. I'll get it into the sandbox tonight.
I got home and realized that I still have quite a bit of documenting to do. In order to make this more immediately useful to Jason and others, I'll get something into the vault this weekend.
FYI, the Boost Parameters library is planning to support named and unnamed function and template parameters in the release after Boost 1.33 -- Dave Abrahams Boost Consulting www.boost-consulting.com

"Jason Hise" wrote: [ policies ]
Granted, defining a category like so is a bit more work, but client code can completely ignore policies that it doesn't care about, and reuse useful categories that it has defined for multiple different singleton types.
If the function destroy() would be renamed to ensure_resource_is_freed() or so no "on-double-destroy" policy may be needed. Such name would also give hint about expected result rather than about internal implementation. --------------- About macros: one should keep them out when possible. If the library will (in the future) handle "application-wide-singletons" (a type would be singletonn even if put in static library linked into a DLL that is manually loaded in an EXE) then some macros will be (likely) necessary. /Pavel
participants (8)
-
David A. Greene
-
David Abrahams
-
David Greene
-
Gennadiy Rozental
-
Jason Hise
-
Jeff Garland
-
Pavel Vozenilek
-
Peter Simons