Next: , Previous: Threads and Signal Handling, Up: POSIX Threads


34.10 Threads and Fork

It's not intuitively obvious what should happen when a multi-threaded POSIX process calls fork. Not only are the semantics tricky, but you may need to write code that does the right thing at fork time even if that code doesn't use the fork function. Moreover, you need to be aware of interaction between fork and some library features like pthread_once and stdio streams.

When fork is called by one of the threads of a process, it creates a new process which is copy of the calling process. Effectively, in addition to copying certain system objects, the function takes a snapshot of the memory areas of the parent process, and creates identical areas in the child. To make matters more complicated, with threads it's possible for two or more threads to concurrently call fork to create two or more child processes.

The child process has a copy of the address space of the parent, but it does not inherit any of its threads. Execution of the child process is carried out by a new thread which returns from fork function with a return value of zero; it is the only thread in the child process. Because threads are not inherited across fork, issues arise. At the time of the call to fork, threads in the parent process other than the one calling fork may have been executing critical regions of code. As a result, the child process may get a copy of objects that are not in a well-defined state. This potential problem affects all components of the program.

Any program component which will continue being used in a child process must correctly handle its state during fork. For this purpose, the POSIX interface provides the special function pthread_atfork for installing pointers to handler functions which are called from within fork.

— Function: int pthread_atfork (void (*prepare)(void), void (*parent)(void), void (*child)(void))

pthread_atfork registers handler functions to be called just before and just after a new process is created with fork. The prepare handler will be called from the parent process, just before the new process is created. The parent handler will be called from the parent process, just before fork returns. The child handler will be called from the child process, just before fork returns.

pthread_atfork returns 0 on success and a non-zero error code on error.

One or more of the three handlers prepare, parent and child can be given as NULL, meaning that no handler needs to be called at the corresponding point.

pthread_atfork can be called several times to install several sets of handlers. At fork time, the prepare handlers are called in LIFO order (last added with pthread_atfork, first called before fork), while the parent and child handlers are called in FIFO order (first added, first called).

If there is insufficient memory available to register the handlers, pthread_atfork fails and returns ENOMEM. Otherwise it returns 0.

The functions fork and pthread_atfork must not be regarded as reentrant from the context of the handlers. That is to say, if a pthread_atfork handler invoked from within fork calls pthread_atfork or fork, the behavior is undefined.

Registering a triplet of handlers is an atomic operation with respect to fork. If new handlers are registered at about the same time as a fork occurs, either all three handlers will be called, or none of them will be called.

The handlers are inherited by the child process, and there is no way to remove them, short of using exec to load a new pocess image.

To understand the purpose of pthread_atfork, recall that fork duplicates the whole memory space, including mutexes in their current locking state, but only the calling thread: other threads are not running in the child process. The mutexes are not usable after the fork and must be initialized with pthread_mutex_init in the child process. This is a limitation of the current implementation and might or might not be present in future versions.

To avoid this, install handlers with pthread_atfork as follows: have the prepare handler lock the mutexes (in locking order), and the parent handler unlock the mutexes. The child handler should reset the mutexes using pthread_mutex_init, as well as any other synchronization objects such as condition variables.

Locking the global mutexes before the fork ensures that all other threads are locked out of the critical regions of code protected by those mutexes. Thus when fork takes a snapshot of the parent's address space, that snapshot will copy valid, stable data. Resetting the synchronization objects in the child process will ensure they are properly cleansed of any artifacts from the threading subsystem of the parent process. For example, a mutex may inherit a wait queue of threads waiting for the lock; this wait queue makes no sense in the child process. Initializing the mutex takes care of this.