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.
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:
do_stuff
until the point return 0
to caller.1
to x
, but before it does..:do_stuff
.x
, it is still 0
, so it returns 0
to the caller and then increases x
by 1
. x
is now 1
.1
to x
so that's what it does.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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With