Opened 13 years ago

Closed 11 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:

Description

No kevent() result checking.

asio/detail/kqueue_reactor.hpp:438
...
    wait_in_progress_ = true;
    lock.unlock();

    // 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;

    lock.lock();
    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, "target.host", "smtp", q);
if(fork()) exit(0); // daemonize
io_service.run();

Here we call fork() then io_service.run(). 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 by anonymous, 12 years ago

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 by chris_kohlhoff, 12 years ago

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:

http://article.gmane.org/gmane.comp.lib.boost.asio.user/3383

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);

  boost::asio::use_service<
    boost::asio::ip::udp::socket::service_type>(
      io_service);

  if (fork()) exit(0);

  io_service.run();
}

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 by devel@…, 12 years ago

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

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

Resolution: fixed
Status: reopenedclosed

Merged to release branch in [72428].

Note: See TracTickets for help on using tickets.