Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Thread Safety & Reentrancy
OS

Thread Safety & Reentrancy

Understanding thread-safe functions, reentrant vs non-reentrant code, and Thread-Local Storage.

A function is thread-safe if it works correctly when called simultaneously by multiple threads. A function is reentrant if it can be interrupted (e.g., by a signal handler) and called again before the first call completes.

Thread Safety Levels

LevelDescriptionExample
UnsafeUses shared state without synchronizationA global counter without a lock
Conditionally safeSafe only if called in specific patternsstrtok() — unsafe, uses internal state
Thread-safeUses synchronization (mutex, atomic) to protect shared statepthread_mutex_lock around critical section
ReentrantNo shared state at all — pure function or thread-local state onlystrtok_r() (reentrant version)

Reentrancy

A function is reentrant if:

  • It uses only local variables (no static/global state)
  • It does not modify its own code (self-modifying code)
  • It does not call non-reentrant functions
// NOT reentrant — uses global state
int counter = 0;
int next_id() {
    return counter++;  // interrupted here → bad
}

// Reentrant — caller provides state
int next_id_r(int *counter) {
    return (*counter)++;
}

Thread-Safe vs Reentrant

Thread-safeReentrant
Multiple threadsYesYes
Interrupted and re-enteredMaybeYes
Uses locksMaybeNever
Uses shared stateYes (with protection)No

A reentrant function is always thread-safe (no shared state to protect), but a thread-safe function is not necessarily reentrant (it might hold a mutex and deadlock if re-entered from a signal handler).

Making Code Thread-Safe

  1. Avoid global/static state — prefer parameters and return values
  2. Use mutexes to protect shared state
  3. Use atomic operations for simple counters/flags
  4. Use Thread-Local Storage (TLS) — each thread gets its own copy of a variable
// Thread-local: each thread has its own copy
__thread int thread_local_var;

// Now safe — no shared state
int get_id() {
    static __thread int id = 0;
    return id++;
}

Q: What is the difference between thread-safe and reentrant?

A: Thread-safe means the function works correctly with multiple concurrent calls using synchronization. Reentrant means the function can be interrupted and called again before the first invocation finishes — it must use no shared state and no locks. All reentrant functions are thread-safe, but not vice versa.

Q: Why is strtok() not thread-safe?

A: strtok() uses a static internal pointer to track the current position in the string. If two threads call it concurrently, they’ll corrupt this pointer. The reentrant version strtok_r() takes the state as a parameter — fully thread-safe.

Q: How does Thread-Local Storage (TLS) help?

A: TLS gives each thread its own copy of a variable. No shared state, no synchronization needed. The OS or compiler maps the TLS variable to different memory addresses for different threads.

Q: What happens when a non-reentrant function is called from a signal handler?

A: If the signal handler interrupts the program while it was executing the same function (e.g., malloc), you get undefined behavior — corruption, crash, or deadlock. This is why signal handlers should only call async-signal-safe functions.

My Private Notes

Notes are auto-saved locally to this device.