Understanding Reentrant vs Thread‑Safe Functions in Unix/Linux
This article explains the concepts of reentrant and thread‑safe functions, their differences, how signals affect them, examples of non‑reentrant scenarios, and practical guidelines for writing safe code in C on Unix-like systems.
When a signal interrupts a process, the normal instruction flow is temporarily taken over by a signal handler, which runs its own code before returning to the original sequence. If the handler calls functions that are not reentrant, such as malloc or functions using static data, the process state can become corrupted.
What Is a Reentrant Function?
A reentrant function can be interrupted at any point and safely resumed later, because it does not rely on shared static data or external state; it uses only its own stack variables or protects any global data it accesses. Reentrancy is a stricter subset of thread safety.
Thread‑Safe Functions
A function is thread‑safe if it can be called concurrently by multiple threads and still produce correct results. Many thread‑safe functions achieve this by using locks or by avoiding shared mutable state. However, a thread‑safe function is not necessarily reentrant, because it may still be unsafe to call from a signal handler.
Key Differences and Relationship
All reentrant functions are thread‑safe, but not all thread‑safe functions are reentrant.
Reentrant functions are also async‑signal‑safe, meaning they can be safely invoked from a signal‑catching routine.
POSIX defines three categories: Reentrant, Thread‑Safe, and Async‑Signal‑Safe, with the first being a subset of the other two.
Common Non‑Reentrant Scenarios
Using static data structures (e.g., getpwnam, getpwuid) – a signal handler calling the same function can overwrite the previous result.
Calling malloc or free inside a handler while the interrupted code is already manipulating the heap’s allocation list.
Standard I/O functions that rely on global buffers (e.g., printf).
Invoking longjmp / siglongjmp from a handler, which can leave data structures partially updated.
Handling errno in Signal Handlers
Since errno is a per‑thread variable, a reentrant function may still modify it. The recommended practice is to save errno at the start of the handler and restore it before exiting.
Signal Masking Techniques
signal(SIGPIPE, SIG_IGN); // ignore specific signals</code>
<code>sigprocmask(); // for single‑threaded programs</code>
<code>pthread_sigmask(); // for multithreaded programsPractical Example of a Thread‑Safe but Non‑Reentrant Function
Consider a function func() that locks a shared resource before use. If a signal arrives while the lock is held and the handler also calls func(), the handler will block on the same lock, leading to a deadlock because the original thread cannot release the lock while the signal handler runs.
Summary
Determine reentrancy by checking whether a function can be interrupted and still produce correct results.
Thread safety ensures correct concurrent execution but does not guarantee safety inside signal handlers.
A function that is reentrant is both thread‑safe and async‑signal‑safe; thread‑safe alone is insufficient for signal‑handler use.
POSIX provides explicit async‑signal‑safe versions of many functions (e.g., gethostbyname_r).
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
