Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

G++ gets order of destruction of static variables wrong

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?

like image 216
Yuvraj Dhillon Avatar asked May 02 '12 20:05

Yuvraj Dhillon


1 Answers

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.

like image 193
n. 1.8e9-where's-my-share m. Avatar answered Oct 20 '22 03:10

n. 1.8e9-where's-my-share m.