When mixing threads, plugs and mutexes, what should I keep track of?

If I fork process in which one thread contains a mutex, am I relatively safe if I immediately exec in the child? What can be done safely for a child before I exec ?

If the thread that executes the fork , then the child continues to issue the mutex before calling exec , will this cause problems? What happens if I try to get a mutex from a child who owns the parent process before fork (and may or may not be yet)?

Are the answers different on different platforms? First of all, I'm interested in Unix variants, and in particular Linux. But I'm interested in NT. Although, of course, NT does not know (as far as I know) fork .

+6
source share
2 answers

See pthread_atfork , especially the JUSTIFICATION section, for a discussion of fork issues in a multi-threaded environment. It also gives a hint about what should be valid before and after fork inside the child and parent.

UPDATE: the Rationale section is non-normative, and it is in conflict with other parts of the standard. For more information, see this Dave Butenhof Defect Report.

Immediate exec after fork should be safe for any state of a multi-threaded program (i.e. any threads containing any mutexes). As for things between fork and exec , the situation is complicated:

Most importantly, only one thread (the one called fork ) is duplicated in the child process. Therefore, any mutex held by another thread at the time of fork is blocked forever. That is, (provided that the mutexes are not related to the process), its copy in the child process is locked forever, because there is no thread to unlock it.

Disabling the mutex after fork is safe when possible, that is, if the fork ing thread belongs to the mutex in the first place. As pthread_atfork handlers usually work: locking mutexes to fork , unlocking in the child device, and unlocking in the parent.

How to get the mutex belonging to the process before fork (remember, we are discussing a copy in the child address space): if it belonged to the fork ing stream, it is recursive blocking (works for PTHREAD_MUTEX_RECURSIVE ); if it belonged to another thread, it remains blocked forever and cannot be re-acquired.

By registering the appropriate pthread_atfork handlers, third-party libraries can guarantee the security of use between fork and exec . (I would expect that this is mainly from runtime of programming languages, and not for general purpose libraries).


After some further research, I would recommend not relying on pthread_atfork and not doing anything except asynchronous signal calls between fork and exec (giving up fork / exec for posix_spawn would be even better).

The problem is that fork itself can be called in the signal handler. This excludes any non-trivial use of pthread_atfork , even if its JUSTIFICATION explicitly mentions unlocking mutexes and re-creating threads (!) In the child process.

I think the β€œgray area” of the various possible interpretations remains:

  • For pthread_atfork handlers in a program that is known to never call fork in a signal handler.
  • For actions other than pthread-atfork that occur around a fork call that is not in the signal handler.

But it is crystal clear which reading should be used for portable applications.

+6
source

It is safe to execute exec () after fork () if the mutex belongs to a program that will be replaced by exec (). If the mutex is part of the library and protects the resource that must be accessed sequentially, it must call pthread_atfork () to register callbacks:

  • A function called before the fork itself and in the context of the thread that calls the fork () system call. Typically, this function captures the lock on mutexes that protect critical sections, thereby ensuring that the thread inside is in the critical section during the fork.
  • A function that is called in the context of the thread that calls fork () after creating the child process, but before returning the fork () system call. This function can then unlock the mutexes in the parent process.
  • The function called in the context of the thread of the child process, if / when the fork process - it is called after the child process has been created, but before returning the fork () system call. Then the child process can unlock its copy of the mutex.

The POSIX standard restricts the type of system calls allowed after fork () and before calling exec () to so-called system calls that support an asynchronous signal. Creating a thread does not explicitly appear as a system call that is safe for an asynchronous signal, so POSIX does not allow the child process to create threads after fork () and before exec (). Ironically, unlocking the mutex is not explicitly indicated as a system call with an asynchronous signal, and isre is not allowed strictly after fork () in the child process, if the intention of fork () is to execute exec () - perhaps the supervision in the standard.

+1
source

All Articles