10

I have the following classes that try to implement a generic Singleton.

struct BaseObject
{
   virtual ~BaseObject() {}
};
class _helper
{
private:
    template<typename T> friend class Singleton;
    set<BaseObject*> _s;
    static _helper& _get()
    {
        static _helper t;
        return t;
    }
    _helper()
    {
        cout<<" _helper ctor"<<endl;
    }
    ~_helper()
    {
        cout<<" _helper dtor"<<endl;
        //assert(_s.empty());
    }
};

// Singleton<foo>::Instance() returns a unique instance of foo
template <typename T>
class Singleton : virtual private T
{
public:
    static T& Instance()
    {
        static Singleton<T> _T;
        return _T;
    } 

private:
    Singleton()
    {
        cout<<"inserting into helper "<<typeid(T).name()<<" ptr "<<this<<endl;
        assert(!_helper::_get()._s.count(this));
        _helper::_get()._s.insert(this);
    }
    ~Singleton()
    {
        cout<<"erasing from helper "<<typeid(T).name()<<" ptr "<<this<<endl;
        assert(_helper::_get()._s.count(this));
        _helper::_get()._s.erase(this);
    }
};

Now if I call Singleton< bar>::Instance() followed by Singleton< foo>::Instance(), I should see the following output:

 inserting into helper 3bar ptr 0x509630  
 _helper ctor  
 inserting into helper 3foo ptr 0x509588  
 erasing from helper 3foo ptr 0x509588  
 erasing from helper 3bar ptr 0x509630  
 _helper dtor  

However in some cases, I see the following:

 inserting into helper 3bar ptr 0x509630  
 _helper ctor  
 inserting into helper 3foo ptr 0x509588  
 erasing from helper 3bar ptr 0x509630  
 _helper dtor  
 erasing from helper 3foo ptr 0x509588  

Note that in the second case, bar and foo got destructed in the same order as they were constructed. This seems to happen when the foo and bar singletons are instantiated inside a shared library(.so) as static references:

static bar& b = Singleton<bar>::Instance();   
static foo& f = Singleton<foo>::Instance();   

Any ideas why it would do that?

