4

E.g., can the following code ever print "3" for one of the threads?

int foo()
{
   static int a = 1;
   return ++a;
}

void thread1()
{
   cout<<foo()<<endl;
}

void thread2()
{
   cout<<foo()<<endl;
}

edit: It's C++ 98

TravisG
  • 2,373
  • 2
  • 30
  • 47

2 Answers2

8

Of course it can print 3. It is even the "usual semantics" of this code to do so. Thread 1 initializes it with 1 and increments it, so it is 2. Thread 2 increments it again, so it is 3.

So, yes, scoped static variables are static, i.e., global variables. They are shared by threads.

Of course, the code has a race condition, so the result can possibly be anything, but 3 is a possible result.

gexicide
  • 38,535
  • 21
  • 92
  • 152
  • 1
    The code has undefined behaviour (due to the race condition in `++a`), so there are no "usual semantics". – Mankarse Oct 18 '12 at 10:24
  • still, you will get 3 on most compilers in most situations. But you are right, "usual semantics" is a quite strange word which is not defined anyway, so I placed it in quotes now... Btw: In C++11, the semantics of a program with race conditions is not fully undefined. – gexicide Oct 18 '12 at 10:25
  • Race conditions are definitely fully undefined -- see `[intro.multithread]/21`: `The execution of a program contains a data race if it contains two conflicting actions in dierent threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.` – Mankarse Oct 18 '12 at 11:53
3

local static variables are shared between threads.

Initialisation of function-local static variables is thread-safe in C++11 (before that, threads did not even exist ;)).

Modification of function-local static variables, on the other hand, is not thread-safe, so your modified code has undefined behaviour (due to the race condition).

Mankarse
  • 39,818
  • 11
  • 97
  • 141
  • So they're not shared? If I let one thread wait a second and let the other execute first, the waiting thread will still print 2? – TravisG Oct 18 '12 at 10:18
  • 1
    The key here is "C++11". Is the OP using C++11? He didn't mention it. I think we're still at the point where somebody says if they're using C++11, as most people and most production environments are not. And then this initialisation is absolutely _not_ thread-safe. – Lightness Races in Orbit Oct 18 '12 at 10:19
  • @TravisG *"So they're not shared?"* - No, they **are** shared. It's only the initialization that is thread-safe (I don't know this, but I believe *Mankarse* here), so you won't have two threads trying to initialize the variable. The variable itself **is** shared and even if you synchronize the access, you still have one thread printing 2 and the other 3. And without synchronization it's UB, anyway, as said in the answer. – Christian Rau Oct 18 '12 at 10:59
  • @LightnessRacesinOrbit, good compilers have had thread-safe init of statics for years, long before C++11 - you can't say "absolutely" whether the init is safe – Jonathan Wakely Oct 18 '12 at 11:23
  • @JonathanWakely: GCC, yes. VS as of 2009, no. Perhaps it does now. In C++ itself? Absolutely not. Writing code for anything other than a specific version of a specific toolchain on a specific platform? Don't rely on it. [Citation for this topic](http://stackoverflow.com/a/1962918/560648) – Lightness Races in Orbit Oct 18 '12 at 11:51
  • @ChristianRau: The initialisation is thread-safe **only in C++11**, which the OP is _not_ using. For C++98, it is only "_possibly_ thread-safe in practice" which is a very different thing. – Lightness Races in Orbit Oct 18 '12 at 11:53
  • @LightnessRacesinOrbit, GCC is the default compiler on more than "a specific platform" and the OP doesn't say which platform(s) are being used, and it doesn't apply only to specific versions of GCC, or even to specific toolchains - Clang supports it too. As I said, good compilers support it, if yours doesn't then change it. But you cannot say "absolutely" one way or the other. Lots of people have been able to rely on thread-safe init of local statics for years, so your comment is not "absolutely" accurate. – Jonathan Wakely Oct 18 '12 at 11:54
  • 1
    @JonathanWakely: In the real world, you can't just "change it" because your code relies on thread-safe function-static initialisation instead of using the appropriate locks suggested by the lack of an implementation guarantee. In the real world, your choice of toolchain comes down to many, many more factors than that and switching toolchains potentially has significant consequences elsewhere. Lots of people have been able to rely on thread-safe init if, like I said, they know for a fact that their version of GCC makes it safe -- I suspect many only _think_ that they can rely on it. – Lightness Races in Orbit Oct 18 '12 at 12:01
  • @LightnessRacesinOrbit *"The initialisation is thread-safe only in C++11"* - Yes, I said nothing contrary and relied on the fact that *Mankarse* **already clearly stated that** in his answer, disregarding of the fact if it is what the OP uses. But you're right in that it doesn't help the OP much and I also understand (and support) your sentiments against relying on the correct implementation-defined behaviour of one certain compiler (which may be the most widely used, but still is not the only one, and VC is definitely not a niche compiler either). – Christian Rau Oct 18 '12 at 12:19