Modify

Ticket #4162 (closed Feature Requests: fixed)

Opened 4 years ago

Last modified 3 years ago

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.

Attachments

Change History

comment:1 Changed 4 years ago by artyomtnk@…

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 Changed 4 years ago by chris_kohlhoff

  • Type changed from Bugs to Feature Requests
  • Milestone changed from Boost 1.43.0 to To Be Determined
  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 Changed 4 years ago by artyomtnk@…

  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 Changed 4 years ago by chris_kohlhoff

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

comment:5 Changed 4 years ago by sephirok@…

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

comment:6 Changed 4 years ago by artyomtnk@…

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 Changed 4 years ago by sephirok@…

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 Changed 3 years ago by chris_kohlhoff

(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 Changed 3 years ago by chris_kohlhoff

  • Status changed from new to closed
  • Resolution set to fixed

Applied to release branch in [72428].

View

Add a comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
The resolution will be deleted. Next status will be 'reopened'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.