malloc
and printf
usually use global structures, and employ lock-based synchronization internally. That's why they're not reentrant.
The malloc
function could either be thread-safe or thread-unsafe. Both are not reentrant:
Malloc operates on a global heap, and it's possible that two different invocations of malloc
that happen at the same time, return the same memory block. (The 2nd malloc call should happen before an address of the chunk is fetched, but the chunk is not marked as unavailable). This violates the postcondition of malloc
, so this implementation would not be re-entrant.
To prevent this effect, a thread-safe implementation of malloc
would use lock-based synchronization. However, if malloc is called from signal handler, the following situation may happen:
malloc(); //initial call
lock(memory_lock); //acquire lock inside malloc implementation
signal_handler(); //interrupt and process signal
malloc(); //call malloc() inside signal handler
lock(memory_lock); //try to acquire lock in malloc implementation
// DEADLOCK! We wait for release of memory_lock, but
// it won't be released because the original malloc call is interrupted
This situation won't happen when malloc
is simply called from different threads. Indeed, the reentrancy concept goes beyond thread-safety and also requires functions to work properly even if one of its invocation never terminates. That's basically the reasoning why any function with locks would be not re-entrant.
The printf
function also operated on global data. Any output stream usually employs a global buffer attached to the resource data are sent to (a buffer for terminal, or for a file). The print process is usually a sequence of copying data to buffer and flushing the buffer afterwards. This buffer should be protected by locks in the same way malloc
does. Therefore, printf
is also non-reentrant.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…