Modify

Ticket #3238 (closed Bugs: fixed)

Opened 5 years ago

Last modified 3 years ago

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.

Attachments

Change History

comment:1 Changed 3 years ago by anonymous

  • Status changed from new to closed
  • Version changed from Boost 1.38.0 to Boost Development Trunk
  • Resolution set to fixed

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

  • Status changed from closed to reopened
  • Resolution fixed deleted
  • Milestone set to To Be Determined

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 Changed 3 years ago by devel@…

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

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

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

Merged 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.