18

I know that declaring a static variable within a function in C means that this variable retains its state between function invocations. In the context of threads, will this result in the variable retaining its state over multiple threads, or having a separate state between each thread?

Here is a past paper exam question I am struggling to answer:

The following C function is intended to be used to allocate unique identifiers (UIDs) to its callers:

get_uid() 
{
static int i = 0;
return i++;
}

Explain in what way get_uid() might work incorrectly in an environment where it is being called by multiple threads. Using a specific example scenario, give specific detail on why and how such incorrect behaviour might occur.

At the moment I am assuming that each thread has a separate state for the variable, but I am not sure if that is correct or if the answer is more to do with the lack of mutual exclusion. If that is the case then how could semaphores be implemented in this example?

Rodia
  • 1,407
  • 8
  • 22
  • 29
dahui
  • 2,128
  • 2
  • 21
  • 40
  • Your final question, you asked how semaphores can be *implemented* in this example. DO you mean to ask how this could be implemented *using* semaphores? – WhozCraig Apr 07 '13 at 01:08
  • 1
    If you are using gcc, a nice addition to the standard are the static __thread variables. They are static variables, but local to each thread. I find it especially useful for managing thread-local connections to a server... – user1251840 Sep 01 '14 at 12:06

6 Answers6

24

Your assumption (threads have their own copy) is not correct. The main problem with code is when multiple threads call that function get_uid(), there's a possible race condition as to which threads increments i and gets the ID which may not be unique.

P.P
  • 117,907
  • 20
  • 175
  • 238
  • Right, so two threads entering the function at the same time will increment i twice and then return with the same value? – dahui Apr 07 '13 at 01:11
  • There are a lot of other possibilities as well. The increment could be in the process of happening, and you end up with what would appear like a totally random value in i. – kanielc Feb 01 '14 at 00:48
17

All the threads of a process share the same address space. Since i is a static variable, it has a fixed address. Its "state" is just the content of the memory at that address, which is shared by all the threads.

The postfix ++ operator increments its argument and yields the value of the argument before the increment. The order in which these are done is not defined. One possible implementation is

copy i to R1
copy R1 to R2
increment R2
copy R2 to i
return R1

If more than one thread is running, they can both be executing these instructions simultaneously or interspersed. Work out for yourself sequences where various results obtain. (Note that each thread does have its own register state, even for threads running on the same CPU, because registers are saved and restored when threads are switched.)

A situation like this where there are different results depending on the indeterministic ordering of operations in different threads is called a race condition, because there's a "race" among the different threads as to which one does which operation first.

Jim Balter
  • 16,163
  • 3
  • 43
  • 66
5

No, if you want a variable which value depends upon the thread in which it is used, you should have a look at Thread Local Storage.

A static variable, you can imagine it really like a completely global variable. It's really much the same. So it's shared by the whole system that knows its address.

EDIT: also as a comment reminds it, if you keep this implementation as a static variable, race conditions could make that the value i is incremented at the same time by several threads, meaning that you don't have any idea of the value which will be returned by the function calls. In such cases, you should protect access by so called synchronization objects like mutexes or critical sections.

Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
  • 1
    The code sample also suffers from non-atomic updates, which means multiple threads could essentially result in a single increment. – WhozCraig Apr 07 '13 at 01:04
1

Since this looks like homework, I'll answer only part of this and that is each thread will share the same copy of i. IOW, threads do not get their own copies. I'll leave the mutual exclusion bit to you.

John Szakmeister
  • 44,691
  • 9
  • 89
  • 79
  • It's not homework but revision for exams! I will probably learn more from working it out myself though, so thanks. – dahui Apr 07 '13 at 01:11
1

Each thread will share the same static variable which is mostly likely a global variable. The scenario where some threads can have wrong value is the race condition (increment isn't done in one single execution rather it is done in 3 assembly instructions, load, increment, store). Read here and the diagram at the link explains it well.

Race Condition

nommyravian
  • 1,316
  • 2
  • 12
  • 30
1

If you are using gcc you can use the atomic builtin functions. I'm not sure what is available for other compilers.

int get_uid() 
{
    static int i = 0;
    return __atomic_fetch_add(&i, 1, __ATOMIC_SEQ_CST);
}

This will ensure that the variable cannot be acted on by more than one thread at a time.

Sam
  • 5,416
  • 7
  • 47
  • 49