0

Thanks in advance. I'm now developing a plugin for a big system in C++. In my plugin I have some static variable. I find that when it is compiled in debug mode on Linux, it works fine without any problem. When it is compiled in RELEASE mode, namely some optimization is done by the compiler, then when I unload the plugin, the static variable is not deleted ( the destructor of the static variable class is never called.) so the memory is never released and next time when I reload the plugin, it causes the main program crash!

Can anybody explain me why the static variable is not destroyed when the plugin is unloaded? NOTE: the static variable is a static instance, not a pointer!

class MySettings
{
   public:
      static MySettings& Instance() {
         static MySettings theSingleton;
         return theSingleton;
      }

      virtual ~MySettings();
}

in the plugin somewhere, it is called like this

....
MySettings &s = MySettings::Instance();
s.xxx();
....

When I compiled and run in debug mode, I printed some information from the destructor, it looks like the instance is destructred properly, when the plugin is unpluged. But when I compile and run in release mode, the destructor is never called when the plugin is unpluged. I'm not the plugin manager developer, cannot tell too much about it. Thanks a lot for your help!

Here is the piece of code which loaded the plugin libs.

newLib._libHandle = ::dlopen(path_to_the_plugin_lib, RTLD_LAZY | RTLD_GLOBAL);
if(! newLib._libHandle) {
  cerr << "dlopen failed for: " << path << " - "
             << ::dlerror();
  return "";

I finally get it work. But still don't understand why. Here is what I did:

class MySettings
{
   public:
      static MySettings& Instance() {
         return theSingleton;
      }

   private:
      static MySettings theSingleton;
      virtual ~MySettings();
}

MySettings MySettins:theSingleton;

Sinece the application is very big with millions of lines of code. My doubt is that when gcc compiles in RELEASE mode, something goes wrong with the optimization.

  • class MySettings { public: static MySettings& Instance() { static MySettings theSingleton; return theSingleton; } virtual ~MySettings(); – user1073719 Nov 30 '11 at 16:17
  • Edit the question and add this information there! – karlphillip Nov 30 '11 at 16:22
  • 1
    That depends on how the plugin is loaded. Plugin for what? – BЈовић Nov 30 '11 at 16:26
  • For testing purposes I would use a pointer instead of reference, and then delete it when the plugin is no longer needed. Is there any reason for you not be using a pointer? – karlphillip Nov 30 '11 at 16:31
  • No specific reason to use a reference. Yes when I use a pointer, and then delete in when my plugin is unloaded, it does work well. What I don't understand is why it is not destroyed when use a reference after the plugin is unloaded. Thanks a lot! – user1073719 Nov 30 '11 at 16:34
  • Can you give some explanation? Why I cannot use a singleton in plugin? Thanks a lot! – user1073719 Nov 30 '11 at 16:52
  • @user1073719: Has nothing to do with being a plugin. Singletons are bad in the general case. – Billy ONeal Nov 30 '11 at 16:53
  • Someone outside the plugin might be still holding a reference to the singleton, so when you unload the plugin the destructor is not called because the object is still in memory. – karlphillip Nov 30 '11 at 16:58
  • @Billy can you provide a reference to that statement? I got interested. Thanks – karlphillip Nov 30 '11 at 17:00
  • @karlphillip: Tons of places. See http://stackoverflow.com/questions/3319434/singleton-pattern or http://stackoverflow.com/search?q=singleton – Billy ONeal Nov 30 '11 at 17:02
  • I'm quiet sure the singleton is not reference from outside of my plugin, it is 'visible' only my plugin. I observed that if run in debug, the destructor is called properly. but if run in RELEASE mode, obviously, the compiler does some magic thing, the destructor is not called when unloaded. Thank you all for the answers and comments. – user1073719 Nov 30 '11 at 17:04
  • Does the same problem occur with `RTLD_LOCAL` instead of `RTLD_GLOBAL`? – NPE Nov 30 '11 at 17:05
  • @Aix I cannot change the flags for dlopen. It is not under my control. And also, my plugin has several libs, there is dependence among them, so I think RTLD_GLOBAL is proper here. Thanks. – user1073719 Nov 30 '11 at 17:19
  • Based on your response to my answer, it sounds like your problems are garden-variety memory corruption, rather than anything related to reloading and/or dlopen(). Can you put your program under valgrind, or your platform's equivalent? – David Seiler Dec 05 '11 at 20:17
  • @David, Thanks a lot. I did some further work and found that when unloading my plugin, one of the libs is not unloaded properly, dlclose returns -1, and the error is: **"A dynamic linking error occurred: libAnotherLibInMyPlugin.so: undefined symbol: _ZN22MySettingsD1Ev)"** MySettings is used in **libAnotherLibInMyPlugin**. But MySettings is in **libMySettingsLib.so**, which is unloaded without error. – user1073719 Dec 06 '11 at 15:48
  • Then I checked libMySettingsLib.so with **nm**. It does show the symbol: `code` 00000000002a8a0 T _ZN22MySettingsC1Ev 000000000002dab0 T _ZN22MySettingsC2Ev 000000000002d4a0 T _ZN22MySettingsD0Ev 000000000002d7a0 T _ZN22MySettingsD1Ev 000000000002e4b0 T _ZN22MySettingsD2Ev `code` – user1073719 Dec 06 '11 at 15:49
  • Here is the demangled of the above part: `code 000000000002a8a0 T MySettings::MySettings() 000000000002dab0 T MySettings::MySettings() 000000000002d4a0 T MySettings::~MySettings() 000000000002d7a0 T MySettings::~MySettings() 000000000002e4b0 T MySettings::~MySettings() code` – user1073719 Dec 06 '11 at 15:49
  • From the symbol table, I see the **undefined symbol" from the plugin unloading error is the **destructor**, obvisously, it is there in the table. But the problem is that **the class only has a default constructor, why it shows two: one at **00000000002a8a0 T _ZN22MySettingsC1Ev** and another at **000000000002dab0 T _ZN22MySettingsC2Ev**. Another confusion is why there are three destructors mangled in this symbol table: `code ** 000000000002d4a0 _ZN22MySettingsD0Ev", ** ** 000000000002d7a0 T _ZN22MySettingsD1Ev, ** ** 000000000002e4b0 T _ZN22MySettingsD2Ev ** `code Is this an compiler bug? – user1073719 Dec 06 '11 at 15:49
  • Just googled and find that the three mangled destructors is OK. Don't understand why it cannot find the mangled destructor symbol. – user1073719 Dec 06 '11 at 16:11
  • Not sure; C++ linking issues are very much outside my expertise. I'd dig in some more, narrow it down as much as you can, and post a new question. – David Seiler Dec 06 '11 at 19:02

1 Answers1

1

I've never tried this myself, the documentation seems to specify that static variables should be "reinitialized" upon reload. It's not at all obvious to me how that interacts with C++'s pre-main() hooks. You can try to understand that (check your vendor's documentation, or just open up the binary and look), but it's probably simpler to redesign. Some ideas:

If you can, get rid of your singletons. As mentioned in the comments, the industry consensus these days is that most of the time, the singleton pattern is more trouble than it's worth - as you are finding! In fairness, you wouldn't have this problem in Java or Ruby, but still.

If you're compiling with gcc, you may be able to register some hooks for "before dlopen() returns" and "after dlclose() is called". From the docs again:

...libraries should export routines using the __attribute__((constructor)) and __attribute__((destructor)) function attributes. See the gcc info pages for information on these. Constructor routines are executed before dlopen() returns, and destructor routines are executed before dlclose() returns."

I'm pretty sure this is gcc-specific; if you're not using gcc, your platform may offer something similar.

If you can't do that, try switching to the "initialize on first use" style of singleton implementation. The idea is to detect in Instance() whether your MySettings singleton has been created already, and create it first if it hasn't. Something like:

static MySettings* theSingleton = NULL;
if(theSingleton == NULL)
  theSingleton = new MySettings();
return *theSingleton;

Note that this version of Instance() is not thread-safe; if you want that you will have to go to some trouble. Also: theSingleton will never be deleted, so your problem will leak some memory/file descriptors/whatever every time your plugin is reloaded. Depending on what sorts of things you're keeping in MySettings, and how often you expect users to reload your plugin between process restarts, this may or may not be acceptable.

Hope this helps.

David Seiler
  • 9,557
  • 2
  • 31
  • 39
  • Thanks for the answer. I tried your method of creating the singleton, but ran into another problem. The constructor of MySettings does not initialize everything needed, some kind of lazy initialization. In my use case I need to set some initial values, so immediately after new MySettings(), I called the method to initialize some private members, which involves to allocate memory to them. then I got something like memory overflow, it crashes the application immediately. – user1073719 Dec 05 '11 at 19:34