3

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.

DumbCoder
  • 5,696
  • 3
  • 29
  • 40
user152508
  • 3,053
  • 8
  • 38
  • 58
  • I don't understand why this is a problem specific in the case of singletons. – Cedric H. Aug 18 '10 at 07:24
  • I assume your application is multithreaded, added the tag, correct me if I am wrong. – DumbCoder Aug 18 '10 at 07:28
  • 1
    Modern C++ design: generic programming and design patterns applied(Andrei Alexandrescu), Chapter 6 has an investigation and solution to the dead reference problem. Did you try that out ? – DumbCoder Aug 18 '10 at 07:31
  • Yes I read the book, but I don't think that the Phoenix singleton is applicable in my case, cause my singletons carry a lot of state, and I am not sure that it is ok to recreate them all the time. Also the singletons with longevity, seems like no option cause I need manually to set number for each , which seems error prone. I was thinking maybe I need not any dead reference detection, cause I will not use my singletons from destructors of other singletons, but I am not sure if this the only scenario where dead reference can be encountered – user152508 Aug 18 '10 at 07:37
  • Depending on your target platform, and what the singleton actually does, you might be able to split the singleton in half. A phoenix that holds the non-memory resources, and a straight pointer that holds state. The state will just leak, but the system is about to reclaim that memory anyhow, so its only a pseudo-leak. – Dennis Zickefoose Aug 18 '10 at 07:59
  • I prefer the monostate design pattern as compared to Singleton. Singleton has many issues (http://en.wikipedia.org/wiki/Singleton_pattern) – Chubsdad Aug 18 '10 at 08:20
  • I will say "don't use singletons". Even if that's not helpful in your situation, that is the by far the simplest way to avoid the problems they bring if you are able to make design decisions. – Mike Seymour Aug 18 '10 at 11:35
  • @chusbad: I too prefer to expose a monostate (interface), but how do you store the state if not with some kind of Singletons (of which the static variable is nothing more than the simplest form) ? – Matthieu M. Aug 18 '10 at 12:02
  • Asked and answered: http://stackoverflow.com/questions/335369/finding-c-static-initialization-order-problems/335746 The singelton used in the destructor is easy to solve. Just use it in the constructor and it will be valid in the destructor (see link) – Martin York Aug 18 '10 at 12:45
  • @user152508: There is actually a valid solution below that is automatic. – Martin York Aug 19 '10 at 15:44

6 Answers6

3

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.

JoeG
  • 12,994
  • 1
  • 38
  • 63
  • 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 Aug 18 '10 at 08:03
  • Destruction order is not a problem any more than construction order is a problem. – Martin York Aug 18 '10 at 12:45
  • 1
    @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 Aug 19 '10 at 15:51
  • Martin - your solution merely moves the fragility and explicit demands on the programmer from the destructor to the constructor. – JoeG Aug 19 '10 at 19:34
1

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
  • 173,980
  • 10
  • 155
  • 350
1

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 Delroy
  • 102,968
  • 15
  • 177
  • 252
1

Copied from here: Finding C++ static initialization order problems (Nobody would have followed just a link sorry)

Also see this article: C++ Singleton design pattern

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.
        }
};
Community
  • 1
  • 1
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • @paulm: YES. That's the point. `A::getInstance_abc()` returns a valid reference to a good object in the constructor and thus the object is valid in the destructor. As described in detail by the comments. But Note. This is only an issue for global objects (static storage duration objects). Normal objects this does not even need to be considered. The problem only happens with global objects with constructor/destructor that does significant work and uses other objects. If you are doing this it is a sign your code is badly designed. – Martin York Nov 06 '14 at 19:05
0

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
  • 26,809
  • 3
  • 46
  • 82
0

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.
  • 287,565
  • 48
  • 449
  • 722
  • 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 Aug 18 '10 at 12:33
  • @Ben Voigt: right, I was so intent on the ALL that I forgot that. – Matthieu M. Aug 18 '10 at 12:47