Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HOW are local static variables thread unsafe in c?

So looking around the internet, I couldn't find consistent and helpful information about this. So here's the issue:

Why are local static variables in C said to be thread-unsafe? I mean, static local variables are stored in the data segment, which is shared by all threads, but isn't internal linkage supposed to stop threads from stepping in each other's static variables?

This forum post seems to suggest that threads do in fact step in each other's data segment occasionally, but wouldn't such behavior clearly violate all c standards since the 90'? If such behavor were to be expected, wouldn't use of the data segment (i.e. all variables with static storage duration, including global variables) have been made deprecated long ago in the successive c standards?

I really don't get this, since everyone seems to have something against local static variables, but people can't seem to agree on why, and researching some of the argument shows them to be ill-conceived.

I, for one, think local static variables are a very good way to communicate information between function calls, that can really improve readability and limit scope (compared to, say, passing the information as arguments forth and writing it back on each function call).

As far as I can see, there are completely legitimate uses of local static variables. But maybe I am missing something? I would really like to know if that were the case.

[EDIT]: The answers here were pretty helpful. Thanks to everyone for the insight.

like image 506
FaresGargouri Avatar asked Jun 11 '18 10:06

FaresGargouri


2 Answers

but isn't internal linkage supposed to stop threads from stepping in each other's static variables?

No, linkage has nothing to do with thread safety. It merely restricts functions from accessing variables declared in other scopes, which is a different and unrelated matter.

Lets assume you have a function like this:

int do_stuff (void)
{
  static int x=0;
  ...
  return x++;
}

and then this function is called by multiple threads, thread 1 and thread 2. The thread callback functions cannot access x directly, because it has local scope. However, they can call do_stuff() and they can do so simultaneously. And then you will get scenarios like this:

  • Thread 1 has executed do_stuff until the point return 0 to caller.
  • Thread 1 is about to write value 1 to x, but before it does..:
  • Context switch, thread 2 steps in and executes do_stuff.
  • Thread 2 reads x, it is still 0, so it returns 0 to the caller and then increases x by 1.
  • x is now 1.
  • Thread 1 gets focus again. It was about to store 1 to x so that's what it does.
  • Now x is still 1, although if the program had behaved correctly, it should have been 2.

This gets even worse when the access to x is done in multiple instructions, so that one thread reads "half of x" and then gets interrupted.

This is a "race condition" and the solution here is to protect x with a mutex or similar protection mechanism. Doing so will make the function thread-safe. Alternatively, do_stuff can be rewritten to not use any static storage variables or similar resources - it would then be re-entrant.

like image 158
Lundin Avatar answered Oct 14 '22 09:10

Lundin


isn't internal linkage supposed to stop threads from stepping in each other's static variables?

Linkage has nothing to do with concurrency: internal linkage stops translation units, not threads, from seeing each other's variables.

I, for one, think local static variables are a very good way to communicate information between function calls, that can really improve readability and limit scope

Communicating information between calls through static variables is not too different from communicating information through globals, for the same reasons: when you do that, your function becomes non-reentrant, severely limiting its uses.

The root cause of the problem is that read/write use of variables with static linkage transforms a function form stateless to stateful. Without static variables any state controlled by the function must be passed to it from the outside; static variables, on the other hand, let functions keep "hidden" state.

To see the consequences of keeping a hidden state, consider strtok function: you cannot use it concurrently, because multiple threads would step on each other's state. Moreover, you cannot use it even from a single thread if you wish to parse each token from a string that is currently being parsed, because your second-level invocation would interfere with your own top-level invocation.

like image 37
Sergey Kalinichenko Avatar answered Oct 14 '22 08:10

Sergey Kalinichenko