tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: pthread_atfork and locks



Taylor R Campbell <campbell+netbsd-tech-userlevel%mumble.net@localhost> writes:

>    From: Greg Troxel <gdt%ir.bbn.com@localhost>
>
>    We had a big problem with this where python (among other things) would
>    fork and then call malloc in the child.   If the malloc lock were taken
>    by some other thread in the parent, then it would deadlock.  python is
>    wrong here, as malloc is not specificed to be async-signal-safe.   But
>    the workaround (which I'm a bit fuzzy on) was to take the malloc lock
>    in the before handler and release it in both - in libc, not in python.
>
> As an aside, that's probably still an issue with NetBSD libc -- I
> don't see any atfork handler in our malloc code.

There is _malloc_prefork() which gets the lock.  I am not sure how
that's hooked in, but I remember someone (Christos) adding this from FreeBSD.

>    I think this is unsound (calling mutex_init).   If the mutex is acquired
>    in the thread that calls fork, then it should be safe to unlock it.
>
> Calling mutex_init is safe in our implementation of libpthread.  It's
> just not portable.  The question with mutex_unlock is whether, if some
> other thread was trying to lock the mutex at the time of the fork,
> some state internal to the mutex might be inconsistent in the child.

But if this thread had gotten the mutex, and then there was a signal,
we'd be clearing it.  That's what I meant by unsound.

If the forking thread has the mutex, then the idea that some other
thread would be trying and somehow causing trouble doesn't make sense.
How is that different from the situation without fork?  I think the
scary part about threaded fork is that the other threads just end in
some random place, possibly messy in time.  (Hopefully fork() waits
until the other threads are descheduled.)


>    Are you trying to figure out how to have the child act like a
>    full-fledged process without doing an exec?   I don't think that's
>    really possible.

> Trying to figure out how to make a library with global state work in
> multithreaded programs and in programs that use fork, without randomly
> hanging or duplicating invariants or (for arc4random(3)) leaking
> secrets to the children or anything horrible like that.

But posix is very clear that in the child after fork from a threaded
program, only calls that are defined to be async-signal-safe may be
called:

  http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html

but I guess defensive programming about this is fine, to avoid
disclosure.

I've come to believe that in a threaded program, the only sane thing
after fork is to immediately exec.  But I can see trying to make this
work by taking all the mutexes before fork to make sure that the forking
thread has them.

> As far as I can tell, the intent in POSIX is that you should be able
> to pthread_atfork(/*prepare*/lock, /*parent*/unlock, /*child*/unlock).
> But I'm not confident we guarantee that to work in NetBSD, and we
> ought to.

I would think that is safe, just based on the implementation.   Do you
find that it isn't?

Attachment: pgpFFBZQOCvMM.pgp
Description: PGP signature



Home | Main Index | Thread Index | Old Index