Reentrant code can be interrupted mid-execution and safely called again before the previous invocation completes. The term originated in operating systems design, where interrupt handlers must handle new interrupts while processing previous ones.

A function is reentrant when it relies only on its parameters and local variables, avoiding static variables, global state, or unprotected shared resources. This property emerges from careful constraint: reentrant code cannot modify itself, cannot call non-reentrant functions, and must treat all external state as potentially hostile.

The distinction matters most at system boundaries. Thread Safety ensures correctness under Concurrency, but reentrancy goes further—it permits interruption at any instruction. Signal handlers in Unix systems must be reentrant because signals can arrive during any function call. Distributed Systems face analogous challenges when processing concurrent requests that may observe partially-updated state.

Parallelism exposes reentrancy violations through race conditions, but the root cause differs from typical thread safety bugs. A function using a static buffer for formatting strings fails reentrancy even without threads—recursive calls corrupt each other’s data. The Global Interpreter Lock cannot protect against this because the corruption happens within a single execution context.

Modern Ruby Concurrency Mechanisms mostly sidestep reentrancy concerns through the GIL, but C extensions must implement reentrant code when they release the GIL during blocking operations. The Actor Pattern achieves reentrancy by eliminating shared state entirely—each actor processes one message at a time, making interruption irrelevant.

Reentrancy represents a stronger guarantee than thread safety: all reentrant code is thread-safe, but thread-safe code may fail under interruption or recursion.