当描述某个函数时,需要刻画其Thread-safety与Re-entrancy特征。那么,什么是Thread-safety和Re-entrancy呢?这正是本文要研究的内容。

1. Definitions

A function is thread-safe if:

it only manipulates shared data structures in a manner that guarantees safe execution by multiple threads at the same time.

A function is reentrant if:

it can be interrupted at any point during its execution and then safely called again (“re-entered”) before its previous invocations complete execution.
The interruption could be caused by an internal action such as a jump or call, or by an external action such as an interrupt or signal.

2. Examples

2.1 Example 1: not thread-safe, not reentrant

This is an example swap function that fails to be reentrant or thread-safe. Since the tmp variable is globally shared, without serialization, among any concurrent instances of the function, one instance may interfere with the data relied upon by another. As such, it should not have been used in the interrupt service routine isr():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int tmp;

void swap(int* x, int* y)
{
tmp = *x;
*x = *y;
/* Hardware interrupt might invoke isr() here. */
*y = tmp;
}

void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}

2.2 Example 2: thread-safe, not reentrant

The function swap() in the preceding example can be made thread-safe by making tmp thread-local. It still fails to be reentrant, and this will continue to cause problems if isr()is called in the same context as a thread already executing swap():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
_Thread_local int tmp;

void swap(int* x, int* y)
{
tmp = *x;
*x = *y;
/* Hardware interrupt might invoke isr() here. */
*y = tmp;
}

void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}

2.3 Example 3: not thread-safe, reentrant

The following modification of the swap function, which is careful to leave the global data in a consistent state at the time it exits, is reentrant; however, it is not thread-safe, since there are no locks employed, it can be interrupted at any time:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int tmp;

void swap(int* x, int* y)
{
/* Save global variable. */
int s;
s = tmp;

tmp = *x;
*x = *y;
*y = tmp; /* Hardware interrupt might invoke isr() here. */

/* Restore global variable. */
tmp = s;
}

void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}

2.4 Example 4: thread-safe, reentrant

An implementation of swap() that allocates tmp on the stack instead of globally and that is called only with unshared variables as parameters is both thread-safe and reentrant. Thread-safe because the stack is local to a thread and a function acting just on local data will always produce the expected result. There is no access to shared data therefore no data race.

1
2
3
4
5
6
7
8
9
10
11
12
13
void swap(int* x, int* y)
{
int tmp;
tmp = *x;
*x = *y;
*y = tmp; /* Hardware interrupt might invoke isr() here. */
}

void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}

参考资料:

  1. Threadsafe vs re-entrant
  2. https://en.wikipedia.org/wiki/Thread_safety
  3. https://en.wikipedia.org/wiki/Reentrancy_(computing)