Reputation: 3141
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.
Upvotes: 3
Views: 1648
Reputation: 264699
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
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.
}
};
Upvotes: 1
Reputation: 300349
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 ? ).
Upvotes: 0
Reputation: 106246
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).
Upvotes: 1
Reputation: 13192
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:
weak_ptr
- they can be destroyed independently of each other and you can safely check if a referenced singleton still exists before using itAlso, 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.
Upvotes: 3
Reputation: 27385
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.
Upvotes: 0
Reputation: 180245
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.
Upvotes: 1