[nanomsg] Re: On pthread_atfork(), and fork()-safe implementation

  • From: Garrett D'Amore <garrett@xxxxxxxxxx>
  • To: nanomsg@xxxxxxxxxxxxx
  • Date: Tue, 13 Dec 2016 08:51:32 -0800

Thanks.  I had planned to design a fork safe version of things in the new 
design. I had implemented freeze and thaw and reset entry points at various 
points and was pretty sure that this would have worked well.  Until I 
discovered that the child side version was not allowed to call any mutex 
functions or to call free.

I will think about this some more.  Delaying the child side action might be 
reasonable and lead to a working solution. 

Sent from my iPhone

On Dec 13, 2016, at 12:20 AM, Franklin Mathieu <franklinmathieu@xxxxxxxxx> 
wrote:

I'm going to give my 2 cents on the matter as I was the one that initially
opened the github issue regarding fork()-safety and I had the time
to work with different approaches on the matter.

I've been maintaining an unit testing framework for C that relies on
worker processes to run tests safely, and as such, for the longest
time, this had been implemented with fork() without a subsequent exec().
I recently switched the I/O layer of the framework to use nanomsg
because it was simple, and it was much more "correct" than what
I had been doing before with pipe() shenanigans.

However, as nanomsg isn't fork()-safe, I took a swab at implementing
a fork()-safety mechanism, which ended up being brittle but was
"good enough" for my purposes, and I reworked other dependencies
to make sure they handled forks correctly.

The problem with fork()-safety is that unless you think of it right at the
design of the software, you're going to end up doing something hack-ish;
which means that the rewrite could be a good starting point to actually
implement the structural basis towards fork()-safety. POSIX might be
right on target with the problems caused by pthread_atfork(), but in
practice there is a lot of wiggle room to do what we must to make
things work at fork.

With all of that being said, I've given up myself on fork()-safety.
The fact is that there is no single silver bullet to address this,
that a lot of software is expecting exec() to be called after a fork(),
and that there aren't many use cases in having worker processes.

I ended up writing a library dedicated to spawning worker
processes [1] in a manner that calls fork() then re-exec()s the current
executable with a patched main function, which while not ideal, is
in my opinion less of a hack than having to make the software and
all of its dependencies fork-safe().

This is why I understand your decision of giving up and panicking
the process on fork-reentry. You might also be able to compromise
by only allowing calls to nng_socket_create after fork, which could under
the covers completely drop the current invalid state and just reinitialize
the library. This would cause a resource leak, but allow the usage
of sockets in the child for those that really want it.

[1]: https://github.com/diacritic/BoxFort

2016-12-12 19:31 GMT+01:00 Garrett D'Amore <garrett@xxxxxxxxxx>:
The following conversation relates to using fork() with nanomsg (or future
rewrites), where you do *not* immediately call exec().  Using fork() and
then immediately calling exec() is fine, and will continue to work as it
always.

But some people want to use fork() to spawn children, e.g. a child worker
process, that communicates back to the parent somehow.   This is never going
to work.

I’ve been doing a bit more research into pthread_atfork() as part of an
attempt to make my new nng library properly fork()-safe.  I’ve more or less
given up though.

The reason for this is that even the OpenGroup has given up — see
http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html
— and especially the RATIONALE section, for the logic behind this.  They
have even indicated plans to deprecate the pthread_atfork() API altogether.

Essentially, it isn’t possible to make a version of the library fork() safe
as it would be necessary to free resources, do locks, etc. — i.e. all those
Async-Signal-Unsafe calls.

So, for libnng, and possibly in the future for libnanomsg, I will be
changing the API so that if you attempt to callback into the library after
fork(), it will actually panic the process.

I probably will also arrange for pthread_atfork() to be called to close any
file descriptors that were not marked close-on-exec…

Stay tuned for more details.

- Garrett


-- 
Franklin "Snaipe" Mathieu
🝰 https://diacritic.io


Other related posts: