Re: g_spawn_async_with_pipes is not thread safe

On 06/03/2016 02:30 PM, Simon McVittie wrote:
On 03/06/16 09:18, Chris Vine wrote:
POSIX allows only async-signal-safe functions (see signal(7)) to be
called in the child between fork() and exec(), which drastically limits
the usefulness of child setup functions
There's a gap here between the theory (GLib mostly has only POSIX
requirements) and the practice (glibc malloc is designed to allow things
POSIX doesn't, and this useful functionality would be difficult to
achieve without that). 
That explains everything. Originally I had crashes/hangs under OpenWrt
and musl as libc. I should have said that in my first email. Then I switched
to desktop Linux with glibc for testing and also got hangs in a forked child,
but it was actually due to rand() which I used to get random memory
allocation sizes. rand() uses __libc_lock_lock which is apparently not fork
safe. After removing rand() and leaving only malloc/free in parent/child I
wasn't able to reproduce the problem. In the same time, the same code
on OpenWrt with musl hangs just after few seconds. So you are right -
glibc does more than POSIX requires, but musl doesn't.

So this is strictly speaking not a musl bug, because it is POSIX compliant.
From the other side, glib code seems to be dependant on the fact that
underlying glibc memory allocation functions are so to say fork-safe which
is not a POSIX requirement.

Am I right in my conclusions? If yes, then what is the right way to address
the problem - mention in docs that spawning glib API is not safe if your
underlying libc is not fork-safe? Doh :(

In principle GLib could call opendir() on
/proc/$pid before forking, then read it in the child, but readdir()
isn't async-signal-safe either. Doing the reading in the parent isn't a
solution, because the parent is potentially multi-threaded, so it could
be opening and closing file descriptors in other threads (racing with
the readdir()).

In principle the parent could read the new child's fd table and write a
list of fds into a pipe, for the child to close them all or set them all
close-on-exec, while the child waits for EOF on that pipe before
proceeding with the close and exec operations... but that seems fairly

I think what we really want here is *BSD closefrom(), which at least
FreeBSD documents as async-signal-safe. Unfortunately, Linux doesn't
have that system call. libdbus would have the same issue on platforms
that don't have closefrom().

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]