Clive D.W. Feather wrote:
In any case, it clearly would not be acceptable for the application to
crash because exit() found the list of atexit() handlers in an
inconsistent state, for example. Nor for atexit() to crash because
exit() might modify the list.
Agreed.
There is, however, always a "race" between callers of dependent
functions. That is, it's not possible to define exit() so that it's
guaranteed to see all atexit() handlers that the application might ever
register (concurrently or in the future). But either exit() shall see
the new atexit handler and invoke it properly OR exit() shall not see
the new atexit handler and will proceed as if this call to atexit() had
not happened. Logically, exit() occurs either before or after atexit()
insertion; never "during".
I disagree here.
An atexit() handler is either installed before the call to exit(), or while
exit() is processing. In the former case, the handler must be called for
obvious reasons. In the latter case it is installed by another atexit()
handler. The C Standard is clear that an atexit() handler installed by
another atexit() handler is called after that one returns and before what
would have been the next one is called. In other words, it has a stack
model:
* atexit() pushes the handler on to the stack.
* exit() effectively goes:
while stack is not empty
{
pop handler from stack
call handler
}
Except that here we're talking about synchronization between two
concurrently executing threads. It IS possible for atexit() to be
invoked after exit() starts, but not from another atexit handler -- it's
called from another thread.
This call may block on an internal libc mutex until exit() calls _exit()
and the process terminates, unless exit() releases the mutex controlling
the atexit handler list during processing. But even if it does, another
thread may invoke atexit() immediately after exit() relocks for the last
time. Until all threads cease execution (which doesn't happen until
_exit()), there's no way to prevent this.
POSIX does not require that the process somehow be shut down to single
thread mode before calling atexit() handlers; and this would be
contentious to specify, difficult to implement, and probably impossible
to use. Therefore, atexit() CAN be called after exit() processing
begins, in such a way that the atexit() handler cannot be seen by exit().
|