Opened 12 years ago

Closed 11 years ago

#4162 closed Feature Requests (fixed)

io_service can't be used correctly after fork().

Reported by: artyomtnk@… Owned by: chris_kohlhoff
Milestone: To Be Determined Component: asio
Version: Boost 1.42.0 Severity: Problem
Keywords: asio posix fork unix Cc:

Description

Hello,

I'm trying to run standard "prefork" unix configuration with boost-asio.

I have following setup:

  1. I bind to some port.
  1. I prefork several processes.
  1. As new connection is avalible one of processes accept it

and run the main loop.


Issue:

The io_service is created at the begging and creates eventfd (or pipe), and unlike devpoll, kqueue or epoll it is shared between forked processes.


There is the problem:

When one thread in process try to "post" something to event loop it interrupts the select with evenfd... And accidentially other process may actually receive this notification and wake up when the main process that should receive notification does not.


In short

If io_service was created before fork(), post() member function would not work correctly.


Solutions:

1) Postpone creation of pipe/eventfd to the stage when run() is called instead of construction of io_service. 2) Reopen interrupter if fork() was called.

Change History (9)

comment:1 by artyomtnk@…, 12 years ago

Any chances to get it fixed?

I'm developer of CppCMS framework. I use Boost.Asio as center event loop and support of prefork is one of central features of this framework. Now I'm quite stuck - I need either got this bug fixed, remove support of prefork or replace boost.asio with other library.

Thanks,

Artyom

comment:2 by chris_kohlhoff, 12 years ago

Milestone: Boost 1.43.0To Be Determined
Type: BugsFeature Requests
  1. Bind to port.
  2. dup() acceptor descriptor.
  3. Destroy io_service.
  4. Prefork.
  5. Create new io_service in child.
  6. acceptor::assign()

comment:3 by artyomtnk@…, 12 years ago

  1. Bind to port.
  2. dup() acceptor descriptor.
  3. Destroy io_service.
  4. Prefork.
  5. Create new io_service in child.
  6. acceptor::assign()

This was the first thing I thought and tried. But, I allow the library user to start some asynchronous actions before fork() at all. For example:

  • I allow him to create timers
  • I allow hit to post() some handlers to event queue so once the service is started they would be dispatched.

So the solution of creating new io_service is not quite fits my needs.

comment:4 by chris_kohlhoff, 12 years ago

Sorry, but for now you'll simply have to start those asynchronous actions after the fork.

comment:5 by sephirok@…, 12 years ago

It would be real nice to have boost.asio play nice with fork(), even after io_service creation or .run().

comment:6 by artyomtnk@…, 12 years ago

To be honst, from my knowlege of boost.asio and my implementation of fork aware booster.aio for CppCMS I don't think this is quite simple feature to implement, few points:

  • Reactor should be created, first time only after fork, probably on call of io_service::run, otherwise, its file descriptor (for epoll/depoll/kqueue) would be shared between processes.,
  • Select interrupters should be created after fork as well as it my be shared between processes as well and lead to very bad bugs.

comment:7 by sephirok@…, 12 years ago

I guess it wouldn't be possible to close the duplicate descriptors in the child and sort of "reinitialize" the io_service with a new eventfd aso.

Also what I haven't gotten working yet is just forking, letting the parent die and continuing as child - asio seems to hiccup.

comment:8 by chris_kohlhoff, 12 years ago

(In [69467]) * Add support for the fork() system call. Programs that use fork must call

io_service.notify_fork() at the appropriate times. Two new examples have been added showing how to use this feature. Refs #3238, #4162.

  • Clean up the handling of errors reported by the close() system call. In particular, assume that most operating systems won't have close() fail with EWOULDBLOCK, but if it does then set blocking mode and restart the call. If any other error occurs we assume the descriptor is closed. Refs #3307.
  • EV_ONESHOT seems to cause problems on some versions of Mac OS X, with the io_service destructor getting stuck inside the close() system call. Use EV_CLEAR instead. Refs #5021.
  • Include function name in exception what() messages.
  • Fix insufficient initialisers warning with MinGW.
  • Make the shutdown_service() member functions private.
  • Add archetypes for testing socket option functions.
  • Add missing lock in signal_set_service::cancel().
  • The signal header needs to be included in signal_set_service.hpp so that we can use constants like NSIG and SIGRTMAX.
  • Don't use Boost.Thread's convenience header. Use the header file that is specifically for the boost::thread class instead.

comment:9 by chris_kohlhoff, 11 years ago

Resolution: fixed
Status: newclosed

Applied to release branch in [72428].

Note: See TracTickets for help on using tickets.