@ page 403 line 13033-13036 section fork objection [gwc fork multithread child]
Problem:
Defect code : 1. Error
An intended use for pthread_atfork() is to allow applications
(particularly application libraries) to ensure that after a fork()
no mutexes are owned by non-existent threads in the child.
This is stated in the rationale for pthread_atfork():
"For example, an application can supply a prepare routine that
acquires the necessary mutexes the library maintains and supply
child and parent routines that release those mutexes, thus
ensuring that the child gets a consistent snapshot of the state of
the library (and that no mutexes are left stranded)."
When used in this way, the function called by pthread_atfork() in the
child needs to call pthread_mutex_unlock(), however the fork() page
states that a child of a multi-threaded process "may only execute
async-signal-safe operations until such time as one of the exec
functions is called", and pthread_mutex_unlock() is not in the list
of async-signal-safe functions at the end of XSH section 2.4.3
"Signal Actions".
The pthread_atfork() rationale also says:
"Alternatively, some libraries might be able to supply just a
child routine that reinitializes the mutexes in the library and
all associated states to some known value (for example, what it
was when the image was originally executed)."
This implies that there is also an intention for pthread_mutex_init()
to be usable in the child. However, this method is not portable
because the reinitialization can fail with EBUSY.
Although not mentioned in the rationale, the same issue affects
read-write locks and spin locks.
The simple solution to the immediate problem would be to add
pthread_mutex_unlock(), pthread_mutex_init(), pthread_rwlock_unlock(),
pthread_rwlock_init(), pthread_spin_unlock() and pthread_spin_init()
to the list of async-signal-safe functions. However, this is not
very satisfactory. Having unlocked all the mutexes/rwlocks/spinlocks
the child is still restricted to using only async-signal-safe
operations, so what advantage was there in acquiring all the locks
across the fork? It seems to me that the intention is that acquiring
all the locks is supposed to be a way to avoid the restriction to
async-signal-safe operations.
Action:
Change:
"Consequently, to avoid errors, the child process may only execute
async-signal-safe operations until such time as one of the exec
functions is called. [THR] Fork handlers may be established by
means of the pthread_atfork() function in order to maintain
application invariants across fork() calls. [/THR]"
To:
"Consequently, to avoid errors, the child process may only execute
async-signal-safe operations until such time as one of the exec
functions is called, unless on entry to fork() the calling thread
owns all of the mutexes, read-write locks and spin locks that
have been initialized (and not subsequently destroyed) by the
application in the current process. [THR] Fork handlers may be
established by means of the pthread_atfork() function in order to
acquire all locks across fork() calls. [/THR]"
On page 993 line 31740 section pthread_atfork, after:
"Alternatively, some libraries might be able to supply just a
child routine that reinitializes the mutexes in the library and
all associated states to some known value (for example, what it
was when the image was originally executed)."
add:
"Note that this alternative method is not portable because the
reinitialization can fail with EBUSY."
|