This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: RFC: mutexes acquired before fork() remain to be acquired by the parent process after fork()


On 12/20/2016 06:02 PM, Torvald Riegel wrote:
I'm looking for discussion and consensus about what happens when a
process is forked that has acquired mutexes.  I'm not aware of a
definite answer given by POSIX, and this affects robust mutexes in
particular.
I'm first looking for opinions and hopefully consensus within glibc, and
would then follow up with the Austin Group if necessary.

I think the most practical choice would be one of these two requirements
(I prefer R1 for reasons I'll mention below):

(R1) Any interaction of the child process with mutexes that are in an
acquired state when fork() was called in the parent is undefined.

Can you clarify if this refers to robust mutexes, or ordinary mutexes alone?

Forking with acquired locks is quite common, and we do it in glibc as well, to support malloc after fork even if the parent process was multi-threaded at the time of the fork.

(R2) Any mutexes that are in an acquired state when fork() was called in
the parent remain to be locked by the parent process.


If a mutex is process-shared, it should not have two owners after fork()
because this is against the whole principle of exclusive-ownership
mutexes.

I think you refer to the effectively process-shared case here, and not just mutexes with a process-shared attribute. With effectively process-shared I mean that the mutex object resides on a shared mapping, for which fork does not make a copy, and the mapping is shared between parent and child process.

Theoretically, we could traverse the robust list, find those mutexes which are on MAP_SHARED pages (using information from /proc) and build a new list of robust mutexes containing only those mutexes which are not effectively process-shared. We would only have to update the list pointers of mutexes which have been copied into the child, so this is feasible conceptually.

But /proc might not be available at this point, or there might not be enough file descriptors to open the files etc., so this approach would be quite brittle.

POSIX states in the rationale that fork() is only used to either create
(something like) a new thread or to call exec().  Both align well with
letting only the parent be the owner of a mutex.
It also states: "When a programmer is writing a multi-threaded program,
the first described use of fork(), creating new threads in the same
program, is provided by the pthread_create() function. The fork()
function is thus used only to run new programs, and the effects of
calling functions that require certain resources between the call to
fork() and the call to an exec function are undefined."  Even though
this ignores the possibility of acquiring mutexes in a single-threaded
program, it states that requiring resources (eg, attempting to lock a
mutex) between fork() and exec() is undefined -- which would align well
with R1.
It also explains that a forkall() idea was rejected that would have
"allow[ed] locks and the state to be preserved without explicit
pthread_atfork() code"; this is again an indication that R1 is the
intent or compatible with the intent.

I disagree with this conclusion. pthread_atfork handlers typically acquire locks to ensure that the parent process is in a specific state, and release them in the parent and child after the fork. With R1, this common pattern is suddenly undefined, which is not what we want, I think.

Maybe we could introduce a new mutex type specifically for this purpose, or declare that a PTHREAD_MUTEX_NORMAL, non-robust, non-pshared mutex is an exception here.

Thanks,
Florian


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]