The difference between thread-safety and re-entrancy

An operation is "thread-safe" if it can be performed from multiple threads safely, even if the calls happen simultaneously on multiple threads.

An operation is re-entrant if it can be performed while the operation is already in progress (perhaps in another context). This is a stronger concept than thread-safety, because the second attempt to perform the operation can even come from within the same thread.

Consider the following function:

int length = 0;
char *s = NULL;

// Note: Since strings end with a 0, if we want to
// add a 0, we encode it as "\0", and encode a
// backslash as "\\".

// WARNING! This code is buggy - do not use!

void AddToString(int ch)
  // +1 for the character we're about to add
  // +1 for the null terminator
  char *newString = realloc(s, (length+1) * sizeof(char));
  if (newString) {
    if (ch == '\0' || ch == '\\') {
      AddToString('\\'); // escape prefix
    newString[length++] = ch;
    newString[length] = '\0';
    s = newString;

This function is thread-safe because the critical section prevents two threads from attempting to add to the string simultaneously. However, it is not re-entrant.

The internal call to AddToString occurs while the data structures are unstable. At the point of the call, execution re-enters the start of the function AddToString, but this time the attempt to realloc the memory will use a pointer (s) that is no longer valid. (It was invalidated by the call to realloc performed by the caller.)

Comments (19)
  1. Serge Wautier says:

    2nd reason why it’s not re-entrant is that it would ‘loop’ forever when ch== 0 or

  2. Cooney says:

    Nah, eventually it’ll crash in some difficult to debug way.

  3. orcmid says:

    Hi Raymond, I like your clean difference between re-entrant and thread-safe. Thanks.

  4. Raymond Chen says:

    (Rats, missed the infinite loop on . Okay, so remove the test against and then you’re set.)

  5. And of course re-entrancy is an interesting issue with STA COM components, especially as if you don’t think you have to worry about it ‘cos you’re in an STA ;)

  6. Callek says:

    I can only assume that "someCriticalSection" is a global variable of some sort, whereas quite obviously, "EnterCriticalSection" blocks re-entrancy from other threads.

    It is fairly obvious but not quite as obvious as it could be.

  7. Sea Urchin says:

    Do I understand it correctly if I believe that re-entrant being stronger than thread-safe means that a re-entrant operation is automagically thread-safe?

    Or can an operation be re-entrant and not thread-safe at the same time?

  8. asdf says:

    A function can be reentrant but not thread safe. There are two types of reentrancy issues, if it is reentrant by the same thread only or if it is reentrant by multiple threads. The latter of the two is the strongest one (and makes it automagically thread safe). For example:

    void foo(unsigned i)


    if (i–) {





    If malloc and free access a global heap and are not protected by any locks, this function is reentrant but not multiple-thread reentrant.

  9. Keith Moore [exmsft] says:

    Another source of re-entrancy is user-mode APCs. Any routine that enters an alertable wait (calls SleepEx() or WaitForXxxxEx() with Alertable == TRUE) can potentially cause queued APCs to run.


    ReadFileEx( …, &MyCompletionRoutine );


    /* etc */





  10. Pavel Lebedinsky says:

    This is one of the reasons why critical sections, mutexes etc. shouldn’t allow recursive locks (at least not by default).

    A deadlock is always better than undefined behavior.

  11. josh says:

    Even if this were reentrant, it wouldn’t work. It’s impossible to have length correct for the AddToString(‘\’) at that point.

    This is reentrant (assuming it’s never called from an interrupt) but not thread safe:

    void AddToString(int ch)


    char *newString;

    if (ch == 0)


    newString = realloc(s, (length+1) * sizeof(char))

    if (newString)


    newString[length++] = ch;

    newString[length] = 0;

    s = newString;



  12. Is reentrancy mostly related to recursive functions? What are other scenarios besides alertable waits and interrupts?

  13. asdf says:

    A really common reentrant function that isn’t recursive is a window’s wndproc. The most common case besides sending messages back and forth between windows is if you spawn something that uses it’s own message loop like a modal dialog or popup window calls your wndproc while you’re already in your wndproc. Only if you’re doing something really dumb can you have problems with that case.

    If you put asserts (the kind that spawns message boxes) on certain messages in your wndproc, your program will just suddenly disapear because of stack overflow.

  14. You seem to suggest that re-entrant implies thread-safe. This is not true.

  15. Jonathan Allen says:

    "You seem to suggest that re-entrant implies thread-safe. This is not true."

    Example please.

  16. Chan Kar Heng says:

    Re-entrant but not thread-safe..

    char * strcopy(char * dst,const char * src)









  17. laughingcynic says:

    This is thread-safe.

    If you are going to bring up the example of 2 threads passing in the same dst, then the callers are behaving in an unsafe manner by failing to protect a shared buffer. The flaw is not with this routine.

  18. Raymond Chen says:

    Right, which demonstrates that the routine is not thread-safe and callers must respect that.

Comments are closed.