Opened 10 years ago

Closed 8 years ago

#3238 closed Bugs (fixed)

asio, kqueue_reactor, result of kevent() isn't checked for error

Reported by: devel@… Owned by: chris_kohlhoff
Milestone: To Be Determined Component: asio
Version: Boost Development Trunk Severity: Problem
Keywords: asio kqueue Cc:


No kevent() result checking.

    wait_in_progress_ = true;

    // Block on the kqueue descriptor.
    struct kevent events[128];
    int num_events = (block || need_kqueue_wait_)
      ? kevent(kqueue_fd_, 0, 0, events, 128, timeout)
      : 0;

    wait_in_progress_ = false;

    // Block signals while performing operations.
    boost::asio::detail::signal_blocker sb;

    // Dispatch the waiting events.
    for (int i = 0; i < num_events; ++i)
            // processing events

Here is int num_events = kevent(kqueue_fd_, ..., then for (int i = 0; i < num_events but num_events can be -1 here, and all this code makes no sense then.

Suppose we have such code:

smtp_queue q("/var/spool/smtpq");
asio::io_service io_service;
smtp_server s(io_service, 25, q);
smtp_client c(io_service, "", "smtp", q);
if(fork()) exit(0); // daemonize;

Here we call fork() then fork doesn't copy kqueue descriptor and subsequent kevent(fd,...) calls return -1 with errno = 9 (EBADFD, "Bad file descriptor"). This case makes asio's kqueue reactor hangup. Probably, kevent() result should be checked for error and exception thrown if any.

Change History (5)

comment:1 Changed 8 years ago by anonymous

Resolution: fixed
Status: newclosed
Version: Boost 1.38.0Boost Development Trunk

This appears to have been fixed, at least in trunk. Marking as fixed.

Please re-open in case the symptom still exhibits. Unfortunately, I don't think sharing the already initialized io_service across processes is supported (unless I'm missing something). At any rate, the code in question has since been refactored and the implementation handles the case where kevent(...) is -1.

comment:2 Changed 8 years ago by chris_kohlhoff

Milestone: To Be Determined
Resolution: fixed
Status: closedreopened

This is a difficult to fix rainy-day bug. It is not fixed on trunk. Please see:

The following program will reproduce the issue:

#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/udp.hpp>
#include <iostream>

int main()
  boost::asio::io_service io_service;
  boost::asio::io_service::work work(io_service);


  if (fork()) exit(0);;

On Mac OS X, kevent() failure means the forked child consumes 100% CPU. Compiling with -DBOOST_ASIO_DISABLE_KQUEUE gives the desired behaviour, which is that the forked child consumes no CPU.

comment:3 Changed 8 years ago by devel@…

rfork(RFPROC) (no RFFDG) does a trick on *BSD

comment:4 Changed 8 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:5 Changed 8 years ago by chris_kohlhoff

Resolution: fixed
Status: reopenedclosed

Merged to release branch in [72428].

Note: See TracTickets for help on using tickets.