views:

1476

answers:

6

I remember reading that static variables declared inside methods is not thread-safe.

Dog* MyClass::BadMethod()
{
  static Dog dog("Lassie");
  return &dog;
}

My library generates C++ code for end-users to compile as part of their application. The code it generates needs to initialize static variables in a thread-safe cross-platform manner. I'd like to use boost::call_once to mutex the variable initialization but then end-users are exposed to the Boost dependency.

Is there a way for me to do this without forcing extra dependencies on end-users?

+1  A: 

The only way I know of to guarantee you won't have threading issues with non-protected resources like your "static Dog" is to make it a requirement that they're all instantiated before any threads are created.

This could be as simple as just documenting that they have to call a MyInit() function in the main thread before doing anything else. Then you construct MyInit() to instantiate and destroy one object of each type that contains one of those statics.

The only other alternative is to put another restriction on how they can use your generated code (use Boost, Win32 threads, etc). Either of those solutions are acceptable in my opinion - it's okay to generate rules that they must follow.

If they don't follow the rules as set out by your documentation, then all bets are off. The rule that they must call an initialization function or be dependent on Boost is not unreasonable to me.

paxdiablo
+2  A: 

One way you could do it that does not require a mutex for thread safety is to make the singleton a file static, rather than function static:

static Dog dog("Lassie");
Dog* MyClass::BadMethod()
{
  return &dog;
}

The Dog instance will be initialised before the main thread runs. File static variables have a famous issue with the initialisation order, but as long as the Dog does not rely on any other statics defined in another translation unit, this should not be of concern.

1800 INFORMATION
I recall that there may be problems if you have global initialisers in DLLs on windows -- but i can't recall specifics, and it's really only relevant if the code is in a DLL anyway :D
olliej
My code *does* reside in a DLL :) What problems should I be aware of?
Gili
darned if I know. My best guess is the constructor will run once for every time the dll is loaded, so if the dll is unloaded and reloaded it will be recreated so it kind of breaks the Singleton "contract" but not much you can do about that
1800 INFORMATION
The way i've dealt with it in the past is with function scoped globals as you did originally but obvoiously there are race issues :-/
olliej
the function scope static is thread-safe in gcc and c++0x.
caspin
+1  A: 

You are correct that static initialization like that isn't thread safe (here is an article discussing what the compiler will turn it into)

At the moment, there's no standard, thread safe, portable way to initialize static singletons. Double checked locking can be used, but you need potentially non-portable threading libraries (see a discussion here).

Here's a few options if thread safety is a must:

  1. Don't be Lazy (loaded): Initialize during static initialization. It could be a problem if another static calls this function in it's constructor, since the order of static initialization is undefined(see here).
  2. Use boost (as you said) or Loki
  3. Roll your own singleton on your supported platforms (should probably be avoided unless you are a threading expect)
  4. Lock a mutex everytime you need access. This could be very slow.

Example for 1:

// in a cpp:
namespace {
    Dog dog("Lassie");
}

Dog* MyClass::BadMethod()
{
  return &dog;
}

Example for 4:

Dog* MyClass::BadMethod()
{
  static scoped_ptr<Dog> pdog;
  {
     Lock l(Mutex);
     if(!pdog.get())
       pdog.reset(new Dog("Lassie"));
  }
  return pdog.get();
}
Todd Gardner
+1  A: 

AFAIK, the only time this has been done safely and without mutexes or prior initialisation of global instances is in Matthew Wilson's Imperfect C++, which discusses how to do this using a "spin mutex". I'm not near to my copy of it, so can't tell you any more precisely at this time.

IIRC, there are some examples of the use of this inside the STLSoft libraries, though I can't remember which components at this time.

dcw
+1  A: 

Not sure whether this is what you mean or not, but you can remove the boost dependency on POSIX systems by calling pthread_once instead. I guess you'd have to do something different on Windows, but avoiding that is exactly why boost has a thread library in the first place, and why people pay the price of depending on it.

Doing anything "thread-safely" is inherently bound up with your threads implementation. You have to depend on something, even if it's only the platform-dependent memory model. It is simply not possible in pure C++03 to assume anything at all about threads, which are outside the scope of the language.

Steve Jessop
A: 

This article on CodeGuru discusses implementing thread-safe function static variables : Function Static Variables in Multi-Threaded Environments.

Dan Berindei