Add lazy kqueue EVFILT_WRITE registration. This fixes support for read-only file descriptors, such as those obtained from libpcap by calling pcap_get_selectable_fd.
diff --git a/asio/include/asio/detail/impl/kqueue_reactor.ipp b/asio/include/asio/detail/impl/kqueue_reactor.ipp index b4f2e9d..06927da 100644 --- a/asio/include/asio/detail/impl/kqueue_reactor.ipp +++ b/asio/include/asio/detail/impl/kqueue_reactor.ipp
@@ -100,9 +100,9 @@ EVFILT_READ, EV_ADD, 0, 0, &interrupter_); if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) { - asio::error_code error(errno, + asio::error_code ec(errno, asio::error::get_system_category()); - asio::detail::throw_error(error); + asio::detail::throw_error(ec, "kqueue interrupter registration"); } // Re-register all descriptors with kqueue. @@ -115,11 +115,11 @@ EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, state); ASIO_KQUEUE_EV_SET(&events[1], state->descriptor_, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, state); - if (::kevent(kqueue_fd_, events, 2, 0, 0, 0) == -1) + if (::kevent(kqueue_fd_, events, state->num_kevents_, 0, 0, 0) == -1) { - asio::error_code error(errno, + asio::error_code ec(errno, asio::error::get_system_category()); - asio::detail::throw_error(error); + asio::detail::throw_error(ec, "kqueue re-registration"); } } } @@ -138,14 +138,13 @@ mutex::scoped_lock lock(descriptor_data->mutex_); descriptor_data->descriptor_ = descriptor; + descriptor_data->num_kevents_ = 1; descriptor_data->shutdown_ = false; - struct kevent events[2]; + struct kevent events[1]; ASIO_KQUEUE_EV_SET(&events[0], descriptor, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, descriptor_data); - ASIO_KQUEUE_EV_SET(&events[1], descriptor, EVFILT_WRITE, - EV_ADD | EV_CLEAR, 0, 0, descriptor_data); - if (::kevent(kqueue_fd_, events, 2, 0, 0, 0) == -1) + if (::kevent(kqueue_fd_, events, 1, 0, 0, 0) == -1) return errno; return 0; @@ -160,15 +159,14 @@ mutex::scoped_lock lock(descriptor_data->mutex_); descriptor_data->descriptor_ = descriptor; + descriptor_data->num_kevents_ = 1; descriptor_data->shutdown_ = false; descriptor_data->op_queue_[op_type].push(op); - struct kevent events[2]; + struct kevent events[1]; ASIO_KQUEUE_EV_SET(&events[0], descriptor, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, descriptor_data); - ASIO_KQUEUE_EV_SET(&events[1], descriptor, EVFILT_WRITE, - EV_ADD | EV_CLEAR, 0, 0, descriptor_data); - if (::kevent(kqueue_fd_, events, 2, 0, 0, 0) == -1) + if (::kevent(kqueue_fd_, events, 1, 0, 0, 0) == -1) return errno; return 0; @@ -201,8 +199,7 @@ return; } - bool first = descriptor_data->op_queue_[op_type].empty(); - if (first) + if (descriptor_data->op_queue_[op_type].empty()) { if (allow_speculative && (op_type != read_op @@ -214,15 +211,43 @@ scheduler_.post_immediate_completion(op, is_continuation); return; } + + if (op_type == write_op) + { + if (descriptor_data->num_kevents_ == 1) + { + struct kevent events[2]; + ASIO_KQUEUE_EV_SET(&events[0], descriptor, EVFILT_READ, + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); + ASIO_KQUEUE_EV_SET(&events[1], descriptor, EVFILT_WRITE, + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); + if (::kevent(kqueue_fd_, events, 2, 0, 0, 0) != -1) + { + descriptor_data->num_kevents_ = 2; + } + else + { + op->ec_ = asio::error_code(errno, + asio::error::get_system_category()); + io_service_.post_immediate_completion(op, is_continuation); + return; + } + } + } } else { + if (op_type == write_op) + { + descriptor_data->num_kevents_ = 2; + } + struct kevent events[2]; ASIO_KQUEUE_EV_SET(&events[0], descriptor, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, descriptor_data); ASIO_KQUEUE_EV_SET(&events[1], descriptor, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, descriptor_data); - ::kevent(kqueue_fd_, events, 2, 0, 0, 0); + ::kevent(kqueue_fd_, events, descriptor_data->num_kevents_, 0, 0, 0); } } @@ -276,7 +301,7 @@ EVFILT_READ, EV_DELETE, 0, 0, 0); ASIO_KQUEUE_EV_SET(&events[1], descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0); - ::kevent(kqueue_fd_, events, 2, 0, 0, 0); + ::kevent(kqueue_fd_, events, descriptor_data->num_kevents_, 0, 0, 0); } op_queue<operation> ops; @@ -317,7 +342,7 @@ EVFILT_READ, EV_DELETE, 0, 0, 0); ASIO_KQUEUE_EV_SET(&events[1], descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0); - ::kevent(kqueue_fd_, events, 2, 0, 0, 0); + ::kevent(kqueue_fd_, events, descriptor_data->num_kevents_, 0, 0, 0); op_queue<operation> ops; for (int i = 0; i < max_ops; ++i)
diff --git a/asio/include/asio/detail/kqueue_reactor.hpp b/asio/include/asio/detail/kqueue_reactor.hpp index e841616..80bdce1 100644 --- a/asio/include/asio/detail/kqueue_reactor.hpp +++ b/asio/include/asio/detail/kqueue_reactor.hpp
@@ -67,6 +67,7 @@ mutex mutex_; int descriptor_; + int num_kevents_; // 1 == read only, 2 == read and write op_queue<reactor_op> op_queue_[max_ops]; bool shutdown_; };