views:

196

answers:

7

Hi,

I was reading around a lot about singleton. I am thinking about the dead reference problem between singletons. In every primer on net , this problem is encountered when one singleton calls other singleton in its destructor, and that singleton is already destroyed, say Log singleton can be called from destructor of many other singletons.

I can't imagine when in other case ( except referencing other singletons in dtr ), the dead reference would be a problem. Can you give me a real world example in which such a problem exists , and how can I solve it ?

The thing is that I need to implement a couple of singletons in our project, which all communicate with each other, and I am having real hard time to choose the right way. Please do not say not to use a singleton, because that's not my decision.

+3  A: 

Destruction order problems are part and parcel of the singleton pattern.

Please do not say not to use a singleton, because that's not my decision.

Not using them is the right thing to do - seeing as that's not possible, you're going to have to use a hacky workaround. Here are some possible solutions, but none of them are pretty:

  • Don't reference other singletons in your destructors
  • Explicitly destroy the singletons in the right order at the end of main
  • Have your singletons hold references to the other singletons by weak_ptr - they can be destroyed independently of each other and you can safely check if a referenced singleton still exists before using it

Also, I would recommend against creating or destroying singletons in a multithreaded context - it's far easier to ensure that all singletons are created before any new threads, and all threads except the main thread have stopped before destroying them.

Joe Gauterin
I had your third point in my deleted answer: as interesting as weak_ptr is in multithreaded applications, it doen't mix well with singletons: you need a shared_ptr that needs to be destroyed at some point (okayish), but then you need to check (lock) the weak_ptr and have an action when it fails, which is what singleton tries to avoid.
stefaanv
As I said, there's no good answer except not using them.
Joe Gauterin
Destruction order is not a problem any more than construction order is a problem.
Martin York
@user152508: Destruction order is no more a problem than construction order. I would also say that EVERY point made above is wrong. (Its easy to access singeltons correctly from the destructor) (Expecting a user to do explicit destruction is the wrong answer as inevitably something will change and the compiler will not detect it. Make the code so the compiler detects it and does it automatically) (Using Weak ptr does not solve the problem. Use a solution that solves the problem. Weak Ptr are designed to solve circular dependencies) (threading problem is non existent. Just add a lock)
Martin York
Martin - your solution merely moves the fragility and explicit demands on the programmer from the destructor to the constructor.
Joe Gauterin
+1  A: 

DumbCoder already pointed you in the right direction. In Modern C++ design, Andrei Alexandrescu explained the intricate design issues with Singletons and showed multiple solutions depending on the precise requirements on the singleton.

It is not a complete guide to all possible singleton implementations, though. You should read it not for the code, but to understand the analysis. The, apply the knowledge you've gained to your particular situation.

To answer your specific questions, the other common case of "dead" references is better called "unborn references" - using a singleton before its constructor is run. But it should be obvious that since singletons live during most of the lifetime of a program, the only two times they don't exist is at the very begin and very end.

MSalters
A: 

As far as I remember, singletons are created in the order you call the access functions the first time and destroyed in reverse order.

As such, you can create an init function for your application (and make sure it's the very first thing to be called in your main function).

Within this function, call the singleton access functions in the order you want them to be created.

utnapistim
Depends how you implement them, not all are lazy...
Matthieu M.
A: 

This article is interesting. The static initialisation/destruction issues discussed in c++ faq lite too. You probably have read them already ?

For a better answer you should precise your needs, I mean tell us what kind of lifecycle you need for you singletons what sort of communication is needed, what is the multithreading environnement ...

my2c

neuro
+1  A: 

A possibility I haven't seen mentioned above, and that may or may not be acceptable depending on what they manage: allocate the Singletons on the heap and don't destruct them... just let the OS reclaim any descriptors/memory/locks etc they're holding when the app terminates (note: doesn't work for everything, e.g. locks in shared memory).

Tony
A: 

We lack information, most notably I do hope that we are talking C++0x otherwise it's going to be quite difficult.

The first solution is to explicitly manage your singletons. Most of the designs you encounter on the web focus on simplicity and ease of use at the cost of correctness in the general situation.

The most simple way not to have any issue with your singletons depending from each other is to instantiate them and release them while you are still single-threaded (to avoid synchronization issues) and in the right order.

This is naturally followed by the idea of a Singleton Manager which is some sort of "super singleton" and will instantiate your singletons and release them accordingly. All accesses to a singleton are done through it so that it can ensure that they are live when accessed. Once again, creation and destruction occurring in single-threaded situation.

It gets much more difficult when we're talking about lazy initialization (on demand). The most simple scheme for that is the local static variable:

MySingleton& Get() { static MySingleton M; return M; }

C++0x finally guarantees that only one instance of MySingleton will be instantiated which makes things much easier! However you do have here the "dead reference" problem.

In C++ the destruction order of static objects is simply the reverse of the construction order, therefore one solution is to mandate that any singleton used within the destructor of a singleton object be used in the constructors (ALL of them). This way you actually guarantee that it will be built before, and thus destroyed after.

Note that lazy instantiation is difficult in a multithreaded environment in C++03 (or sooner) because there was no guarantee that a single instance would be created... and it's extremely difficult to grab a lock at that point (after all, the mutex is itself a singleton... isn't it ? ).

Matthieu M.
You said "any singleton used within the destructor of a singleton object be used in the constructors (default, copy, ... ALL of them)", but singleton objects never have copy constructors, so specifically the one with the destructor has a deleted copy constructor which does not and should not reference the other singleton. Furthermore, a copy constructor can't ever be the first constructor used (in any well-behaved program).
Ben Voigt
@Ben Voigt: right, I was so intent on the ALL that I forgot that.
Matthieu M.
A: 

Copied from here: http://stackoverflow.com/questions/335369/finding-c-static-initialization-order-problems/335746#335746 (Nobody would have followed just a link sorry)

Also see this article: http://stackoverflow.com/questions/1008019/c-singleton-design-pattern/1008289#1008289

Destruction Problems:

There is a potential problem of accessing the object after it has been destroyed. This only happens if you access the object from the destructor of another global variable (by global I am refering to any non local static variable).

Solution you must make sure you force the order of destruction.
Remember the order of destruction is the exact inverse of the order of construction. So if you access the object in your destructor you must gurantee that the object has not been destroyed. To do this you must just gurantee that the object is fully constructed before the calling object is constructed.

class B
{
    public:
        static B& getInstance_Bglob;
        {
            static B instance_Bglob;
            return instance_Bglob;;
        }

        ~B()
        {
             A::getInstance_abc().doSomthing();
             // The object abc is accessed from the destructor.
             // Potential problem.
             // You must guarantee that abc is destroyed after this object.
             // To gurantee this you must make sure it is constructed first.
             // To do this just access the object from the constructor.
        }

        B()
        {
            A::getInstance_abc();
            // abc is now fully constructed.
            // This means it was constructed before this object.
            // This means it will be destroyed after this object.
            // This means it is safe to use from the destructor.
        }
};
Martin York