Menu

Singleton thread safety & life cycle changes

2006-07-29
2013-04-08
  • Einar Otto Stangvik

    I've recently found myself in need of a certain type of certain Singleton, with a life cycle controlled by a reference counter. This is handy when a service provider is needed by several blocks, but whose life time should be kept at a minimum. It's especially useful if the creation / destruction overhead is less than that of a continous run.

    Upon needing this, I looked to Loki's implementation of the Singleton pattern, but found that certain features are lacking for it to be as efficient as possible. I ended up writing a flavor of my own (which actually utilizes the threading policies of Loki). This is based of smart pointer principles in the sense that it acquires and releases singleton instances as the proxy class enters and falls out of scope. Apart from the refcount, it resembles the DeletableSingleton / PhoenixSingleton policies.

    As for Loki's Singleton, a discussion follows.

    My initial line of thought is that SingletonHolder could call into the LifeCycle policy class when an instance is being requested, to increase the ref count. To keep instance acquiring overhead as low as possible, this would be done if (and only if) the LifeCycle policy says so -- e.g. by keeping a public static const flag for which events it needs notification of. With the 80-20 rule in mind, this should be an acceptable code addition performance wise (I'll get back to further worries, though). The actual implementation of the RefCount LifeCycle policy would be similar to that of DeletableSingleton, with an added reference count decrease upon deletion. A smart pointer like wrap of the SingletonHolder would call the deletion method as it's destructed.

    This solution has many flaws. For DeletableSingleton (and others) to remain safe, SingletonHolder::Instance should keep returning a reference (or should it? Discussed shortly). Also, any calls done from this method to other classes would definitely wreak havoc on it being inline. All considered, it's pretty clear that the current SingletonHolder::Instance can't increment the ref count.

    The fact that SingletonHolder::Instance returns a reference after checking pInstance_, doesn't make it completely safe. Even with ClassLevelLockable as ThreadingPolicy, two threads working on the same singleton may find themselves crashing. If one thread is swapped out right after “if(!pInstance_)” is executed, and the other manages to ship off e.g. a GracefulDelete, oblivion awaits. For this to be resolved, a ThreadingModel::Lock must be added to both SingletonHolder::Instance as well as SingletonHolder::DestroySingleton. This will in turn make inlining SH::Instance and returning a reference a lot less exotic, as (at least in my book) existence concurrency not necessarily should mean call concurrency.

    So how does all this add up? I’ll depict one possible solution. Methods like GracefulDelete should schedule a deletion, rather than issuing it instantly. Singletons would be accessed on a get / release basis. Acquiring an instance of a singleton increases a reference count. Upon release, the count will be decreased. Only upon decreasing the reference count to zero should a scheduled delete be done. For my initially described LifeCycle variation, a delete would be scheduled immediately upon the initial creation. Whether or not all this should be up to the LifeCycle policy to implement, I can’t say. Also, this design is anything but perfect. Even with acquiring and releasing of singleton instances controlled by smart pointer like classes, one could end up with reference counts never dropping to zero. Possible solutions? Plenty :)

     
    • Peter Kuemmel

      Peter Kuemmel - 2006-07-30

      Hi Einar,

      I agree, SingletonHolder is not ready for use with a ref counted smart pointer, because PtrInstanceType is a pure pointer T:typedef typename ThreadingModel<T*,MutexPolicy>::VolatileType PtrInstanceType;

      Maybe the simplest way is to change the typedef and Instance to return a smart pointer.

      But generally I'm not sure if it is necessary to use the Singleton pattern. Didn't a smart pointer with a auto create functionality (have a look at Pimpl.h) fit better?

      Very interesting is the problem of GracefulDelete in a multithreaded environment, I have to think about it.

      Peter

       
    • Einar Otto Stangvik

      > Maybe the simplest way is to change the typedef and Instance to return
      > a smart pointer.

      Returning a smart pointer should be sufficient, but I do see some pitfalls. First of all it has to be adapted to the LifeCycle policies. It seems to me that these should be responsible for telling when (though not how) the singletons are to be established and destructed. Also, there’s the question of concurrency and threading policies. Should the smart pointer returned from SingletonHolder be implemented both in terms of the LifeCycle and ThreadingModel? If so, should it check if the instance actually is alive upon calls to operator-> (which would require locks, and force everyone back to the mutex-on-access-vs-creation dilemma), or should it just be a helper forced upon the user for SingletonHolder / LifeCycle to know when it can safely go through with destructions?

      The latter is similar to what I mentioned in the first posting. Forcing the use of singletons through a smart pointer should relieve both the ref count implementation problems as well as deal with the concurrency issues with multithreaded GracefulDelete. It will however cost you the power to abruptly destroy singletons (which from several perspectives seems to be fundamentally problematic).

      What I haven’t considered much is the implications of this on different longevity variations. The need for longevity might actually vanish in many cases, in others it might make it intolerably complicated from a library point of view. The user will have to make sure that no stray smart pointers exist, or else the longevity deletion would have no effect – and potentially lead to disaster.

      > But generally I'm not sure if it is necessary to use the Singleton
      > pattern.

      The use of singleton vs other patterns would depend a lot on the problem at hand. I absolutely agree that in many cases a pimpl (or factory) approach may be better, but I do feel that my usage was justified. I’m sure there are those who disagree, though :)

      An example of (in my eyes) fit use would be a win32 networking library I was working on. A few of its classes provided asynchronous notifications through window messages (details omitted for the sake of simplicity). All the async socket classes had a reference to a singleton message provider which would, upon its creation, construct a thread with a message loop. The sockets could then register which notifications they wanted. At any given moment, either one or zero of these singletons would exist; destructing themselves as the last async socket died off only to emerge again if another async socket instance came about. The use of a ref counting singleton from within the needing classes removed the need for a factory for the library user to make sockets through.. And ease of use scores big with me.

       

Log in to post a comment.