Updated: April 19, 2023 |
Using fork() in a multithreaded process can be tricky, especially if one thread calls fork() while other threads are in critical sections of code.
POSIX says that following a fork(), the child's memory state is a copy of the parent's, but the child contains only a single thread; any other threads in the parent aren't duplicated in the child. If one of those parent threads was in the middle of manipulating a data structure when the fork() happened, that data structure will be in an undefined, possibly inconsistent, state in the child following the fork(). Regular mutexes can be used to protect different threads from interfering with each other, but protecting against fork() requires the forking code to be aware of the internals of your data structure synchronization.
For example, suppose the parent process has two threads, T1 and T2, and T2 has locked a mutex. If T1 calls fork(), the child process is a copy of the parent, including any mutexes, but with only one thread. If the thread in the child tries to lock the mutex, the operation fails because T2 has it locked—and there is no T2 in the child process, so no one can unlock the mutex. This can lead to a deadlock.
The simplest solution is to call fork() only from a single-threaded process, or to call it before you create additional threads. But, if you insist on using fork() in a multithreaded process, there are some things you can do:
You can use pthread_atfork() to register the following handlers:
You can call pthread_atfork() more than once if you want to register additional handlers; the parent and child handlers are called in the order that you registered them, and the prepare handlers are called in the reverse order.
In the prepare handler, you can lock all the mutexes, and then unlock them in the parent and child handlers. This ensures that no other threads can hold the mutexes across the fork(). However, if you have many different mutexes in different code that is only loosely coupled, getting the locking order correct to avoid deadlock can be tricky.
In QNX Neutrino 6.6 and later, we've implemented a forksafe mutex as another way to deal with this problem. A forksafe_mutex_t is a replacement for a pthread_mutex but is mutually exclusive with fork() calls. The fork() function is aware of mutexes of this type and can't execute while any forksafe mutexes are held: fork() blocks while any forksafe mutexes are held, and while a fork() call is underway, any attempts to lock a forksafe mutex block until the fork() has completed.
You can use the following functions to work with forksafe mutexes:
They replace the corresponding pthread_mutex_*() functions; for more information, see the C Library Reference.
Note the following:
In order to use a forksafe mutex, you need to link your program against libforksafe_mutex.
At-fork handlers and forksafe mutexes will help, but it can still be tricky to correctly set up the mutexes in the child, so our recommendation is not to use fork() in a multithreaded process, or to use posix_spawn() instead.