Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding out whether static initialization is over

The abridged problem (Y)

Suppose you need to know, from within a function, whether this function has been called as part of the initialization of a static object, or not. Is there a standard or platform-specific way to do so?

The backstory (X)

I'm knee-deep into the source code of a DLL that is used by many applications. This DLL exposes an Init function, and this function should construct a boost::asio::deadline_timer to be used later (but the DLL can work without it in a degraded mode).

The issue is that the timer cannot be constructed during the initialization of static objects (DLL load time), lest its constructor deadlock.

Of course, everyone and their cat calls Init from everywhere (yes, multiple times!), including static constructors from source code I shan't edit, hence the need to detect that situation and bail out.

After pragmatism overcame disgust, I ended up walking up the callstack to try and find wWinMain and deduce that static initialization is over. It's awful and doesn't work with dynamically-loaded binaries, which thankfully was outside the scope of my particular case. I sure wish there were a cleaner way.

like image 252
Quentin Avatar asked Nov 22 '17 16:11

Quentin


1 Answers

For the moment, let's assume your DLL has two entry points: Init and Frobnicate.

Init effectively does nothing. Frobnicate checks a global boolean to find out if the DLL has ever really initialized. If not, it first does the real initialization, which also sets the I've-really-initialized flag, before actually frobnicating.

Likely, you have more entry points (Frobnicate2, Fronbnicate3, ...), so you'd have to add this logic into every one of those. This is tedious but not unheard of. Back in the day, we used to have to write our own delay-load mechanisms, which were very similar. Of course, we only presented C-style interfaces from DLLs back then, not C++-style objects with mangled method names, but it must still be do-able.

This assumes that it's OK to delay initialization until the first non-Init call into the DLL. That may or may not be a good assumption.

Other hacky ideas:

  • Find a way to detect the loader lock (other than actually deadlocking). If the loader lock is held when Init is called, then this is one of those "too soon" moments. This is on par with your walk-the-stack-to-find-WinMain solution.
  • Do something that guarantees a deadlock when Init is called too soon, and make your clients fix their damn code.
  • Create a hidden message-only window and post a message to it. Assuming the client is a traditional GUI app that will pump messages on the same thread, then, by the time you receive the message, it should be safe to do real initialization (and, hopefully, not too late).
like image 94
Adrian McCarthy Avatar answered Sep 28 '22 02:09

Adrian McCarthy