Ross Rogers
  • 23,523
  • 27
  • 108
  • 164
  • I don't think C++ makes any guarantees about the order in which destructors of static variables will be called ? – Paul R May 02 '12 at 20:24
  • 2
    Are they in the same translation unit? – GManNickG May 02 '12 at 20:25
  • 1
    I have to put in some several dollars of opinion here. This is an incorrect application of the singleton pattern. The singleton pattern should be strictly reserved to those objects that MUST be solitary within a program, not applied to some object that just happens to only exist once in the program. If you want a generic singleton pattern facility then you should inherit FROM it, rendering the object unable to exist in multiple instances. If you need a global, use one. Singleton != global variable. – Edward Strange May 02 '12 at 20:29
  • 4
    @PaulR: The standard mandates that the order of construction and destruction are reversed, and it also mandates that within a single translation unit the order of definition (for global statics) is maintained. For function statics, the order of construction is that of the calls to the functions, but the order of destruction shall still be the reverse of the order of construction. – David Rodríguez - dribeas May 02 '12 at 20:30
  • possible duplicate of [Destruction order of static objects in C++](http://stackoverflow.com/questions/469597/destruction-order-of-static-objects-in-c) – netcoder May 02 '12 at 20:31
  • 1
    @netcoder: I don't think this is a duplicate. Yuvraj seems to have a clear idea of what the order should be, and does not want to change it, but rather understand why he is getting that unexpected output. – David Rodríguez - dribeas May 02 '12 at 20:35
  • 1
    It works absolutely correct. Destruction in reverse order of construction. Absolutely as mentioned standard. Your Singleton::Instance() call creates helper object (first get() call), so the only guarantee, that helper will be destructed after Singleton. Nothing more. – johngull May 02 '12 at 20:36
  • @DavidRodríguez-dribeas: That first clause only holds for objects within the same scope. – GManNickG May 02 '12 at 20:37
  • 1
    Are you **manually** loading and unloading the shared library? Are you doing this in multiple threads? – Martin York May 02 '12 at 20:43
  • What is the reason for using _helper class? You want to count instances? But you will always have 1 instance for every class/typename. Maybe you can just throw it away? Or you have some other reason? – johngull May 02 '12 at 20:45
  • 1
    Note: It is worth putting the print statement as the last statement in the constructor as this helps you spot the correct order of construction easier (as the object is not considered constructed until the constructor completes). Thus you will see `_helper ctor` first. – Martin York May 02 '12 at 20:49
  • 5
    @GManNickG: The wording in C++11 is a bit more convoluted (due to thread static objects), but in C++03 3.6.3p1 is quite clear: *Destructors (12.4) for initialized objects of static storage duration [...] These objects are destroyed in the reverse order of the completion of their constructor or of the completion of their dynamic initialization.* No mention of the different translation units. The important piece of info is that while the order of construction is undefined across translation units, the order of destruction is guaranteed to be the inverse. – David Rodríguez - dribeas May 02 '12 at 20:50
  • ... rereading it, the quote is not *that* complex in C++11 3.6.3p1 *[...]If the completion of the constructor or dynamic initialization of an object with thread storage duration is sequenced before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first.[...]* – David Rodríguez - dribeas May 02 '12 at 20:52
  • 3
    @johngull: If you carefully read the last dump, you will see that the order is not consistent: `Singleton` constructed before `Singleton` and `Singleton` destructed before `Singleton`, while it should be (abbreviating): `S, S => ~S, ~S`. **That** is the inconsistency, for those runs the implementation does not seem to be producing the correct code for termination. – David Rodríguez - dribeas May 02 '12 at 20:54
  • @DavidRodríguez-dribeas: You're right of course, I'm not sure what I was thinking. – GManNickG May 02 '12 at 21:08
  • Also note the destruction of the singleton _helper object. I would have expected it to be the last object destructed (since it is the first object FULLY constructed), but that's not what happens in my second case. – Yuvraj Dhillon May 02 '12 at 21:57

2 Answers2

1

This can happen if the singletons and the helper are located in different translation units, or different shared objects. Bear in mind that it is rather difficult to predict in which translation unit a template instance will end up. Also remember that each shared object can get its own instance of say Singleton<foo>::_T. So you have some kind of per-shared-object singletons (not very useful IMHO).

Note that your helper is destroyed before the last object is removed from it. This will lead to program crash on exit. Yes this exact thing has happened to me. You will need to implement an object counter in the _helper class such that it is not destroyed until there's at least one object registered with it. Alternatively, allocate all singletons on the heap and let the helper destroy them when its lifetime ends.

update This destruction order reversal probably cannot happen if the two static objects are owned by the same dynamic library. It definitely can and does happen otherwise. Here programmers are advised against exporting static objects across dynamic library boundaries.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • 2
    Shouldn't happen even in that case. The order of construction across TU boundaries is undefined, but the standard mandates that the order of destruction is the reverse order of completion of the appropriate constructors (or dynamic initialization). That is, given two static objects `a` and `b` in two translation units, the standard requires one of two possible orders: `a, b, ~b, ~a` or `b, a, ~a, ~b` – David Rodríguez - dribeas May 02 '12 at 21:13
  • Exactly what I thought should be the case. – Yuvraj Dhillon May 02 '12 at 21:39
  • 2
    The standard says nothing about dynamically loaded libraries. In practice, when such a library is unloaded, all static objects that it "owns" are destroyed, regardless of how static objects in other .so/.dlls are constructed. – n. m. could be an AI May 03 '12 at 02:17
  • I did not use dlopen/dlclose to load my shared library. I would have thought that a regular .so usage would not have this problem. – Yuvraj Dhillon May 03 '12 at 16:21
  • It could possible in theory to have two different destruction sequences, one for `dlopen` and another for "regular" usage. But it's not done like that. I know, I was bit by this behaviour in production code. A static variable was moved to a different library and suddenly everything started to dump core on termination. – n. m. could be an AI May 03 '12 at 16:35
0

static _helper t; defines one address as far as I can tell.

static _helper& _get()
{
    static _helper t;
    return t;
}

But it looks like you're trying to use it for 2 distinct objects.

Anyway, I've never seen a template being used for a singleton. And in your case it looks like you're trying to destroy a singleton. That too I don't recall seeing before. A singleton is generally created once and stays around until you leave the program (and is still allocated when you leave.)

Otherwise, you may want to look into shared pointers or intrusive ref counted objects?

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156