diff options
author | Remko Tronçon <git@el-tramo.be> | 2012-12-23 13:16:26 (GMT) |
---|---|---|
committer | Remko Tronçon <git@el-tramo.be> | 2012-12-23 14:43:26 (GMT) |
commit | 491ddd570a752cf9bda85933bed0c6942e39b1f9 (patch) | |
tree | 10c25c1be8cc08d0497df1dccd56a10fbb30beee /3rdParty/Boost/src/boost/asio/detail/impl/epoll_reactor.ipp | |
parent | da7d7a0ca71b80281aa9ff2526290b61ccb0cc60 (diff) | |
download | swift-491ddd570a752cf9bda85933bed0c6942e39b1f9.zip swift-491ddd570a752cf9bda85933bed0c6942e39b1f9.tar.bz2 |
Update Boost to 1.52.0.
Change-Id: I1e56bea2600bf2ed9c5b3aba8c4f9d2a0f350e77
Diffstat (limited to '3rdParty/Boost/src/boost/asio/detail/impl/epoll_reactor.ipp')
-rw-r--r-- | 3rdParty/Boost/src/boost/asio/detail/impl/epoll_reactor.ipp | 361 |
1 files changed, 316 insertions, 45 deletions
diff --git a/3rdParty/Boost/src/boost/asio/detail/impl/epoll_reactor.ipp b/3rdParty/Boost/src/boost/asio/detail/impl/epoll_reactor.ipp index 5afb891..073bd08 100644 --- a/3rdParty/Boost/src/boost/asio/detail/impl/epoll_reactor.ipp +++ b/3rdParty/Boost/src/boost/asio/detail/impl/epoll_reactor.ipp @@ -2,7 +2,7 @@ // detail/impl/epoll_reactor.ipp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -39,13 +39,9 @@ epoll_reactor::epoll_reactor(boost::asio::io_service& io_service) : boost::asio::detail::service_base<epoll_reactor>(io_service), io_service_(use_service<io_service_impl>(io_service)), mutex_(), - epoll_fd_(do_epoll_create()), -#if defined(BOOST_ASIO_HAS_TIMERFD) - timer_fd_(timerfd_create(CLOCK_MONOTONIC, 0)), -#else // defined(BOOST_ASIO_HAS_TIMERFD) - timer_fd_(-1), -#endif // defined(BOOST_ASIO_HAS_TIMERFD) interrupter_(), + epoll_fd_(do_epoll_create()), + timer_fd_(do_timerfd_create()), shutdown_(false) { // Add the interrupter's descriptor to epoll. @@ -66,7 +62,8 @@ epoll_reactor::epoll_reactor(boost::asio::io_service& io_service) epoll_reactor::~epoll_reactor() { - close(epoll_fd_); + if (epoll_fd_ != -1) + close(epoll_fd_); if (timer_fd_ != -1) close(timer_fd_); } @@ -88,6 +85,59 @@ void epoll_reactor::shutdown_service() } timer_queues_.get_all_timers(ops); + + io_service_.abandon_operations(ops); +} + +void epoll_reactor::fork_service(boost::asio::io_service::fork_event fork_ev) +{ + if (fork_ev == boost::asio::io_service::fork_child) + { + if (epoll_fd_ != -1) + ::close(epoll_fd_); + epoll_fd_ = -1; + epoll_fd_ = do_epoll_create(); + + if (timer_fd_ != -1) + ::close(timer_fd_); + timer_fd_ = -1; + timer_fd_ = do_timerfd_create(); + + interrupter_.recreate(); + + // Add the interrupter's descriptor to epoll. + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLET; + ev.data.ptr = &interrupter_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); + interrupter_.interrupt(); + + // Add the timer descriptor to epoll. + if (timer_fd_ != -1) + { + ev.events = EPOLLIN | EPOLLERR; + ev.data.ptr = &timer_fd_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &ev); + } + + update_timeout(); + + // Re-register all descriptors with epoll. + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + for (descriptor_state* state = registered_descriptors_.first(); + state != 0; state = state->next_) + { + ev.events = state->registered_events_; + ev.data.ptr = state; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, state->descriptor_, &ev); + if (result != 0) + { + boost::system::error_code ec(errno, + boost::asio::error::get_system_category()); + boost::asio::detail::throw_error(ec, "epoll re-registration"); + } + } + } } void epoll_reactor::init_task() @@ -98,15 +148,19 @@ void epoll_reactor::init_task() int epoll_reactor::register_descriptor(socket_type descriptor, epoll_reactor::per_descriptor_data& descriptor_data) { - mutex::scoped_lock lock(registered_descriptors_mutex_); + descriptor_data = allocate_descriptor_state(); - descriptor_data = registered_descriptors_.alloc(); - descriptor_data->shutdown_ = false; + { + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - lock.unlock(); + descriptor_data->reactor_ = this; + descriptor_data->descriptor_ = descriptor; + descriptor_data->shutdown_ = false; + } epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLOUT | EPOLLPRI | EPOLLET; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLPRI | EPOLLET; + descriptor_data->registered_events_ = ev.events; ev.data.ptr = descriptor_data; int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); if (result != 0) @@ -115,6 +169,40 @@ int epoll_reactor::register_descriptor(socket_type descriptor, return 0; } +int epoll_reactor::register_internal_descriptor( + int op_type, socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data, reactor_op* op) +{ + descriptor_data = allocate_descriptor_state(); + + { + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + descriptor_data->reactor_ = this; + descriptor_data->descriptor_ = descriptor; + descriptor_data->shutdown_ = false; + descriptor_data->op_queue_[op_type].push(op); + } + + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLPRI | EPOLLET; + descriptor_data->registered_events_ = ev.events; + ev.data.ptr = descriptor_data; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); + if (result != 0) + return errno; + + return 0; +} + +void epoll_reactor::move_descriptor(socket_type, + epoll_reactor::per_descriptor_data& target_descriptor_data, + epoll_reactor::per_descriptor_data& source_descriptor_data) +{ + target_descriptor_data = source_descriptor_data; + source_descriptor_data = 0; +} + void epoll_reactor::start_op(int op_type, socket_type descriptor, epoll_reactor::per_descriptor_data& descriptor_data, reactor_op* op, bool allow_speculative) @@ -146,12 +234,37 @@ void epoll_reactor::start_op(int op_type, socket_type descriptor, io_service_.post_immediate_completion(op); return; } + + if (op_type == write_op) + { + if ((descriptor_data->registered_events_ & EPOLLOUT) == 0) + { + epoll_event ev = { 0, { 0 } }; + ev.events = descriptor_data->registered_events_ | EPOLLOUT; + ev.data.ptr = descriptor_data; + if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev) == 0) + { + descriptor_data->registered_events_ |= ev.events; + } + else + { + op->ec_ = boost::system::error_code(errno, + boost::asio::error::get_system_category()); + io_service_.post_immediate_completion(op); + return; + } + } + } } else { + if (op_type == write_op) + { + descriptor_data->registered_events_ |= EPOLLOUT; + } + epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLIN | EPOLLERR | EPOLLHUP - | EPOLLOUT | EPOLLPRI | EPOLLET; + ev.events = descriptor_data->registered_events_; ev.data.ptr = descriptor_data; epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); } @@ -185,19 +298,26 @@ void epoll_reactor::cancel_ops(socket_type, io_service_.post_deferred_completions(ops); } -void epoll_reactor::close_descriptor(socket_type, - epoll_reactor::per_descriptor_data& descriptor_data) +void epoll_reactor::deregister_descriptor(socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data, bool closing) { if (!descriptor_data) return; mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); if (!descriptor_data->shutdown_) { - // Remove the descriptor from the set of known descriptors. The descriptor - // will be automatically removed from the epoll set when it is closed. + if (closing) + { + // The descriptor will be automatically removed from the epoll set when + // it is closed. + } + else + { + epoll_event ev = { 0, { 0 } }; + epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev); + } op_queue<operation> ops; for (int i = 0; i < max_ops; ++i) @@ -210,21 +330,53 @@ void epoll_reactor::close_descriptor(socket_type, } } + descriptor_data->descriptor_ = -1; descriptor_data->shutdown_ = true; descriptor_lock.unlock(); - registered_descriptors_.free(descriptor_data); + free_descriptor_state(descriptor_data); descriptor_data = 0; - descriptors_lock.unlock(); - io_service_.post_deferred_completions(ops); } } +void epoll_reactor::deregister_internal_descriptor(socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (!descriptor_data->shutdown_) + { + epoll_event ev = { 0, { 0 } }; + epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev); + + op_queue<operation> ops; + for (int i = 0; i < max_ops; ++i) + ops.push(descriptor_data->op_queue_[i]); + + descriptor_data->descriptor_ = -1; + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + free_descriptor_state(descriptor_data); + descriptor_data = 0; + } +} + void epoll_reactor::run(bool block, op_queue<operation>& ops) { + // This code relies on the fact that the task_io_service queues the reactor + // task behind all descriptor operations generated by this function. This + // means, that by the time we reach this point, any previously returned + // descriptor operations have already been dequeued. Therefore it is now safe + // for us to reuse and return them for the task_io_service to queue again. + // Calculate a timeout only if timerfd is not used. int timeout; if (timer_fd_ != -1) @@ -271,28 +423,12 @@ void epoll_reactor::run(bool block, op_queue<operation>& ops) #endif // defined(BOOST_ASIO_HAS_TIMERFD) else { + // The descriptor operation doesn't count as work in and of itself, so we + // don't call work_started() here. This still allows the io_service to + // stop if the only remaining operations are descriptor operations. descriptor_state* descriptor_data = static_cast<descriptor_state*>(ptr); - mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - - // Exception operations must be processed first to ensure that any - // out-of-band data is read before normal data. - static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI }; - for (int j = max_ops - 1; j >= 0; --j) - { - if (events[i].events & (flag[j] | EPOLLERR | EPOLLHUP)) - { - while (reactor_op* op = descriptor_data->op_queue_[j].front()) - { - if (op->perform()) - { - descriptor_data->op_queue_[j].pop(); - ops.push(op); - } - else - break; - } - } - } + descriptor_data->set_ready_events(events[i].events); + ops.push(descriptor_data); } } @@ -323,16 +459,65 @@ void epoll_reactor::interrupt() int epoll_reactor::do_epoll_create() { - int fd = epoll_create(epoll_size); +#if defined(EPOLL_CLOEXEC) + int fd = epoll_create1(EPOLL_CLOEXEC); +#else // defined(EPOLL_CLOEXEC) + int fd = -1; + errno = EINVAL; +#endif // defined(EPOLL_CLOEXEC) + + if (fd == -1 && (errno == EINVAL || errno == ENOSYS)) + { + fd = epoll_create(epoll_size); + if (fd != -1) + ::fcntl(fd, F_SETFD, FD_CLOEXEC); + } + if (fd == -1) { boost::system::error_code ec(errno, boost::asio::error::get_system_category()); boost::asio::detail::throw_error(ec, "epoll"); } + return fd; } +int epoll_reactor::do_timerfd_create() +{ +#if defined(BOOST_ASIO_HAS_TIMERFD) +# if defined(TFD_CLOEXEC) + int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); +# else // defined(TFD_CLOEXEC) + int fd = -1; + errno = EINVAL; +# endif // defined(TFD_CLOEXEC) + + if (fd == -1 && errno == EINVAL) + { + fd = timerfd_create(CLOCK_MONOTONIC, 0); + if (fd != -1) + ::fcntl(fd, F_SETFD, FD_CLOEXEC); + } + + return fd; +#else // defined(BOOST_ASIO_HAS_TIMERFD) + return -1; +#endif // defined(BOOST_ASIO_HAS_TIMERFD) +} + +epoll_reactor::descriptor_state* epoll_reactor::allocate_descriptor_state() +{ + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + return registered_descriptors_.alloc(); +} + +void epoll_reactor::free_descriptor_state(epoll_reactor::descriptor_state* s) +{ + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + registered_descriptors_.free(s); +} + void epoll_reactor::do_add_timer_queue(timer_queue_base& queue) { mutex::scoped_lock lock(mutex_); @@ -381,6 +566,92 @@ int epoll_reactor::get_timeout(itimerspec& ts) } #endif // defined(BOOST_ASIO_HAS_TIMERFD) +struct epoll_reactor::perform_io_cleanup_on_block_exit +{ + explicit perform_io_cleanup_on_block_exit(epoll_reactor* r) + : reactor_(r), first_op_(0) + { + } + + ~perform_io_cleanup_on_block_exit() + { + if (first_op_) + { + // Post the remaining completed operations for invocation. + if (!ops_.empty()) + reactor_->io_service_.post_deferred_completions(ops_); + + // A user-initiated operation has completed, but there's no need to + // explicitly call work_finished() here. Instead, we'll take advantage of + // the fact that the task_io_service will call work_finished() once we + // return. + } + else + { + // No user-initiated operations have completed, so we need to compensate + // for the work_finished() call that the task_io_service will make once + // this operation returns. + reactor_->io_service_.work_started(); + } + } + + epoll_reactor* reactor_; + op_queue<operation> ops_; + operation* first_op_; +}; + +epoll_reactor::descriptor_state::descriptor_state() + : operation(&epoll_reactor::descriptor_state::do_complete) +{ +} + +operation* epoll_reactor::descriptor_state::perform_io(uint32_t events) +{ + perform_io_cleanup_on_block_exit io_cleanup(reactor_); + mutex::scoped_lock descriptor_lock(mutex_); + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI }; + for (int j = max_ops - 1; j >= 0; --j) + { + if (events & (flag[j] | EPOLLERR | EPOLLHUP)) + { + while (reactor_op* op = op_queue_[j].front()) + { + if (op->perform()) + { + op_queue_[j].pop(); + io_cleanup.ops_.push(op); + } + else + break; + } + } + } + + // The first operation will be returned for completion now. The others will + // be posted for later by the io_cleanup object's destructor. + io_cleanup.first_op_ = io_cleanup.ops_.front(); + io_cleanup.ops_.pop(); + return io_cleanup.first_op_; +} + +void epoll_reactor::descriptor_state::do_complete( + io_service_impl* owner, operation* base, + const boost::system::error_code& ec, std::size_t bytes_transferred) +{ + if (owner) + { + descriptor_state* descriptor_data = static_cast<descriptor_state*>(base); + uint32_t events = static_cast<uint32_t>(bytes_transferred); + if (operation* op = descriptor_data->perform_io(events)) + { + op->complete(*owner, ec, 0); + } + } +} + } // namespace detail } // namespace asio } // namespace boost |