Threadsafe vs re-entrant

Alphaneo picture Alphaneo · May 13, 2009 · Viewed 42k times · Source

Recently, I asked a question, with title as "Is malloc thread safe?", and inside that I asked, "Is malloc re-entrant?"

I was under the impression that all re-entrant are thread-safe.

Is this assumption wrong?

Answer

MiniQuark picture MiniQuark · Oct 30, 2015

TL;DR: A function can be reentrant, thread-safe, both or neither.

The Wikipedia articles for thread-safety and reentrancy are well worth reading. Here are a few citations:

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.

As examples of possible reentrance, the Wikipedia gives the example of a function designed to be called by system interrupts: suppose it is already running when another interrupt happens. But don't think you're safe just because you don't code with system interrupts: you can have reentrance problems in a single-threaded program if you use callbacks or recursive functions.

The key for avoiding confusion is that reentrant refers to only one thread executing. It is a concept from the time when no multitasking operating systems existed.

Examples

(Slightly modified from the Wikipedia articles)

Example 1: not thread-safe, not reentrant

/* As this function uses a non-const global variable without
   any precaution, it is neither reentrant nor thread-safe. */

int t;

void swap(int *x, int *y)
{
    t = *x;
    *x = *y;
    *y = t;
}

Example 2: thread-safe, not reentrant

/* We use a thread local variable: the function is now
   thread-safe but still not reentrant (within the
   same thread). */

__thread int t;

void swap(int *x, int *y)
{
    t = *x;
    *x = *y;
    *y = t;
}

Example 3: not thread-safe, reentrant

/* We save the global state in a local variable and we restore
   it at the end of the function.  The function is now reentrant
   but it is not thread safe. */

int t;

void swap(int *x, int *y)
{
    int s;
    s = t;
    t = *x;
    *x = *y;
    *y = t;
    t = s;
}

Example 4: thread-safe, reentrant

/* We use a local variable: the function is now
   thread-safe and reentrant, we have ascended to
   higher plane of existence.  */

void swap(int *x, int *y)
{
    int t;
    t = *x;
    *x = *y;
    *y = t;
}