diff options
Diffstat (limited to '3rdParty/Boost/src/boost/asio/detail')
67 files changed, 5277 insertions, 6606 deletions
diff --git a/3rdParty/Boost/src/boost/asio/detail/base_from_completion_cond.hpp b/3rdParty/Boost/src/boost/asio/detail/base_from_completion_cond.hpp new file mode 100644 index 0000000..1b1f142 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/base_from_completion_cond.hpp @@ -0,0 +1,67 @@ +// +// base_from_completion_cond.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP +#define BOOST_ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/completion_condition.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename CompletionCondition> +class base_from_completion_cond +{ +protected: + explicit base_from_completion_cond(CompletionCondition completion_condition) + : completion_condition_(completion_condition) + { + } + + std::size_t check(const boost::system::error_code& ec, + std::size_t total_transferred) + { + return detail::adapt_completion_condition_result( + completion_condition_(ec, total_transferred)); + } + +private: + CompletionCondition completion_condition_; +}; + +template <> +class base_from_completion_cond<transfer_all_t> +{ +protected: + explicit base_from_completion_cond(transfer_all_t) + { + } + + static std::size_t check(const boost::system::error_code& ec, + std::size_t total_transferred) + { + return transfer_all_t()(ec, total_transferred); + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/buffer_sequence_adapter.hpp b/3rdParty/Boost/src/boost/asio/detail/buffer_sequence_adapter.hpp new file mode 100644 index 0000000..ce9652e --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/buffer_sequence_adapter.hpp @@ -0,0 +1,254 @@ +// +// buffer_sequence_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP +#define BOOST_ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/buffer.hpp> + +namespace boost { +namespace asio { +namespace detail { + +class buffer_sequence_adapter_base +{ +protected: +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + typedef WSABUF native_buffer_type; + + static void init_native_buffer(WSABUF& buf, + const boost::asio::mutable_buffer& buffer) + { + buf.buf = boost::asio::buffer_cast<char*>(buffer); + buf.len = boost::asio::buffer_size(buffer); + } + + static void init_native_buffer(WSABUF& buf, + const boost::asio::const_buffer& buffer) + { + buf.buf = const_cast<char*>(boost::asio::buffer_cast<const char*>(buffer)); + buf.len = boost::asio::buffer_size(buffer); + } +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + typedef iovec native_buffer_type; + + static void init_iov_base(void*& base, void* addr) + { + base = addr; + } + + template <typename T> + static void init_iov_base(T& base, void* addr) + { + base = static_cast<T>(addr); + } + + static void init_native_buffer(iovec& iov, + const boost::asio::mutable_buffer& buffer) + { + init_iov_base(iov.iov_base, boost::asio::buffer_cast<void*>(buffer)); + iov.iov_len = boost::asio::buffer_size(buffer); + } + + static void init_native_buffer(iovec& iov, + const boost::asio::const_buffer& buffer) + { + init_iov_base(iov.iov_base, const_cast<void*>( + boost::asio::buffer_cast<const void*>(buffer))); + iov.iov_len = boost::asio::buffer_size(buffer); + } +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +}; + +// Helper class to translate buffers into the native buffer representation. +template <typename Buffer, typename Buffers> +class buffer_sequence_adapter + : buffer_sequence_adapter_base +{ +public: + explicit buffer_sequence_adapter(const Buffers& buffers) + : count_(0), total_buffer_size_(0) + { + typename Buffers::const_iterator iter = buffers.begin(); + typename Buffers::const_iterator end = buffers.end(); + for (; iter != end && count_ < max_buffers; ++iter, ++count_) + { + Buffer buffer(*iter); + init_native_buffer(buffers_[count_], buffer); + total_buffer_size_ += boost::asio::buffer_size(buffer); + } + } + + native_buffer_type* buffers() + { + return buffers_; + } + + std::size_t count() const + { + return count_; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const Buffers& buffers) + { + typename Buffers::const_iterator iter = buffers.begin(); + typename Buffers::const_iterator end = buffers.end(); + std::size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + if (boost::asio::buffer_size(Buffer(*iter)) > 0) + return false; + return true; + } + + static void validate(const Buffers& buffers) + { + typename Buffers::const_iterator iter = buffers.begin(); + typename Buffers::const_iterator end = buffers.end(); + for (; iter != end; ++iter) + { + Buffer buffer(*iter); + boost::asio::buffer_cast<const void*>(buffer); + } + } + + static Buffer first(const Buffers& buffers) + { + typename Buffers::const_iterator iter = buffers.begin(); + typename Buffers::const_iterator end = buffers.end(); + for (; iter != end; ++iter) + { + Buffer buffer(*iter); + if (boost::asio::buffer_size(buffer) != 0) + return buffer; + } + return Buffer(); + } + +private: + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; + + native_buffer_type buffers_[max_buffers]; + std::size_t count_; + std::size_t total_buffer_size_; +}; + +template <typename Buffer> +class buffer_sequence_adapter<Buffer, boost::asio::mutable_buffers_1> + : buffer_sequence_adapter_base +{ +public: + explicit buffer_sequence_adapter( + const boost::asio::mutable_buffers_1& buffers) + { + init_native_buffer(buffer_, buffers); + total_buffer_size_ = boost::asio::buffer_size(buffers); + } + + native_buffer_type* buffers() + { + return &buffer_; + } + + std::size_t count() const + { + return 1; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const boost::asio::mutable_buffers_1& buffers) + { + return boost::asio::buffer_size(buffers) == 0; + } + + static void validate(const boost::asio::mutable_buffers_1& buffers) + { + boost::asio::buffer_cast<const void*>(buffers); + } + + static Buffer first(const boost::asio::mutable_buffers_1& buffers) + { + return Buffer(buffers); + } + +private: + native_buffer_type buffer_; + std::size_t total_buffer_size_; +}; + +template <typename Buffer> +class buffer_sequence_adapter<Buffer, boost::asio::const_buffers_1> + : buffer_sequence_adapter_base +{ +public: + explicit buffer_sequence_adapter( + const boost::asio::const_buffers_1& buffers) + { + init_native_buffer(buffer_, buffers); + total_buffer_size_ = boost::asio::buffer_size(buffers); + } + + native_buffer_type* buffers() + { + return &buffer_; + } + + std::size_t count() const + { + return 1; + } + + bool all_empty() const + { + return total_buffer_size_ == 0; + } + + static bool all_empty(const boost::asio::const_buffers_1& buffers) + { + return boost::asio::buffer_size(buffers) == 0; + } + + static void validate(const boost::asio::const_buffers_1& buffers) + { + boost::asio::buffer_cast<const void*>(buffers); + } + + static Buffer first(const boost::asio::const_buffers_1& buffers) + { + return Buffer(buffers); + } + +private: + native_buffer_type buffer_; + std::size_t total_buffer_size_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/completion_handler.hpp b/3rdParty/Boost/src/boost/asio/detail/completion_handler.hpp new file mode 100644 index 0000000..d78e6c8 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/completion_handler.hpp @@ -0,0 +1,73 @@ +// +// completion_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_COMPLETION_HANDLER_HPP +#define BOOST_ASIO_DETAIL_COMPLETION_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/fenced_block.hpp> +#include <boost/asio/detail/handler_alloc_helpers.hpp> +#include <boost/asio/detail/handler_invoke_helpers.hpp> +#include <boost/asio/detail/operation.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename Handler> +class completion_handler : public operation +{ +public: + completion_handler(Handler h) + : operation(&completion_handler::do_complete), + handler_(h) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + completion_handler* h(static_cast<completion_handler*>(base)); + typedef handler_alloc_traits<Handler, completion_handler> alloc_traits; + handler_ptr<alloc_traits> ptr(h->handler_, h); + + // Make the upcall if required. + if (owner) + { + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + Handler handler(h->handler_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_COMPLETION_HANDLER_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/consuming_buffers.hpp b/3rdParty/Boost/src/boost/asio/detail/consuming_buffers.hpp index bf04b1b..3604cba 100644 --- a/3rdParty/Boost/src/boost/asio/detail/consuming_buffers.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/consuming_buffers.hpp @@ -21,12 +21,11 @@ #include <algorithm> #include <cstddef> #include <boost/config.hpp> -#include <boost/iterator/iterator_facade.hpp> +#include <boost/iterator.hpp> #include <boost/limits.hpp> #include <boost/asio/detail/pop_options.hpp> #include <boost/asio/buffer.hpp> -#include <boost/asio/completion_condition.hpp> namespace boost { namespace asio { @@ -35,9 +34,7 @@ namespace detail { // A proxy iterator for a sub-range in a list of buffers. template <typename Buffer, typename Buffer_Iterator> class consuming_buffers_iterator - : public boost::iterator_facade< - consuming_buffers_iterator<Buffer, Buffer_Iterator>, - const Buffer, boost::forward_traversal_tag> + : public boost::iterator<std::forward_iterator_tag, const Buffer> { public: // Default constructor creates an end iterator. @@ -60,9 +57,48 @@ public: { } -private: - friend class boost::iterator_core_access; + // Dereference an iterator. + const Buffer& operator*() const + { + return dereference(); + } + + // Dereference an iterator. + const Buffer* operator->() const + { + return &dereference(); + } + + // Increment operator (prefix). + consuming_buffers_iterator& operator++() + { + increment(); + return *this; + } + // Increment operator (postfix). + consuming_buffers_iterator operator++(int) + { + consuming_buffers_iterator tmp(*this); + ++*this; + return tmp; + } + + // Test two iterators for equality. + friend bool operator==(const consuming_buffers_iterator& a, + const consuming_buffers_iterator& b) + { + return a.equal(b); + } + + // Test two iterators for inequality. + friend bool operator!=(const consuming_buffers_iterator& a, + const consuming_buffers_iterator& b) + { + return !a.equal(b); + } + +private: void increment() { if (!at_end_) @@ -170,7 +206,7 @@ public: } // Set the maximum size for a single transfer. - void set_max_size(std::size_t max_size) + void prepare(std::size_t max_size) { max_size_ = max_size; } @@ -226,7 +262,7 @@ public: // No-op. } - void set_max_size(std::size_t) + void prepare(std::size_t) { // No-op. } diff --git a/3rdParty/Boost/src/boost/asio/detail/deadline_timer_service.hpp b/3rdParty/Boost/src/boost/asio/detail/deadline_timer_service.hpp index 7885c9a..5ba21b1 100644 --- a/3rdParty/Boost/src/boost/asio/detail/deadline_timer_service.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/deadline_timer_service.hpp @@ -26,21 +26,20 @@ #include <boost/asio/error.hpp> #include <boost/asio/io_service.hpp> #include <boost/asio/detail/bind_handler.hpp> -#include <boost/asio/detail/handler_base_from_member.hpp> +#include <boost/asio/detail/fenced_block.hpp> #include <boost/asio/detail/noncopyable.hpp> -#include <boost/asio/detail/service_base.hpp> #include <boost/asio/detail/socket_ops.hpp> #include <boost/asio/detail/socket_types.hpp> +#include <boost/asio/detail/timer_op.hpp> #include <boost/asio/detail/timer_queue.hpp> +#include <boost/asio/detail/timer_scheduler.hpp> namespace boost { namespace asio { namespace detail { -template <typename Time_Traits, typename Timer_Scheduler> +template <typename Time_Traits> class deadline_timer_service - : public boost::asio::detail::service_base< - deadline_timer_service<Time_Traits, Timer_Scheduler> > { public: // The time type. @@ -60,9 +59,7 @@ public: // Constructor. deadline_timer_service(boost::asio::io_service& io_service) - : boost::asio::detail::service_base< - deadline_timer_service<Time_Traits, Timer_Scheduler> >(io_service), - scheduler_(boost::asio::use_service<Timer_Scheduler>(io_service)) + : scheduler_(boost::asio::use_service<timer_scheduler>(io_service)) { scheduler_.init_task(); scheduler_.add_timer_queue(timer_queue_); @@ -156,34 +153,58 @@ public: } template <typename Handler> - class wait_handler : - public handler_base_from_member<Handler> + class wait_handler : public timer_op { public: - wait_handler(boost::asio::io_service& io_service, Handler handler) - : handler_base_from_member<Handler>(handler), - io_service_(io_service), - work_(io_service) + wait_handler(Handler handler) + : timer_op(&wait_handler::do_complete), + handler_(handler) { } - void operator()(const boost::system::error_code& result) + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) { - io_service_.post(detail::bind_handler(this->handler_, result)); + // Take ownership of the handler object. + wait_handler* h(static_cast<wait_handler*>(base)); + typedef handler_alloc_traits<Handler, wait_handler> alloc_traits; + handler_ptr<alloc_traits> ptr(h->handler_, h); + + // Make the upcall if required. + if (owner) + { + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder1<Handler, boost::system::error_code> + handler(h->handler_, h->ec_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } private: - boost::asio::io_service& io_service_; - boost::asio::io_service::work work_; + Handler handler_; }; // Start an asynchronous wait on the timer. template <typename Handler> void async_wait(implementation_type& impl, Handler handler) { + // Allocate and construct an operation to wrap the handler. + typedef wait_handler<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); + impl.might_have_pending_waits = true; - scheduler_.schedule_timer(timer_queue_, impl.expiry, - wait_handler<Handler>(this->get_io_service(), handler), &impl); + + scheduler_.schedule_timer(timer_queue_, impl.expiry, ptr.get(), &impl); + ptr.release(); } private: @@ -191,7 +212,7 @@ private: timer_queue<Time_Traits> timer_queue_; // The object that schedules and executes timers. Usually a reactor. - Timer_Scheduler& scheduler_; + timer_scheduler& scheduler_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/dev_poll_reactor.hpp b/3rdParty/Boost/src/boost/asio/detail/dev_poll_reactor.hpp index 7315384..e00f501 100644 --- a/3rdParty/Boost/src/boost/asio/detail/dev_poll_reactor.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/dev_poll_reactor.hpp @@ -33,27 +33,30 @@ #include <boost/asio/error.hpp> #include <boost/asio/io_service.hpp> -#include <boost/asio/detail/bind_handler.hpp> #include <boost/asio/detail/hash_map.hpp> #include <boost/asio/detail/mutex.hpp> -#include <boost/asio/detail/task_io_service.hpp> -#include <boost/asio/detail/thread.hpp> +#include <boost/asio/detail/op_queue.hpp> +#include <boost/asio/detail/reactor_op.hpp> #include <boost/asio/detail/reactor_op_queue.hpp> #include <boost/asio/detail/select_interrupter.hpp> #include <boost/asio/detail/service_base.hpp> -#include <boost/asio/detail/signal_blocker.hpp> #include <boost/asio/detail/socket_types.hpp> -#include <boost/asio/detail/timer_queue.hpp> +#include <boost/asio/detail/timer_op.hpp> +#include <boost/asio/detail/timer_queue_base.hpp> +#include <boost/asio/detail/timer_queue_fwd.hpp> +#include <boost/asio/detail/timer_queue_set.hpp> namespace boost { namespace asio { namespace detail { -template <bool Own_Thread> class dev_poll_reactor - : public boost::asio::detail::service_base<dev_poll_reactor<Own_Thread> > + : public boost::asio::detail::service_base<dev_poll_reactor> { public: + enum { read_op = 0, write_op = 1, + connect_op = 1, except_op = 2, max_ops = 3 }; + // Per-descriptor data. struct per_descriptor_data { @@ -61,28 +64,13 @@ public: // Constructor. dev_poll_reactor(boost::asio::io_service& io_service) - : boost::asio::detail::service_base< - dev_poll_reactor<Own_Thread> >(io_service), + : boost::asio::detail::service_base<dev_poll_reactor>(io_service), + io_service_(use_service<io_service_impl>(io_service)), mutex_(), dev_poll_fd_(do_dev_poll_create()), - wait_in_progress_(false), interrupter_(), - read_op_queue_(), - write_op_queue_(), - except_op_queue_(), - pending_cancellations_(), - stop_thread_(false), - thread_(0), shutdown_(false) { - // Start the reactor's internal thread only if needed. - if (Own_Thread) - { - boost::asio::detail::signal_blocker sb; - thread_ = new boost::asio::detail::thread( - bind_handler(&dev_poll_reactor::call_run_thread, this)); - } - // Add the interrupter's descriptor to /dev/poll. ::pollfd ev = { 0 }; ev.fd = interrupter_.read_descriptor(); @@ -103,35 +91,20 @@ public: { boost::asio::detail::mutex::scoped_lock lock(mutex_); shutdown_ = true; - stop_thread_ = true; lock.unlock(); - if (thread_) - { - interrupter_.interrupt(); - thread_->join(); - delete thread_; - thread_ = 0; - } + op_queue<operation> ops; - read_op_queue_.destroy_operations(); - write_op_queue_.destroy_operations(); - except_op_queue_.destroy_operations(); + for (int i = 0; i < max_ops; ++i) + op_queue_[i].get_all_operations(ops); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - timer_queues_[i]->destroy_timers(); - timer_queues_.clear(); + timer_queues_.get_all_timers(ops); } - // Initialise the task, but only if the reactor is not in its own thread. + // Initialise the task. void init_task() { - if (!Own_Thread) - { - typedef task_io_service<dev_poll_reactor<Own_Thread> > - task_io_service_type; - use_service<task_io_service_type>(this->get_io_service()).init_task(); - } + io_service_.init_task(); } // Register a socket with the reactor. Returns 0 on success, system error @@ -141,121 +114,46 @@ public: return 0; } - // Start a new read operation. The handler object will be invoked when the - // given descriptor is ready to be read, or an error has occurred. - template <typename Handler> - void start_read_op(socket_type descriptor, per_descriptor_data&, - Handler handler, bool allow_speculative_read = true) - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - - if (shutdown_) - return; - - if (allow_speculative_read) - { - if (!read_op_queue_.has_operation(descriptor)) - { - boost::system::error_code ec; - std::size_t bytes_transferred = 0; - if (handler.perform(ec, bytes_transferred)) - { - handler.complete(ec, bytes_transferred); - return; - } - } - } - - if (read_op_queue_.enqueue_operation(descriptor, handler)) - { - ::pollfd& ev = add_pending_event_change(descriptor); - ev.events = POLLIN | POLLERR | POLLHUP; - if (write_op_queue_.has_operation(descriptor)) - ev.events |= POLLOUT; - if (except_op_queue_.has_operation(descriptor)) - ev.events |= POLLPRI; - interrupter_.interrupt(); - } - } - - // Start a new write operation. The handler object will be invoked when the - // given descriptor is ready to be written, or an error has occurred. - template <typename Handler> - void start_write_op(socket_type descriptor, per_descriptor_data&, - Handler handler, bool allow_speculative_write = true) + // Start a new operation. The reactor operation will be performed when the + // given descriptor is flagged as ready, or an error has occurred. + void start_op(int op_type, socket_type descriptor, + per_descriptor_data&, reactor_op* op, bool allow_speculative) { boost::asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) return; - if (allow_speculative_write) + if (allow_speculative) { - if (!write_op_queue_.has_operation(descriptor)) + if (op_type != read_op || !op_queue_[except_op].has_operation(descriptor)) { - boost::system::error_code ec; - std::size_t bytes_transferred = 0; - if (handler.perform(ec, bytes_transferred)) + if (!op_queue_[op_type].has_operation(descriptor)) { - handler.complete(ec, bytes_transferred); - return; + if (op->perform()) + { + lock.unlock(); + io_service_.post_immediate_completion(op); + return; + } } } } - if (write_op_queue_.enqueue_operation(descriptor, handler)) + bool first = op_queue_[op_type].enqueue_operation(descriptor, op); + io_service_.work_started(); + if (first) { ::pollfd& ev = add_pending_event_change(descriptor); - ev.events = POLLOUT | POLLERR | POLLHUP; - if (read_op_queue_.has_operation(descriptor)) + ev.events = POLLERR | POLLHUP; + if (op_type == read_op + || op_queue_[read_op].has_operation(descriptor)) ev.events |= POLLIN; - if (except_op_queue_.has_operation(descriptor)) - ev.events |= POLLPRI; - interrupter_.interrupt(); - } - } - - // Start a new exception operation. The handler object will be invoked when - // the given descriptor has exception information, or an error has occurred. - template <typename Handler> - void start_except_op(socket_type descriptor, - per_descriptor_data&, Handler handler) - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - - if (shutdown_) - return; - - if (except_op_queue_.enqueue_operation(descriptor, handler)) - { - ::pollfd& ev = add_pending_event_change(descriptor); - ev.events = POLLPRI | POLLERR | POLLHUP; - if (read_op_queue_.has_operation(descriptor)) - ev.events |= POLLIN; - if (write_op_queue_.has_operation(descriptor)) + if (op_type == write_op + || op_queue_[write_op].has_operation(descriptor)) ev.events |= POLLOUT; - interrupter_.interrupt(); - } - } - - // Start a new write operation. The handler object will be invoked when the - // information available, or an error has occurred. - template <typename Handler> - void start_connect_op(socket_type descriptor, - per_descriptor_data&, Handler handler) - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - - if (shutdown_) - return; - - if (write_op_queue_.enqueue_operation(descriptor, handler)) - { - ::pollfd& ev = add_pending_event_change(descriptor); - ev.events = POLLOUT | POLLERR | POLLHUP; - if (read_op_queue_.has_operation(descriptor)) - ev.events |= POLLIN; - if (except_op_queue_.has_operation(descriptor)) + if (op_type == except_op + || op_queue_[except_op].has_operation(descriptor)) ev.events |= POLLPRI; interrupter_.interrupt(); } @@ -267,7 +165,7 @@ public: void cancel_ops(socket_type descriptor, per_descriptor_data&) { boost::asio::detail::mutex::scoped_lock lock(mutex_); - cancel_ops_unlocked(descriptor); + cancel_ops_unlocked(descriptor, boost::asio::error::operation_aborted); } // Cancel any operations that are running against the descriptor and remove @@ -282,7 +180,7 @@ public: interrupter_.interrupt(); // Cancel any outstanding operations associated with the descriptor. - cancel_ops_unlocked(descriptor); + cancel_ops_unlocked(descriptor, boost::asio::error::operation_aborted); } // Add a new timer queue to the reactor. @@ -290,7 +188,7 @@ public: void add_timer_queue(timer_queue<Time_Traits>& timer_queue) { boost::asio::detail::mutex::scoped_lock lock(mutex_); - timer_queues_.push_back(&timer_queue); + timer_queues_.insert(&timer_queue); } // Remove a timer queue from the reactor. @@ -298,71 +196,48 @@ public: void remove_timer_queue(timer_queue<Time_Traits>& timer_queue) { boost::asio::detail::mutex::scoped_lock lock(mutex_); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - if (timer_queues_[i] == &timer_queue) - { - timer_queues_.erase(timer_queues_.begin() + i); - return; - } - } + timer_queues_.erase(&timer_queue); } - // Schedule a timer in the given timer queue to expire at the specified - // absolute time. The handler object will be invoked when the timer expires. - template <typename Time_Traits, typename Handler> + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template <typename Time_Traits> void schedule_timer(timer_queue<Time_Traits>& timer_queue, - const typename Time_Traits::time_type& time, Handler handler, void* token) + const typename Time_Traits::time_type& time, timer_op* op, void* token) { boost::asio::detail::mutex::scoped_lock lock(mutex_); if (!shutdown_) - if (timer_queue.enqueue_timer(time, handler, token)) + { + bool earliest = timer_queue.enqueue_timer(time, op, token); + io_service_.work_started(); + if (earliest) interrupter_.interrupt(); + } } - // Cancel the timer associated with the given token. Returns the number of - // handlers that have been posted or dispatched. + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. template <typename Time_Traits> std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token) { boost::asio::detail::mutex::scoped_lock lock(mutex_); - std::size_t n = timer_queue.cancel_timer(token); - if (n > 0) - interrupter_.interrupt(); + op_queue<operation> ops; + std::size_t n = timer_queue.cancel_timer(token, ops); + lock.unlock(); + io_service_.post_deferred_completions(ops); return n; } -private: - friend class task_io_service<dev_poll_reactor<Own_Thread> >; - // Run /dev/poll once until interrupted or events are ready to be dispatched. - void run(bool block) + void run(bool block, op_queue<operation>& ops) { boost::asio::detail::mutex::scoped_lock lock(mutex_); - // Dispatch any operation cancellations that were made while the select - // loop was not running. - read_op_queue_.perform_cancellations(); - write_op_queue_.perform_cancellations(); - except_op_queue_.perform_cancellations(); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - timer_queues_[i]->dispatch_cancellations(); - - // Check if the thread is supposed to stop. - if (stop_thread_) - { - complete_operations_and_timers(lock); - return; - } - // We can return immediately if there's no work to do and the reactor is // not supposed to block. - if (!block && read_op_queue_.empty() && write_op_queue_.empty() - && except_op_queue_.empty() && all_timer_queues_are_empty()) - { - complete_operations_and_timers(lock); + if (!block && op_queue_[read_op].empty() && op_queue_[write_op].empty() + && op_queue_[except_op].empty() && timer_queues_.all_empty()) return; - } // Write the pending event registration changes to the /dev/poll descriptor. std::size_t events_size = sizeof(::pollfd) * pending_event_changes_.size(); @@ -373,14 +248,13 @@ private: &pending_event_changes_[0], events_size); if (result != static_cast<int>(events_size)) { + boost::system::error_code ec = boost::system::error_code( + errno, boost::asio::error::get_system_category()); for (std::size_t i = 0; i < pending_event_changes_.size(); ++i) { int descriptor = pending_event_changes_[i].fd; - boost::system::error_code ec = boost::system::error_code( - errno, boost::asio::error::get_system_category()); - read_op_queue_.perform_all_operations(descriptor, ec); - write_op_queue_.perform_all_operations(descriptor, ec); - except_op_queue_.perform_all_operations(descriptor, ec); + for (int j = 0; j < max_ops; ++j) + op_queue_[j].cancel_operations(descriptor, ops, ec); } } pending_event_changes_.clear(); @@ -388,7 +262,6 @@ private: } int timeout = block ? get_timeout() : 0; - wait_in_progress_ = true; lock.unlock(); // Block on the /dev/poll descriptor. @@ -400,7 +273,6 @@ private: int num_events = ::ioctl(dev_poll_fd_, DP_POLL, &dp); lock.lock(); - wait_in_progress_ = false; // Dispatch the waiting events. for (int i = 0; i < num_events; ++i) @@ -415,24 +287,24 @@ private: bool more_reads = false; bool more_writes = false; bool more_except = false; - boost::system::error_code ec; // Exception operations must be processed first to ensure that any // out-of-band data is read before normal data. if (events[i].events & (POLLPRI | POLLERR | POLLHUP)) - more_except = except_op_queue_.perform_operation(descriptor, ec); + more_except = + op_queue_[except_op].perform_operations(descriptor, ops); else - more_except = except_op_queue_.has_operation(descriptor); + more_except = op_queue_[except_op].has_operation(descriptor); if (events[i].events & (POLLIN | POLLERR | POLLHUP)) - more_reads = read_op_queue_.perform_operation(descriptor, ec); + more_reads = op_queue_[read_op].perform_operations(descriptor, ops); else - more_reads = read_op_queue_.has_operation(descriptor); + more_reads = op_queue_[read_op].has_operation(descriptor); if (events[i].events & (POLLOUT | POLLERR | POLLHUP)) - more_writes = write_op_queue_.perform_operation(descriptor, ec); + more_writes = op_queue_[write_op].perform_operations(descriptor, ops); else - more_writes = write_op_queue_.has_operation(descriptor); + more_writes = op_queue_[write_op].has_operation(descriptor); if ((events[i].events & (POLLERR | POLLHUP)) != 0 && !more_except && !more_reads && !more_writes) @@ -463,48 +335,15 @@ private: int result = ::write(dev_poll_fd_, &ev, sizeof(ev)); if (result != sizeof(ev)) { - ec = boost::system::error_code(errno, + boost::system::error_code ec(errno, boost::asio::error::get_system_category()); - read_op_queue_.perform_all_operations(descriptor, ec); - write_op_queue_.perform_all_operations(descriptor, ec); - except_op_queue_.perform_all_operations(descriptor, ec); + for (int j = 0; j < max_ops; ++j) + op_queue_[j].cancel_operations(descriptor, ops, ec); } } } } - read_op_queue_.perform_cancellations(); - write_op_queue_.perform_cancellations(); - except_op_queue_.perform_cancellations(); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - timer_queues_[i]->dispatch_timers(); - timer_queues_[i]->dispatch_cancellations(); - } - - // Issue any pending cancellations. - for (size_t i = 0; i < pending_cancellations_.size(); ++i) - cancel_ops_unlocked(pending_cancellations_[i]); - pending_cancellations_.clear(); - - complete_operations_and_timers(lock); - } - - // Run the select loop in the thread. - void run_thread() - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - while (!stop_thread_) - { - lock.unlock(); - run(true); - lock.lock(); - } - } - - // Entry point for the select loop thread. - static void call_run_thread(dev_poll_reactor* reactor) - { - reactor->run_thread(); + timer_queues_.get_ready_timers(ops); } // Interrupt the select loop. @@ -513,6 +352,7 @@ private: interrupter_.interrupt(); } +private: // Create the /dev/poll file descriptor. Throws an exception if the descriptor // cannot be created. static int do_dev_poll_create() @@ -529,75 +369,32 @@ private: return fd; } - // Check if all timer queues are empty. - bool all_timer_queues_are_empty() const - { - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - if (!timer_queues_[i]->empty()) - return false; - return true; - } - // Get the timeout value for the /dev/poll DP_POLL operation. The timeout // value is returned as a number of milliseconds. A return value of -1 // indicates that the poll should block indefinitely. int get_timeout() { - if (all_timer_queues_are_empty()) - return -1; - // By default we will wait no longer than 5 minutes. This will ensure that // any changes to the system clock are detected after no longer than this. - boost::posix_time::time_duration minimum_wait_duration - = boost::posix_time::minutes(5); - - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - boost::posix_time::time_duration wait_duration - = timer_queues_[i]->wait_duration(); - if (wait_duration < minimum_wait_duration) - minimum_wait_duration = wait_duration; - } - - if (minimum_wait_duration > boost::posix_time::time_duration()) - { - int milliseconds = minimum_wait_duration.total_milliseconds(); - return milliseconds > 0 ? milliseconds : 1; - } - else - { - return 0; - } + return timer_queues_.wait_duration_msec(5 * 60 * 1000); } // Cancel all operations associated with the given descriptor. The do_cancel // function of the handler objects will be invoked. This function does not // acquire the dev_poll_reactor's mutex. - void cancel_ops_unlocked(socket_type descriptor) + void cancel_ops_unlocked(socket_type descriptor, + const boost::system::error_code& ec) { - bool interrupt = read_op_queue_.cancel_operations(descriptor); - interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; - interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; - if (interrupt) + bool need_interrupt = false; + op_queue<operation> ops; + for (int i = 0; i < max_ops; ++i) + need_interrupt = op_queue_[i].cancel_operations( + descriptor, ops, ec) || need_interrupt; + io_service_.post_deferred_completions(ops); + if (need_interrupt) interrupter_.interrupt(); } - // Clean up operations and timers. We must not hold the lock since the - // destructors may make calls back into this reactor. We make a copy of the - // vector of timer queues since the original may be modified while the lock - // is not held. - void complete_operations_and_timers( - boost::asio::detail::mutex::scoped_lock& lock) - { - timer_queues_for_cleanup_ = timer_queues_; - lock.unlock(); - read_op_queue_.complete_operations(); - write_op_queue_.complete_operations(); - except_op_queue_.complete_operations(); - for (std::size_t i = 0; i < timer_queues_for_cleanup_.size(); ++i) - timer_queues_for_cleanup_[i]->complete_timers(); - } - // Add a pending event entry for the given descriptor. ::pollfd& add_pending_event_change(int descriptor) { @@ -619,6 +416,9 @@ private: } } + // The io_service implementation used to post completions. + io_service_impl& io_service_; + // Mutex to protect access to internal data. boost::asio::detail::mutex mutex_; @@ -631,36 +431,14 @@ private: // Hash map to associate a descriptor with a pending event change index. hash_map<int, std::size_t> pending_event_change_index_; - // Whether the DP_POLL operation is currently in progress - bool wait_in_progress_; - // The interrupter is used to break a blocking DP_POLL operation. select_interrupter interrupter_; - // The queue of read operations. - reactor_op_queue<socket_type> read_op_queue_; - - // The queue of write operations. - reactor_op_queue<socket_type> write_op_queue_; - - // The queue of except operations. - reactor_op_queue<socket_type> except_op_queue_; + // The queues of read, write and except operations. + reactor_op_queue<socket_type> op_queue_[max_ops]; // The timer queues. - std::vector<timer_queue_base*> timer_queues_; - - // A copy of the timer queues, used when cleaning up timers. The copy is - // stored as a class data member to avoid unnecessary memory allocation. - std::vector<timer_queue_base*> timer_queues_for_cleanup_; - - // The descriptors that are pending cancellation. - std::vector<socket_type> pending_cancellations_; - - // Does the reactor loop thread need to stop. - bool stop_thread_; - - // The thread that is running the reactor loop. - boost::asio::detail::thread* thread_; + timer_queue_set timer_queues_; // Whether the service has been shut down. bool shutdown_; diff --git a/3rdParty/Boost/src/boost/asio/detail/dev_poll_reactor_fwd.hpp b/3rdParty/Boost/src/boost/asio/detail/dev_poll_reactor_fwd.hpp index d890fa8..238cee5 100644 --- a/3rdParty/Boost/src/boost/asio/detail/dev_poll_reactor_fwd.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/dev_poll_reactor_fwd.hpp @@ -27,7 +27,6 @@ namespace boost { namespace asio { namespace detail { -template <bool Own_Thread> class dev_poll_reactor; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/epoll_reactor.hpp b/3rdParty/Boost/src/boost/asio/detail/epoll_reactor.hpp index 6c6bfe3..4684944 100644 --- a/3rdParty/Boost/src/boost/asio/detail/epoll_reactor.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/epoll_reactor.hpp @@ -23,115 +23,126 @@ #include <boost/asio/detail/push_options.hpp> #include <cstddef> -#include <vector> #include <sys/epoll.h> #include <boost/config.hpp> -#include <boost/date_time/posix_time/posix_time_types.hpp> #include <boost/throw_exception.hpp> #include <boost/system/system_error.hpp> #include <boost/asio/detail/pop_options.hpp> #include <boost/asio/error.hpp> #include <boost/asio/io_service.hpp> -#include <boost/asio/detail/bind_handler.hpp> #include <boost/asio/detail/hash_map.hpp> #include <boost/asio/detail/mutex.hpp> -#include <boost/asio/detail/task_io_service.hpp> -#include <boost/asio/detail/thread.hpp> -#include <boost/asio/detail/reactor_op_queue.hpp> +#include <boost/asio/detail/op_queue.hpp> +#include <boost/asio/detail/reactor_op.hpp> #include <boost/asio/detail/select_interrupter.hpp> #include <boost/asio/detail/service_base.hpp> -#include <boost/asio/detail/signal_blocker.hpp> #include <boost/asio/detail/socket_types.hpp> -#include <boost/asio/detail/timer_queue.hpp> +#include <boost/asio/detail/timer_op.hpp> +#include <boost/asio/detail/timer_queue_base.hpp> +#include <boost/asio/detail/timer_queue_fwd.hpp> +#include <boost/asio/detail/timer_queue_set.hpp> + +#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) +# define BOOST_ASIO_HAS_TIMERFD 1 +#endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) + +#if defined(BOOST_ASIO_HAS_TIMERFD) +# include <boost/asio/detail/push_options.hpp> +# include <sys/timerfd.h> +# include <boost/asio/detail/pop_options.hpp> +#endif // defined(BOOST_ASIO_HAS_TIMERFD) namespace boost { namespace asio { namespace detail { -template <bool Own_Thread> class epoll_reactor - : public boost::asio::detail::service_base<epoll_reactor<Own_Thread> > + : public boost::asio::detail::service_base<epoll_reactor> { public: - // Per-descriptor data. - struct per_descriptor_data + enum { read_op = 0, write_op = 1, + connect_op = 1, except_op = 2, max_ops = 3 }; + + // Per-descriptor queues. + struct descriptor_state { - bool allow_speculative_read; - bool allow_speculative_write; + descriptor_state() {} + descriptor_state(const descriptor_state&) {} + void operator=(const descriptor_state&) {} + + mutex mutex_; + op_queue<reactor_op> op_queue_[max_ops]; + bool shutdown_; }; + // Per-descriptor data. + typedef descriptor_state* per_descriptor_data; + // Constructor. epoll_reactor(boost::asio::io_service& io_service) - : boost::asio::detail::service_base<epoll_reactor<Own_Thread> >(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()), - wait_in_progress_(false), +#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_(), - read_op_queue_(), - write_op_queue_(), - except_op_queue_(), - pending_cancellations_(), - stop_thread_(false), - thread_(0), - shutdown_(false), - need_epoll_wait_(true) + shutdown_(false) { - // Start the reactor's internal thread only if needed. - if (Own_Thread) - { - boost::asio::detail::signal_blocker sb; - thread_ = new boost::asio::detail::thread( - bind_handler(&epoll_reactor::call_run_thread, this)); - } - // Add the interrupter's descriptor to epoll. epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLIN | EPOLLERR; - ev.data.fd = interrupter_.read_descriptor(); + 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); + } } // Destructor. ~epoll_reactor() { - shutdown_service(); close(epoll_fd_); + if (timer_fd_ != -1) + close(timer_fd_); } // Destroy all user-defined handler objects owned by the service. void shutdown_service() { - boost::asio::detail::mutex::scoped_lock lock(mutex_); + mutex::scoped_lock lock(mutex_); shutdown_ = true; - stop_thread_ = true; lock.unlock(); - if (thread_) + op_queue<operation> ops; + + descriptor_map::iterator iter = registered_descriptors_.begin(); + descriptor_map::iterator end = registered_descriptors_.end(); + while (iter != end) { - interrupter_.interrupt(); - thread_->join(); - delete thread_; - thread_ = 0; + for (int i = 0; i < max_ops; ++i) + ops.push(iter->second.op_queue_[i]); + iter->second.shutdown_ = true; + ++iter; } - read_op_queue_.destroy_operations(); - write_op_queue_.destroy_operations(); - except_op_queue_.destroy_operations(); - - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - timer_queues_[i]->destroy_timers(); - timer_queues_.clear(); + timer_queues_.get_all_timers(ops); } - // Initialise the task, but only if the reactor is not in its own thread. + // Initialise the task. void init_task() { - if (!Own_Thread) - { - typedef task_io_service<epoll_reactor<Own_Thread> > task_io_service_type; - use_service<task_io_service_type>(this->get_io_service()).init_task(); - } + io_service_.init_task(); } // Register a socket with the reactor. Returns 0 on success, system error @@ -139,454 +150,274 @@ public: int register_descriptor(socket_type descriptor, per_descriptor_data& descriptor_data) { - // No need to lock according to epoll documentation. + mutex::scoped_lock lock(registered_descriptors_mutex_); - descriptor_data.allow_speculative_read = true; - descriptor_data.allow_speculative_write = true; + descriptor_map::iterator new_entry = registered_descriptors_.insert( + std::make_pair(descriptor, descriptor_state())).first; + descriptor_data = &new_entry->second; epoll_event ev = { 0, { 0 } }; - ev.events = 0; - ev.data.fd = descriptor; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLOUT | EPOLLPRI | EPOLLET; + ev.data.ptr = descriptor_data; int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); if (result != 0) return errno; + + descriptor_data->shutdown_ = false; + return 0; } - // Start a new read operation. The handler object will be invoked when the - // given descriptor is ready to be read, or an error has occurred. - template <typename Handler> - void start_read_op(socket_type descriptor, + // Start a new operation. The reactor operation will be performed when the + // given descriptor is flagged as ready, or an error has occurred. + void start_op(int op_type, socket_type descriptor, per_descriptor_data& descriptor_data, - Handler handler, bool allow_speculative_read = true) + reactor_op* op, bool allow_speculative) { - if (allow_speculative_read && descriptor_data.allow_speculative_read) - { - boost::system::error_code ec; - std::size_t bytes_transferred = 0; - if (handler.perform(ec, bytes_transferred)) - { - handler.complete(ec, bytes_transferred); - return; - } - - // We only get one shot at a speculative read in this function. - allow_speculative_read = false; - } - - boost::asio::detail::mutex::scoped_lock lock(mutex_); - - if (shutdown_) + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + if (descriptor_data->shutdown_) return; - if (!allow_speculative_read) - need_epoll_wait_ = true; - else if (!read_op_queue_.has_operation(descriptor)) + if (descriptor_data->op_queue_[op_type].empty()) { - // Speculative reads are ok as there are no queued read operations. - descriptor_data.allow_speculative_read = true; - - boost::system::error_code ec; - std::size_t bytes_transferred = 0; - if (handler.perform(ec, bytes_transferred)) + if (allow_speculative + && (op_type != read_op + || descriptor_data->op_queue_[except_op].empty())) { - handler.complete(ec, bytes_transferred); - return; + if (op->perform()) + { + descriptor_lock.unlock(); + io_service_.post_immediate_completion(op); + return; + } } - } - - // Speculative reads are not ok as there will be queued read operations. - descriptor_data.allow_speculative_read = false; - - if (read_op_queue_.enqueue_operation(descriptor, handler)) - { - epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLIN | EPOLLERR | EPOLLHUP; - if (write_op_queue_.has_operation(descriptor)) - ev.events |= EPOLLOUT; - if (except_op_queue_.has_operation(descriptor)) - ev.events |= EPOLLPRI; - ev.data.fd = descriptor; - - int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); - if (result != 0 && errno == ENOENT) - result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); - if (result != 0) + else { - boost::system::error_code ec(errno, - boost::asio::error::get_system_category()); - read_op_queue_.perform_all_operations(descriptor, ec); + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP + | EPOLLOUT | EPOLLPRI | EPOLLET; + ev.data.ptr = descriptor_data; + epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); } } + + descriptor_data->op_queue_[op_type].push(op); + io_service_.work_started(); } - // Start a new write operation. The handler object will be invoked when the - // given descriptor is ready to be written, or an error has occurred. - template <typename Handler> - void start_write_op(socket_type descriptor, - per_descriptor_data& descriptor_data, - Handler handler, bool allow_speculative_write = true) + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + void cancel_ops(socket_type descriptor, per_descriptor_data& descriptor_data) { - if (allow_speculative_write && descriptor_data.allow_speculative_write) - { - boost::system::error_code ec; - std::size_t bytes_transferred = 0; - if (handler.perform(ec, bytes_transferred)) - { - handler.complete(ec, bytes_transferred); - return; - } - - // We only get one shot at a speculative write in this function. - allow_speculative_write = false; - } - - boost::asio::detail::mutex::scoped_lock lock(mutex_); + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - if (shutdown_) - return; - - if (!allow_speculative_write) - need_epoll_wait_ = true; - else if (!write_op_queue_.has_operation(descriptor)) + op_queue<operation> ops; + for (int i = 0; i < max_ops; ++i) { - // Speculative writes are ok as there are no queued write operations. - descriptor_data.allow_speculative_write = true; - - boost::system::error_code ec; - std::size_t bytes_transferred = 0; - if (handler.perform(ec, bytes_transferred)) + while (reactor_op* op = descriptor_data->op_queue_[i].front()) { - handler.complete(ec, bytes_transferred); - return; + op->ec_ = boost::asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); } } - // Speculative writes are not ok as there will be queued write operations. - descriptor_data.allow_speculative_write = false; + descriptor_lock.unlock(); - if (write_op_queue_.enqueue_operation(descriptor, handler)) - { - epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLOUT | EPOLLERR | EPOLLHUP; - if (read_op_queue_.has_operation(descriptor)) - ev.events |= EPOLLIN; - if (except_op_queue_.has_operation(descriptor)) - ev.events |= EPOLLPRI; - ev.data.fd = descriptor; - - int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); - if (result != 0 && errno == ENOENT) - result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); - if (result != 0) - { - boost::system::error_code ec(errno, - boost::asio::error::get_system_category()); - write_op_queue_.perform_all_operations(descriptor, ec); - } - } + io_service_.post_deferred_completions(ops); } - // Start a new exception operation. The handler object will be invoked when - // the given descriptor has exception information, or an error has occurred. - template <typename Handler> - void start_except_op(socket_type descriptor, - per_descriptor_data&, Handler handler) + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. + void close_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data) { - boost::asio::detail::mutex::scoped_lock lock(mutex_); + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); - if (shutdown_) - return; + // Remove the descriptor from the set of known descriptors. The descriptor + // will be automatically removed from the epoll set when it is closed. + descriptor_data->shutdown_ = true; - if (except_op_queue_.enqueue_operation(descriptor, handler)) + op_queue<operation> ops; + for (int i = 0; i < max_ops; ++i) { - epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLPRI | EPOLLERR | EPOLLHUP; - if (read_op_queue_.has_operation(descriptor)) - ev.events |= EPOLLIN; - if (write_op_queue_.has_operation(descriptor)) - ev.events |= EPOLLOUT; - ev.data.fd = descriptor; - - int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); - if (result != 0 && errno == ENOENT) - result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); - if (result != 0) + while (reactor_op* op = descriptor_data->op_queue_[i].front()) { - boost::system::error_code ec(errno, - boost::asio::error::get_system_category()); - except_op_queue_.perform_all_operations(descriptor, ec); + op->ec_ = boost::asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); } } - } - // Start a new write operation. The handler object will be invoked when the - // given descriptor is ready for writing or an error has occurred. Speculative - // writes are not allowed. - template <typename Handler> - void start_connect_op(socket_type descriptor, - per_descriptor_data& descriptor_data, Handler handler) - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); + descriptor_lock.unlock(); - if (shutdown_) - return; + registered_descriptors_.erase(descriptor); - // Speculative writes are not ok as there will be queued write operations. - descriptor_data.allow_speculative_write = false; - - if (write_op_queue_.enqueue_operation(descriptor, handler)) - { - epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLOUT | EPOLLERR | EPOLLHUP; - if (read_op_queue_.has_operation(descriptor)) - ev.events |= EPOLLIN; - if (except_op_queue_.has_operation(descriptor)) - ev.events |= EPOLLPRI; - ev.data.fd = descriptor; - - int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); - if (result != 0 && errno == ENOENT) - result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); - if (result != 0) - { - boost::system::error_code ec(errno, - boost::asio::error::get_system_category()); - write_op_queue_.perform_all_operations(descriptor, ec); - } - } - } + descriptors_lock.unlock(); - // Cancel all operations associated with the given descriptor. The - // handlers associated with the descriptor will be invoked with the - // operation_aborted error. - void cancel_ops(socket_type descriptor, per_descriptor_data&) - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - cancel_ops_unlocked(descriptor); - } - - // Cancel any operations that are running against the descriptor and remove - // its registration from the reactor. - void close_descriptor(socket_type descriptor, per_descriptor_data&) - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - - // Remove the descriptor from epoll. - epoll_event ev = { 0, { 0 } }; - epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev); - - // Cancel any outstanding operations associated with the descriptor. - cancel_ops_unlocked(descriptor); + io_service_.post_deferred_completions(ops); } // Add a new timer queue to the reactor. template <typename Time_Traits> void add_timer_queue(timer_queue<Time_Traits>& timer_queue) { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - timer_queues_.push_back(&timer_queue); + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&timer_queue); } // Remove a timer queue from the reactor. template <typename Time_Traits> void remove_timer_queue(timer_queue<Time_Traits>& timer_queue) { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - if (timer_queues_[i] == &timer_queue) - { - timer_queues_.erase(timer_queues_.begin() + i); - return; - } - } + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&timer_queue); } - // Schedule a timer in the given timer queue to expire at the specified - // absolute time. The handler object will be invoked when the timer expires. - template <typename Time_Traits, typename Handler> + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template <typename Time_Traits> void schedule_timer(timer_queue<Time_Traits>& timer_queue, - const typename Time_Traits::time_type& time, Handler handler, void* token) + const typename Time_Traits::time_type& time, timer_op* op, void* token) { - boost::asio::detail::mutex::scoped_lock lock(mutex_); + mutex::scoped_lock lock(mutex_); if (!shutdown_) - if (timer_queue.enqueue_timer(time, handler, token)) + { + bool earliest = timer_queue.enqueue_timer(time, op, token); + io_service_.work_started(); + if (earliest) + { +#if defined(BOOST_ASIO_HAS_TIMERFD) + if (timer_fd_ != -1) + { + itimerspec new_timeout; + itimerspec old_timeout; + int flags = get_timeout(new_timeout); + timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); + return; + } +#endif // defined(BOOST_ASIO_HAS_TIMERFD) interrupter_.interrupt(); + } + } } - // Cancel the timer associated with the given token. Returns the number of - // handlers that have been posted or dispatched. + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. template <typename Time_Traits> std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token) { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - std::size_t n = timer_queue.cancel_timer(token); - if (n > 0) - interrupter_.interrupt(); + mutex::scoped_lock lock(mutex_); + op_queue<operation> ops; + std::size_t n = timer_queue.cancel_timer(token, ops); + lock.unlock(); + io_service_.post_deferred_completions(ops); return n; } -private: - friend class task_io_service<epoll_reactor<Own_Thread> >; - // Run epoll once until interrupted or events are ready to be dispatched. - void run(bool block) + void run(bool block, op_queue<operation>& ops) { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - - // Dispatch any operation cancellations that were made while the select - // loop was not running. - read_op_queue_.perform_cancellations(); - write_op_queue_.perform_cancellations(); - except_op_queue_.perform_cancellations(); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - timer_queues_[i]->dispatch_cancellations(); - - // Check if the thread is supposed to stop. - if (stop_thread_) - { - complete_operations_and_timers(lock); - return; - } - - // We can return immediately if there's no work to do and the reactor is - // not supposed to block. - if (!block && read_op_queue_.empty() && write_op_queue_.empty() - && except_op_queue_.empty() && all_timer_queues_are_empty()) + // Calculate a timeout only if timerfd is not used. + int timeout; + if (timer_fd_ != -1) + timeout = block ? -1 : 0; + else { - complete_operations_and_timers(lock); - return; + mutex::scoped_lock lock(mutex_); + timeout = block ? get_timeout() : 0; } - int timeout = block ? get_timeout() : 0; - wait_in_progress_ = true; - lock.unlock(); - // Block on the epoll descriptor. epoll_event events[128]; - int num_events = (block || need_epoll_wait_) - ? epoll_wait(epoll_fd_, events, 128, timeout) - : 0; + int num_events = epoll_wait(epoll_fd_, events, 128, timeout); - lock.lock(); - wait_in_progress_ = false; +#if defined(BOOST_ASIO_HAS_TIMERFD) + bool check_timers = (timer_fd_ == -1); +#else // defined(BOOST_ASIO_HAS_TIMERFD) + bool check_timers = true; +#endif // defined(BOOST_ASIO_HAS_TIMERFD) // Dispatch the waiting events. for (int i = 0; i < num_events; ++i) { - int descriptor = events[i].data.fd; - if (descriptor == interrupter_.read_descriptor()) + void* ptr = events[i].data.ptr; + if (ptr == &interrupter_) { - interrupter_.reset(); + // No need to reset the interrupter since we're leaving the descriptor + // in a ready-to-read state and relying on edge-triggered notifications + // to make it so that we only get woken up when the descriptor's epoll + // registration is updated. + +#if defined(BOOST_ASIO_HAS_TIMERFD) + if (timer_fd_ == -1) + check_timers = true; +#else // defined(BOOST_ASIO_HAS_TIMERFD) + check_timers = true; +#endif // defined(BOOST_ASIO_HAS_TIMERFD) } +#if defined(BOOST_ASIO_HAS_TIMERFD) + else if (ptr == &timer_fd_) + { + check_timers = true; + } +#endif // defined(BOOST_ASIO_HAS_TIMERFD) else { - bool more_reads = false; - bool more_writes = false; - bool more_except = false; - boost::system::error_code ec; + 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. - if (events[i].events & (EPOLLPRI | EPOLLERR | EPOLLHUP)) - more_except = except_op_queue_.perform_operation(descriptor, ec); - else - more_except = except_op_queue_.has_operation(descriptor); - - if (events[i].events & (EPOLLIN | EPOLLERR | EPOLLHUP)) - more_reads = read_op_queue_.perform_operation(descriptor, ec); - else - more_reads = read_op_queue_.has_operation(descriptor); - - if (events[i].events & (EPOLLOUT | EPOLLERR | EPOLLHUP)) - more_writes = write_op_queue_.perform_operation(descriptor, ec); - else - more_writes = write_op_queue_.has_operation(descriptor); - - if ((events[i].events & (EPOLLERR | EPOLLHUP)) != 0 - && (events[i].events & ~(EPOLLERR | EPOLLHUP)) == 0 - && !more_except && !more_reads && !more_writes) + static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI }; + for (int j = max_ops - 1; j >= 0; --j) { - // If we have an event and no operations associated with the - // descriptor then we need to delete the descriptor from epoll. The - // epoll_wait system call can produce EPOLLHUP or EPOLLERR events - // when there is no operation pending, so if we do not remove the - // descriptor we can end up in a tight loop of repeated - // calls to epoll_wait. - epoll_event ev = { 0, { 0 } }; - epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev); - } - else - { - epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLERR | EPOLLHUP; - if (more_reads) - ev.events |= EPOLLIN; - if (more_writes) - ev.events |= EPOLLOUT; - if (more_except) - ev.events |= EPOLLPRI; - ev.data.fd = descriptor; - int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); - if (result != 0 && errno == ENOENT) - result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); - if (result != 0) + if (events[i].events & (flag[j] | EPOLLERR | EPOLLHUP)) { - ec = boost::system::error_code(errno, - boost::asio::error::get_system_category()); - read_op_queue_.perform_all_operations(descriptor, ec); - write_op_queue_.perform_all_operations(descriptor, ec); - except_op_queue_.perform_all_operations(descriptor, ec); + while (reactor_op* op = descriptor_data->op_queue_[j].front()) + { + if (op->perform()) + { + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + else + break; + } } } } } - read_op_queue_.perform_cancellations(); - write_op_queue_.perform_cancellations(); - except_op_queue_.perform_cancellations(); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - timer_queues_[i]->dispatch_timers(); - timer_queues_[i]->dispatch_cancellations(); - } - - // Issue any pending cancellations. - for (size_t i = 0; i < pending_cancellations_.size(); ++i) - cancel_ops_unlocked(pending_cancellations_[i]); - pending_cancellations_.clear(); - - // Determine whether epoll_wait should be called when the reactor next runs. - need_epoll_wait_ = !read_op_queue_.empty() - || !write_op_queue_.empty() || !except_op_queue_.empty(); - - complete_operations_and_timers(lock); - } - // Run the select loop in the thread. - void run_thread() - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - while (!stop_thread_) + if (check_timers) { - lock.unlock(); - run(true); - lock.lock(); - } - } + mutex::scoped_lock common_lock(mutex_); + timer_queues_.get_ready_timers(ops); - // Entry point for the select loop thread. - static void call_run_thread(epoll_reactor* reactor) - { - reactor->run_thread(); +#if defined(BOOST_ASIO_HAS_TIMERFD) + if (timer_fd_ != -1) + { + itimerspec new_timeout; + itimerspec old_timeout; + int flags = get_timeout(new_timeout); + timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); + } +#endif // defined(BOOST_ASIO_HAS_TIMERFD) + } } // Interrupt the select loop. void interrupt() { - interrupter_.interrupt(); + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLET; + ev.data.ptr = &interrupter_; + epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, interrupter_.read_descriptor(), &ev); } +private: // The hint to pass to epoll_create to size its data structures. enum { epoll_size = 20000 }; @@ -606,117 +437,65 @@ private: return fd; } - // Check if all timer queues are empty. - bool all_timer_queues_are_empty() const - { - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - if (!timer_queues_[i]->empty()) - return false; - return true; - } - // Get the timeout value for the epoll_wait call. The timeout value is // returned as a number of milliseconds. A return value of -1 indicates // that epoll_wait should block indefinitely. int get_timeout() { - if (all_timer_queues_are_empty()) - return -1; - // By default we will wait no longer than 5 minutes. This will ensure that // any changes to the system clock are detected after no longer than this. - boost::posix_time::time_duration minimum_wait_duration - = boost::posix_time::minutes(5); - - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - boost::posix_time::time_duration wait_duration - = timer_queues_[i]->wait_duration(); - if (wait_duration < minimum_wait_duration) - minimum_wait_duration = wait_duration; - } - - if (minimum_wait_duration > boost::posix_time::time_duration()) - { - int milliseconds = minimum_wait_duration.total_milliseconds(); - return milliseconds > 0 ? milliseconds : 1; - } - else - { - return 0; - } + return timer_queues_.wait_duration_msec(5 * 60 * 1000); } - // Cancel all operations associated with the given descriptor. The do_cancel - // function of the handler objects will be invoked. This function does not - // acquire the epoll_reactor's mutex. - void cancel_ops_unlocked(socket_type descriptor) +#if defined(BOOST_ASIO_HAS_TIMERFD) + // Get the timeout value for the timer descriptor. The return value is the + // flag argument to be used when calling timerfd_settime. + int get_timeout(itimerspec& ts) { - bool interrupt = read_op_queue_.cancel_operations(descriptor); - interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; - interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; - if (interrupt) - interrupter_.interrupt(); - } + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; - // Clean up operations and timers. We must not hold the lock since the - // destructors may make calls back into this reactor. We make a copy of the - // vector of timer queues since the original may be modified while the lock - // is not held. - void complete_operations_and_timers( - boost::asio::detail::mutex::scoped_lock& lock) - { - timer_queues_for_cleanup_ = timer_queues_; - lock.unlock(); - read_op_queue_.complete_operations(); - write_op_queue_.complete_operations(); - except_op_queue_.complete_operations(); - for (std::size_t i = 0; i < timer_queues_for_cleanup_.size(); ++i) - timer_queues_for_cleanup_[i]->complete_timers(); + long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); + ts.it_value.tv_sec = usec / 1000000; + ts.it_value.tv_nsec = usec ? (usec % 1000000) * 1000 : 1; + + return usec ? 0 : TFD_TIMER_ABSTIME; } +#endif // defined(BOOST_ASIO_HAS_TIMERFD) + + // The io_service implementation used to post completions. + io_service_impl& io_service_; // Mutex to protect access to internal data. - boost::asio::detail::mutex mutex_; + mutex mutex_; // The epoll file descriptor. int epoll_fd_; - // Whether the epoll_wait call is currently in progress - bool wait_in_progress_; + // The timer file descriptor. + int timer_fd_; // The interrupter is used to break a blocking epoll_wait call. select_interrupter interrupter_; - // The queue of read operations. - reactor_op_queue<socket_type> read_op_queue_; - - // The queue of write operations. - reactor_op_queue<socket_type> write_op_queue_; - - // The queue of except operations. - reactor_op_queue<socket_type> except_op_queue_; - // The timer queues. - std::vector<timer_queue_base*> timer_queues_; - - // A copy of the timer queues, used when cleaning up timers. The copy is - // stored as a class data member to avoid unnecessary memory allocation. - std::vector<timer_queue_base*> timer_queues_for_cleanup_; - - // The descriptors that are pending cancellation. - std::vector<socket_type> pending_cancellations_; - - // Does the reactor loop thread need to stop. - bool stop_thread_; - - // The thread that is running the reactor loop. - boost::asio::detail::thread* thread_; + timer_queue_set timer_queues_; // Whether the service has been shut down. bool shutdown_; - // Whether we need to call epoll_wait the next time the reactor is run. - bool need_epoll_wait_; + // Mutex to protect access to the registered descriptors. + mutex registered_descriptors_mutex_; + + // Keep track of all registered descriptors. This code relies on the fact that + // the hash_map implementation pools deleted nodes, meaning that we can assume + // our descriptor_state pointer remains valid even after the entry is removed. + // Technically this is not true for C++98, as that standard says that spliced + // elements in a list are invalidated. However, C++0x fixes this shortcoming + // so we'll just assume that C++98 std::list implementations will do the right + // thing anyway. + typedef detail::hash_map<socket_type, descriptor_state> descriptor_map; + descriptor_map registered_descriptors_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/epoll_reactor_fwd.hpp b/3rdParty/Boost/src/boost/asio/detail/epoll_reactor_fwd.hpp index 9659dd4..214cee5 100644 --- a/3rdParty/Boost/src/boost/asio/detail/epoll_reactor_fwd.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/epoll_reactor_fwd.hpp @@ -33,7 +33,6 @@ namespace boost { namespace asio { namespace detail { -template <bool Own_Thread> class epoll_reactor; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/event.hpp b/3rdParty/Boost/src/boost/asio/detail/event.hpp index 89f9896..e8d185f 100644 --- a/3rdParty/Boost/src/boost/asio/detail/event.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/event.hpp @@ -21,7 +21,7 @@ #include <boost/config.hpp> #include <boost/asio/detail/pop_options.hpp> -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) # include <boost/asio/detail/null_event.hpp> #elif defined(BOOST_WINDOWS) # include <boost/asio/detail/win_event.hpp> @@ -35,7 +35,7 @@ namespace boost { namespace asio { namespace detail { -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) typedef null_event event; #elif defined(BOOST_WINDOWS) typedef win_event event; diff --git a/3rdParty/Boost/src/boost/asio/detail/fenced_block.hpp b/3rdParty/Boost/src/boost/asio/detail/fenced_block.hpp new file mode 100644 index 0000000..c80161f --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/fenced_block.hpp @@ -0,0 +1,72 @@ +// +// fenced_block.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_FENCED_BLOCK_HPP +#define BOOST_ASIO_DETAIL_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/push_options.hpp> +#include <boost/config.hpp> +#include <boost/asio/detail/pop_options.hpp> + +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) +# include <boost/asio/detail/null_fenced_block.hpp> +#elif defined(__MACH__) && defined(__APPLE__) +# include <boost/asio/detail/macos_fenced_block.hpp> +#elif defined(__sun) +# include <boost/asio/detail/solaris_fenced_block.hpp> +#elif defined(__GNUC__) \ + && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ + && !defined(__INTEL_COMPILER) && !defined(__ICL) \ + && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) +# include <boost/asio/detail/gcc_fenced_block.hpp> +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +# include <boost/asio/detail/gcc_x86_fenced_block.hpp> +#elif defined(BOOST_WINDOWS) && !defined(UNDER_CE) +# include <boost/asio/detail/win_fenced_block.hpp> +#else +# include <boost/asio/detail/null_fenced_block.hpp> +#endif + +namespace boost { +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) +typedef null_fenced_block fenced_block; +#elif defined(__MACH__) && defined(__APPLE__) +typedef macos_fenced_block fenced_block; +#elif defined(__sun) +typedef solaris_fenced_block fenced_block; +#elif defined(__GNUC__) \ + && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ + && !defined(__INTEL_COMPILER) && !defined(__ICL) \ + && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) +typedef gcc_fenced_block fenced_block; +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +typedef gcc_x86_fenced_block fenced_block; +#elif defined(BOOST_WINDOWS) && !defined(UNDER_CE) +typedef win_fenced_block fenced_block; +#else +typedef null_fenced_block fenced_block; +#endif + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_FENCED_BLOCK_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/gcc_fenced_block.hpp b/3rdParty/Boost/src/boost/asio/detail/gcc_fenced_block.hpp new file mode 100644 index 0000000..0c086a6 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/gcc_fenced_block.hpp @@ -0,0 +1,65 @@ +// +// gcc_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_GCC_FENCED_BLOCK_HPP +#define BOOST_ASIO_DETAIL_GCC_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/push_options.hpp> +#include <boost/config.hpp> +#include <boost/asio/detail/pop_options.hpp> + +#if defined(__GNUC__) \ + && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ + && !defined(__INTEL_COMPILER) && !defined(__ICL) \ + && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) + +namespace boost { +namespace asio { +namespace detail { + +class gcc_fenced_block + : private noncopyable +{ +public: + // Constructor. + gcc_fenced_block() + : value_(0) + { + __sync_lock_test_and_set(&value_, 1); + } + + // Destructor. + ~gcc_fenced_block() + { + __sync_lock_release(&value_); + } + +private: + int value_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(__GNUC__) + // && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) + // && !defined(__INTEL_COMPILER) && !defined(__ICL) + // && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_GCC_FENCED_BLOCK_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/gcc_x86_fenced_block.hpp b/3rdParty/Boost/src/boost/asio/detail/gcc_x86_fenced_block.hpp new file mode 100644 index 0000000..fc23d66 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/gcc_x86_fenced_block.hpp @@ -0,0 +1,63 @@ +// +// gcc_x86_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP +#define BOOST_ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/push_options.hpp> +#include <boost/config.hpp> +#include <boost/asio/detail/pop_options.hpp> + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +namespace boost { +namespace asio { +namespace detail { + +class gcc_x86_fenced_block + : private noncopyable +{ +public: + // Constructor. + gcc_x86_fenced_block() + { + barrier(); + } + + // Destructor. + ~gcc_x86_fenced_block() + { + barrier(); + } + +private: + static int barrier() + { + int r = 0; + __asm__ __volatile__ ("xchgl %%eax, %0" : "=m" (r) : : "memory", "cc"); + return r; + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/handler_base_from_member.hpp b/3rdParty/Boost/src/boost/asio/detail/handler_base_from_member.hpp deleted file mode 100644 index 4e471e3..0000000 --- a/3rdParty/Boost/src/boost/asio/detail/handler_base_from_member.hpp +++ /dev/null @@ -1,78 +0,0 @@ -// -// handler_base_from_member.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2010 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) -// - -#ifndef BOOST_ASIO_DETAIL_HANDLER_BASE_FROM_MEMBER_HPP -#define BOOST_ASIO_DETAIL_HANDLER_BASE_FROM_MEMBER_HPP - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -#include <boost/asio/detail/push_options.hpp> - -#include <boost/asio/detail/handler_alloc_helpers.hpp> -#include <boost/asio/detail/handler_invoke_helpers.hpp> - -namespace boost { -namespace asio { -namespace detail { - -// Base class for classes that need a handler data member. Forwards the custom -// allocation and invocation hooks to the contained handler. -template <typename Handler> -class handler_base_from_member -{ -public: - handler_base_from_member(Handler handler) - : handler_(handler) - { - } - -//protected: - Handler handler_; - -protected: - // Protected destructor to prevent deletion through this type. - ~handler_base_from_member() - { - } -}; - -template <typename Handler> -inline void* asio_handler_allocate(std::size_t size, - handler_base_from_member<Handler>* this_handler) -{ - return boost_asio_handler_alloc_helpers::allocate( - size, this_handler->handler_); -} - -template <typename Handler> -inline void asio_handler_deallocate(void* pointer, std::size_t size, - handler_base_from_member<Handler>* this_handler) -{ - boost_asio_handler_alloc_helpers::deallocate( - pointer, size, this_handler->handler_); -} - -template <typename Function, typename Handler> -inline void asio_handler_invoke(const Function& function, - handler_base_from_member<Handler>* this_handler) -{ - boost_asio_handler_invoke_helpers::invoke( - function, this_handler->handler_); -} - -} // namespace detail -} // namespace asio -} // namespace boost - -#include <boost/asio/detail/pop_options.hpp> - -#endif // BOOST_ASIO_DETAIL_HANDLER_BASE_FROM_MEMBER_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/handler_queue.hpp b/3rdParty/Boost/src/boost/asio/detail/handler_queue.hpp deleted file mode 100644 index c1fb988..0000000 --- a/3rdParty/Boost/src/boost/asio/detail/handler_queue.hpp +++ /dev/null @@ -1,231 +0,0 @@ -// -// handler_queue.hpp -// ~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2010 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) -// - -#ifndef BOOST_ASIO_DETAIL_HANDLER_QUEUE_HPP -#define BOOST_ASIO_DETAIL_HANDLER_QUEUE_HPP - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -#include <boost/asio/detail/push_options.hpp> - -#include <boost/asio/detail/handler_alloc_helpers.hpp> -#include <boost/asio/detail/handler_invoke_helpers.hpp> -#include <boost/asio/detail/noncopyable.hpp> - -namespace boost { -namespace asio { -namespace detail { - -class handler_queue - : private noncopyable -{ -public: - // Base class for handlers in the queue. - class handler - : private noncopyable - { - public: - void invoke() - { - invoke_func_(this); - } - - void destroy() - { - destroy_func_(this); - } - - protected: - typedef void (*invoke_func_type)(handler*); - typedef void (*destroy_func_type)(handler*); - - handler(invoke_func_type invoke_func, - destroy_func_type destroy_func) - : next_(0), - invoke_func_(invoke_func), - destroy_func_(destroy_func) - { - } - - ~handler() - { - } - - private: - friend class handler_queue; - handler* next_; - invoke_func_type invoke_func_; - destroy_func_type destroy_func_; - }; - - // Smart point to manager handler lifetimes. - class scoped_ptr - : private noncopyable - { - public: - explicit scoped_ptr(handler* h) - : handler_(h) - { - } - - ~scoped_ptr() - { - if (handler_) - handler_->destroy(); - } - - handler* get() const - { - return handler_; - } - - handler* release() - { - handler* tmp = handler_; - handler_ = 0; - return tmp; - } - - private: - handler* handler_; - }; - - // Constructor. - handler_queue() - : front_(0), - back_(0) - { - } - - // Wrap a handler to be pushed into the queue. - template <typename Handler> - static handler* wrap(Handler h) - { - // Allocate and construct an object to wrap the handler. - typedef handler_wrapper<Handler> value_type; - typedef handler_alloc_traits<Handler, value_type> alloc_traits; - raw_handler_ptr<alloc_traits> raw_ptr(h); - handler_ptr<alloc_traits> ptr(raw_ptr, h); - return ptr.release(); - } - - // Get the handler at the front of the queue. - handler* front() - { - return front_; - } - - // Pop a handler from the front of the queue. - void pop() - { - if (front_) - { - handler* tmp = front_; - front_ = front_->next_; - if (front_ == 0) - back_ = 0; - tmp->next_= 0; - } - } - - // Push a handler on to the back of the queue. - void push(handler* h) - { - h->next_ = 0; - if (back_) - { - back_->next_ = h; - back_ = h; - } - else - { - front_ = back_ = h; - } - } - - // Whether the queue is empty. - bool empty() const - { - return front_ == 0; - } - -private: - // Template wrapper for handlers. - template <typename Handler> - class handler_wrapper - : public handler - { - public: - handler_wrapper(Handler h) - : handler( - &handler_wrapper<Handler>::do_call, - &handler_wrapper<Handler>::do_destroy), - handler_(h) - { - } - - static void do_call(handler* base) - { - // Take ownership of the handler object. - typedef handler_wrapper<Handler> this_type; - this_type* h(static_cast<this_type*>(base)); - typedef handler_alloc_traits<Handler, this_type> alloc_traits; - handler_ptr<alloc_traits> ptr(h->handler_, h); - - // Make a copy of the handler so that the memory can be deallocated before - // the upcall is made. - Handler handler(h->handler_); - - // Free the memory associated with the handler. - ptr.reset(); - - // Make the upcall. - boost_asio_handler_invoke_helpers::invoke(handler, handler); - } - - static void do_destroy(handler* base) - { - // Take ownership of the handler object. - typedef handler_wrapper<Handler> this_type; - this_type* h(static_cast<this_type*>(base)); - typedef handler_alloc_traits<Handler, this_type> alloc_traits; - handler_ptr<alloc_traits> ptr(h->handler_, h); - - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(h->handler_); - (void)handler; - - // Free the memory associated with the handler. - ptr.reset(); - } - - private: - Handler handler_; - }; - - // The front of the queue. - handler* front_; - - // The back of the queue. - handler* back_; -}; - -} // namespace detail -} // namespace asio -} // namespace boost - -#include <boost/asio/detail/pop_options.hpp> - -#endif // BOOST_ASIO_DETAIL_HANDLER_QUEUE_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/hash_map.hpp b/3rdParty/Boost/src/boost/asio/detail/hash_map.hpp index c620da7..36c9a99 100644 --- a/3rdParty/Boost/src/boost/asio/detail/hash_map.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/hash_map.hpp @@ -21,7 +21,6 @@ #include <cassert> #include <list> #include <utility> -#include <vector> #include <boost/functional/hash.hpp> #include <boost/asio/detail/pop_options.hpp> @@ -62,9 +61,16 @@ public: // Constructor. hash_map() - : size_(0) + : size_(0), + buckets_(0), + num_buckets_(0) { - rehash(hash_size(0)); + } + + // Destructor. + ~hash_map() + { + delete[] buckets_; } // Get an iterator for the beginning of the map. @@ -100,17 +106,20 @@ public: // Find an entry in the map. iterator find(const K& k) { - size_t bucket = calculate_hash_value(k) % buckets_.size(); - iterator it = buckets_[bucket].first; - if (it == values_.end()) - return values_.end(); - iterator end = buckets_[bucket].last; - ++end; - while (it != end) + if (num_buckets_) { - if (it->first == k) - return it; - ++it; + size_t bucket = calculate_hash_value(k) % num_buckets_; + iterator it = buckets_[bucket].first; + if (it == values_.end()) + return values_.end(); + iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == k) + return it; + ++it; + } } return values_.end(); } @@ -118,17 +127,20 @@ public: // Find an entry in the map. const_iterator find(const K& k) const { - size_t bucket = calculate_hash_value(k) % buckets_.size(); - const_iterator it = buckets_[bucket].first; - if (it == values_.end()) - return it; - const_iterator end = buckets_[bucket].last; - ++end; - while (it != end) + if (num_buckets_) { - if (it->first == k) + size_t bucket = calculate_hash_value(k) % num_buckets_; + const_iterator it = buckets_[bucket].first; + if (it == values_.end()) return it; - ++it; + const_iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == k) + return it; + ++it; + } } return values_.end(); } @@ -136,9 +148,9 @@ public: // Insert a new entry into the map. std::pair<iterator, bool> insert(const value_type& v) { - if (size_ + 1 >= buckets_.size()) + if (size_ + 1 >= num_buckets_) rehash(hash_size(size_ + 1)); - size_t bucket = calculate_hash_value(v.first) % buckets_.size(); + size_t bucket = calculate_hash_value(v.first) % num_buckets_; iterator it = buckets_[bucket].first; if (it == values_.end()) { @@ -165,7 +177,7 @@ public: { assert(it != values_.end()); - size_t bucket = calculate_hash_value(it->first) % buckets_.size(); + size_t bucket = calculate_hash_value(it->first) % num_buckets_; bool is_first = (it == buckets_[bucket].first); bool is_last = (it == buckets_[bucket].last); if (is_first && is_last) @@ -179,6 +191,14 @@ public: --size_; } + // Erase a key from the map. + void erase(const K& k) + { + iterator it = find(k); + if (it != values_.end()) + erase(it); + } + // Remove all entries from the map. void clear() { @@ -187,8 +207,9 @@ public: size_ = 0; // Initialise all buckets to empty. - for (size_t i = 0; i < buckets_.size(); ++i) - buckets_[i].first = buckets_[i].last = values_.end(); + iterator end = values_.end(); + for (size_t i = 0; i < num_buckets_; ++i) + buckets_[i].first = buckets_[i].last = end; } private: @@ -215,21 +236,24 @@ private: // Re-initialise the hash from the values already contained in the list. void rehash(std::size_t num_buckets) { - if (num_buckets == buckets_.size()) + if (num_buckets == num_buckets_) return; + num_buckets_ = num_buckets; iterator end = values_.end(); // Update number of buckets and initialise all buckets to empty. - buckets_.resize(num_buckets); - for (std::size_t i = 0; i < buckets_.size(); ++i) + bucket_type* tmp = new bucket_type[num_buckets_]; + delete[] buckets_; + buckets_ = tmp; + for (std::size_t i = 0; i < num_buckets_; ++i) buckets_[i].first = buckets_[i].last = end; // Put all values back into the hash. iterator iter = values_.begin(); while (iter != end) { - std::size_t bucket = calculate_hash_value(iter->first) % buckets_.size(); + std::size_t bucket = calculate_hash_value(iter->first) % num_buckets_; if (buckets_[bucket].last == end) { buckets_[bucket].first = buckets_[bucket].last = iter++; @@ -282,14 +306,15 @@ private: // The type for a bucket in the hash table. struct bucket_type { - bucket_type() {} - bucket_type(const bucket_type&) { /* noop */ } iterator first; iterator last; }; // The buckets in the hash. - std::vector<bucket_type> buckets_; + bucket_type* buckets_; + + // The number of buckets in the hash. + std::size_t num_buckets_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/indirect_handler_queue.hpp b/3rdParty/Boost/src/boost/asio/detail/indirect_handler_queue.hpp deleted file mode 100644 index 72b1030..0000000 --- a/3rdParty/Boost/src/boost/asio/detail/indirect_handler_queue.hpp +++ /dev/null @@ -1,293 +0,0 @@ -// -// indirect_handler_queue.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2010 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) -// - -#ifndef BOOST_ASIO_DETAIL_INDIRECT_HANDLER_QUEUE_HPP -#define BOOST_ASIO_DETAIL_INDIRECT_HANDLER_QUEUE_HPP - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -#include <boost/asio/detail/push_options.hpp> - -#include <boost/asio/detail/handler_alloc_helpers.hpp> -#include <boost/asio/detail/handler_invoke_helpers.hpp> -#include <boost/asio/detail/noncopyable.hpp> - -#if defined(_MSC_VER) && (_MSC_VER >= 1310) -extern "C" void _ReadWriteBarrier(); -# pragma intrinsic(_ReadWriteBarrier) -#endif // defined(_MSC_VER) && (_MSC_VER >= 1310) - -namespace boost { -namespace asio { -namespace detail { - -class indirect_handler_queue - : private noncopyable -{ -public: - class handler; - - // Element for a node in the queue. - class node - { - public: - node() - : version_(0), - handler_(0), - next_(0) - { - } - - private: - friend class indirect_handler_queue; - unsigned long version_; - handler* handler_; - node* next_; - }; - - // Base class for handlers in the queue. - class handler - : private noncopyable - { - public: - void invoke() - { - invoke_func_(this); - } - - void destroy() - { - destroy_func_(this); - } - - protected: - typedef void (*invoke_func_type)(handler*); - typedef void (*destroy_func_type)(handler*); - - handler(invoke_func_type invoke_func, - destroy_func_type destroy_func) - : node_(new node), - invoke_func_(invoke_func), - destroy_func_(destroy_func) - { - } - - ~handler() - { - if (node_) - delete node_; - } - - private: - friend class indirect_handler_queue; - node* node_; - invoke_func_type invoke_func_; - destroy_func_type destroy_func_; - }; - - // Smart point to manager handler lifetimes. - class scoped_ptr - : private noncopyable - { - public: - explicit scoped_ptr(handler* h) - : handler_(h) - { - } - - ~scoped_ptr() - { - if (handler_) - handler_->destroy(); - } - - handler* get() const - { - return handler_; - } - - handler* release() - { - handler* tmp = handler_; - handler_ = 0; - return tmp; - } - - private: - handler* handler_; - }; - - // Constructor. - indirect_handler_queue() - : front_(new node), - back_(front_), - next_version_(1) - { - } - - // Destructor. - ~indirect_handler_queue() - { - while (front_) - { - node* tmp = front_; - front_ = front_->next_; - delete tmp; - } - } - - // Wrap a handler to be pushed into the queue. - template <typename Handler> - static handler* wrap(Handler h) - { - // Allocate and construct an object to wrap the handler. - typedef handler_wrapper<Handler> value_type; - typedef handler_alloc_traits<Handler, value_type> alloc_traits; - raw_handler_ptr<alloc_traits> raw_ptr(h); - handler_ptr<alloc_traits> ptr(raw_ptr, h); - return ptr.release(); - } - - // Determine whether the queue has something ready to pop. - bool poppable() - { - return front_->next_ != 0; - } - - // The version number at the front of the queue. - unsigned long front_version() - { - return front_->version_; - } - - // The version number at the back of the queue. - unsigned long back_version() - { - return back_->version_; - } - - // Pop a handler from the front of the queue. - handler* pop() - { - node* n = front_; - node* new_front = n->next_; - if (new_front) - { - handler* h = new_front->handler_; - h->node_ = n; - new_front->handler_ = 0; - front_ = new_front; - return h; - } - return 0; - } - - // Push a handler on to the back of the queue. - void push(handler* h) - { - node* n = h->node_; - h->node_ = 0; - n->version_ = next_version_; - next_version_ += 2; - n->handler_ = h; - n->next_ = 0; - memory_barrier(); - back_->next_ = n; - back_ = n; - } - -private: - // Template wrapper for handlers. - template <typename Handler> - class handler_wrapper - : public handler - { - public: - handler_wrapper(Handler h) - : handler( - &handler_wrapper<Handler>::do_call, - &handler_wrapper<Handler>::do_destroy), - handler_(h) - { - } - - static void do_call(handler* base) - { - // Take ownership of the handler object. - typedef handler_wrapper<Handler> this_type; - this_type* h(static_cast<this_type*>(base)); - typedef handler_alloc_traits<Handler, this_type> alloc_traits; - handler_ptr<alloc_traits> ptr(h->handler_, h); - - // Make a copy of the handler so that the memory can be deallocated before - // the upcall is made. - Handler handler(h->handler_); - - // Free the memory associated with the handler. - ptr.reset(); - - // Make the upcall. - boost_asio_handler_invoke_helpers::invoke(handler, handler); - } - - static void do_destroy(handler* base) - { - // Take ownership of the handler object. - typedef handler_wrapper<Handler> this_type; - this_type* h(static_cast<this_type*>(base)); - typedef handler_alloc_traits<Handler, this_type> alloc_traits; - handler_ptr<alloc_traits> ptr(h->handler_, h); - - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(h->handler_); - (void)handler; - - // Free the memory associated with the handler. - ptr.reset(); - } - - private: - Handler handler_; - }; - - // Helper function to create a memory barrier. - static void memory_barrier() - { -#if defined(_GLIBCXX_WRITE_MEM_BARRIER) - _GLIBCXX_WRITE_MEM_BARRIER; -#elif defined(_MSC_VER) && (_MSC_VER >= 1310) - _ReadWriteBarrier(); -#else -# error memory barrier required -#endif - } - - // The front of the queue. - node* front_; - - // The back of the queue. - node* back_; - - // The next version counter to be assigned to a node. - unsigned long next_version_; -}; - -} // namespace detail -} // namespace asio -} // namespace boost - -#include <boost/asio/detail/pop_options.hpp> - -#endif // BOOST_ASIO_DETAIL_INDIRECT_HANDLER_QUEUE_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/kqueue_reactor.hpp b/3rdParty/Boost/src/boost/asio/detail/kqueue_reactor.hpp index ea8e285..c1a6705 100644 --- a/3rdParty/Boost/src/boost/asio/detail/kqueue_reactor.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/kqueue_reactor.hpp @@ -24,28 +24,27 @@ #include <boost/asio/detail/push_options.hpp> #include <cstddef> -#include <vector> #include <sys/types.h> #include <sys/event.h> #include <sys/time.h> #include <boost/config.hpp> -#include <boost/date_time/posix_time/posix_time_types.hpp> #include <boost/throw_exception.hpp> #include <boost/system/system_error.hpp> #include <boost/asio/detail/pop_options.hpp> #include <boost/asio/error.hpp> #include <boost/asio/io_service.hpp> -#include <boost/asio/detail/bind_handler.hpp> +#include <boost/asio/detail/hash_map.hpp> #include <boost/asio/detail/mutex.hpp> -#include <boost/asio/detail/task_io_service.hpp> -#include <boost/asio/detail/thread.hpp> -#include <boost/asio/detail/reactor_op_queue.hpp> +#include <boost/asio/detail/op_queue.hpp> +#include <boost/asio/detail/reactor_op.hpp> #include <boost/asio/detail/select_interrupter.hpp> #include <boost/asio/detail/service_base.hpp> -#include <boost/asio/detail/signal_blocker.hpp> #include <boost/asio/detail/socket_types.hpp> -#include <boost/asio/detail/timer_queue.hpp> +#include <boost/asio/detail/timer_op.hpp> +#include <boost/asio/detail/timer_queue_base.hpp> +#include <boost/asio/detail/timer_queue_fwd.hpp> +#include <boost/asio/detail/timer_queue_set.hpp> // Older versions of Mac OS X may not define EV_OOBAND. #if !defined(EV_OOBAND) @@ -56,520 +55,368 @@ namespace boost { namespace asio { namespace detail { -template <bool Own_Thread> class kqueue_reactor - : public boost::asio::detail::service_base<kqueue_reactor<Own_Thread> > + : public boost::asio::detail::service_base<kqueue_reactor> { public: - // Per-descriptor data. - struct per_descriptor_data + enum op_types { read_op = 0, write_op = 1, + connect_op = 1, except_op = 2, max_ops = 3 }; + + // Per-descriptor queues. + struct descriptor_state { - bool allow_speculative_read; - bool allow_speculative_write; + descriptor_state() {} + descriptor_state(const descriptor_state&) {} + void operator=(const descriptor_state&) {} + + mutex mutex_; + op_queue<reactor_op> op_queue_[max_ops]; + bool shutdown_; }; + // Per-descriptor data. + typedef descriptor_state* per_descriptor_data; + // Constructor. kqueue_reactor(boost::asio::io_service& io_service) - : boost::asio::detail::service_base< - kqueue_reactor<Own_Thread> >(io_service), + : boost::asio::detail::service_base<kqueue_reactor>(io_service), + io_service_(use_service<io_service_impl>(io_service)), mutex_(), kqueue_fd_(do_kqueue_create()), - wait_in_progress_(false), interrupter_(), - read_op_queue_(), - write_op_queue_(), - except_op_queue_(), - pending_cancellations_(), - stop_thread_(false), - thread_(0), - shutdown_(false), - need_kqueue_wait_(true) + shutdown_(false) { - // Start the reactor's internal thread only if needed. - if (Own_Thread) - { - boost::asio::detail::signal_blocker sb; - thread_ = new boost::asio::detail::thread( - bind_handler(&kqueue_reactor::call_run_thread, this)); - } - - // Add the interrupter's descriptor to the kqueue. - struct kevent event; - EV_SET(&event, interrupter_.read_descriptor(), - EVFILT_READ, EV_ADD, 0, 0, 0); - ::kevent(kqueue_fd_, &event, 1, 0, 0, 0); + // The interrupter is put into a permanently readable state. Whenever we + // want to interrupt the blocked kevent call we register a one-shot read + // operation against the descriptor. + interrupter_.interrupt(); } // Destructor. ~kqueue_reactor() { - shutdown_service(); close(kqueue_fd_); } // Destroy all user-defined handler objects owned by the service. void shutdown_service() { - boost::asio::detail::mutex::scoped_lock lock(mutex_); + mutex::scoped_lock lock(mutex_); shutdown_ = true; - stop_thread_ = true; lock.unlock(); - if (thread_) + op_queue<operation> ops; + + descriptor_map::iterator iter = registered_descriptors_.begin(); + descriptor_map::iterator end = registered_descriptors_.end(); + while (iter != end) { - interrupter_.interrupt(); - thread_->join(); - delete thread_; - thread_ = 0; + for (int i = 0; i < max_ops; ++i) + ops.push(iter->second.op_queue_[i]); + iter->second.shutdown_ = true; + ++iter; } - read_op_queue_.destroy_operations(); - write_op_queue_.destroy_operations(); - except_op_queue_.destroy_operations(); - - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - timer_queues_[i]->destroy_timers(); - timer_queues_.clear(); + timer_queues_.get_all_timers(ops); } - // Initialise the task, but only if the reactor is not in its own thread. + // Initialise the task. void init_task() { - if (!Own_Thread) - { - typedef task_io_service<kqueue_reactor<Own_Thread> > task_io_service_type; - use_service<task_io_service_type>(this->get_io_service()).init_task(); - } + io_service_.init_task(); } // Register a socket with the reactor. Returns 0 on success, system error // code on failure. - int register_descriptor(socket_type, per_descriptor_data& descriptor_data) + int register_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data) { - descriptor_data.allow_speculative_read = true; - descriptor_data.allow_speculative_write = true; + mutex::scoped_lock lock(registered_descriptors_mutex_); + + descriptor_map::iterator new_entry = registered_descriptors_.insert( + std::make_pair(descriptor, descriptor_state())).first; + descriptor_data = &new_entry->second; + + descriptor_data->shutdown_ = false; return 0; } - // Start a new read operation. The handler object will be invoked when the - // given descriptor is ready to be read, or an error has occurred. - template <typename Handler> - void start_read_op(socket_type descriptor, - per_descriptor_data& descriptor_data, Handler handler, - bool allow_speculative_read = true) + // Start a new operation. The reactor operation will be performed when the + // given descriptor is flagged as ready, or an error has occurred. + void start_op(int op_type, socket_type descriptor, + per_descriptor_data& descriptor_data, + reactor_op* op, bool allow_speculative) { - if (allow_speculative_read && descriptor_data.allow_speculative_read) - { - boost::system::error_code ec; - std::size_t bytes_transferred = 0; - if (handler.perform(ec, bytes_transferred)) - { - handler.complete(ec, bytes_transferred); - return; - } - - // We only get one shot at a speculative read in this function. - allow_speculative_read = false; - } - - boost::asio::detail::mutex::scoped_lock lock(mutex_); - - if (shutdown_) + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + if (descriptor_data->shutdown_) return; - if (!allow_speculative_read) - need_kqueue_wait_ = true; - else if (!read_op_queue_.has_operation(descriptor)) + bool first = descriptor_data->op_queue_[op_type].empty(); + if (first) { - // Speculative reads are ok as there are no queued read operations. - descriptor_data.allow_speculative_read = true; - - boost::system::error_code ec; - std::size_t bytes_transferred = 0; - if (handler.perform(ec, bytes_transferred)) + if (allow_speculative) { - handler.complete(ec, bytes_transferred); - return; + if (op_type != read_op || descriptor_data->op_queue_[except_op].empty()) + { + if (op->perform()) + { + descriptor_lock.unlock(); + io_service_.post_immediate_completion(op); + return; + } + } } } - // Speculative reads are not ok as there will be queued read operations. - descriptor_data.allow_speculative_read = false; + descriptor_data->op_queue_[op_type].push(op); + io_service_.work_started(); - if (read_op_queue_.enqueue_operation(descriptor, handler)) + if (first) { struct kevent event; - EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + switch (op_type) + { + case read_op: + EV_SET(&event, descriptor, EVFILT_READ, + EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + break; + case write_op: + EV_SET(&event, descriptor, EVFILT_WRITE, + EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + break; + case except_op: + if (!descriptor_data->op_queue_[read_op].empty()) + return; // Already registered for read events. + EV_SET(&event, descriptor, EVFILT_READ, + EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data); + break; + } + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) { - boost::system::error_code ec(errno, + op->ec_ = boost::system::error_code(errno, boost::asio::error::get_system_category()); - read_op_queue_.perform_all_operations(descriptor, ec); + descriptor_data->op_queue_[op_type].pop(); + io_service_.post_deferred_completion(op); } } } - // Start a new write operation. The handler object will be invoked when the - // given descriptor is ready to be written, or an error has occurred. - template <typename Handler> - void start_write_op(socket_type descriptor, - per_descriptor_data& descriptor_data, Handler handler, - bool allow_speculative_write = true) + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + void cancel_ops(socket_type descriptor, per_descriptor_data& descriptor_data) { - if (allow_speculative_write && descriptor_data.allow_speculative_write) - { - boost::system::error_code ec; - std::size_t bytes_transferred = 0; - if (handler.perform(ec, bytes_transferred)) - { - handler.complete(ec, bytes_transferred); - return; - } - - // We only get one shot at a speculative write in this function. - allow_speculative_write = false; - } + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - boost::asio::detail::mutex::scoped_lock lock(mutex_); - - if (shutdown_) - return; - - if (!allow_speculative_write) - need_kqueue_wait_ = true; - else if (!write_op_queue_.has_operation(descriptor)) + op_queue<operation> ops; + for (int i = 0; i < max_ops; ++i) { - // Speculative writes are ok as there are no queued write operations. - descriptor_data.allow_speculative_write = true; - - boost::system::error_code ec; - std::size_t bytes_transferred = 0; - if (handler.perform(ec, bytes_transferred)) + while (reactor_op* op = descriptor_data->op_queue_[i].front()) { - handler.complete(ec, bytes_transferred); - return; + op->ec_ = boost::asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); } } - // Speculative writes are not ok as there will be queued write operations. - descriptor_data.allow_speculative_write = false; + descriptor_lock.unlock(); - if (write_op_queue_.enqueue_operation(descriptor, handler)) - { - struct kevent event; - EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0); - if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) - { - boost::system::error_code ec(errno, - boost::asio::error::get_system_category()); - write_op_queue_.perform_all_operations(descriptor, ec); - } - } + io_service_.post_deferred_completions(ops); } - // Start a new exception operation. The handler object will be invoked when - // the given descriptor has exception information, or an error has occurred. - template <typename Handler> - void start_except_op(socket_type descriptor, - per_descriptor_data&, Handler handler) + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. + void close_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data) { - boost::asio::detail::mutex::scoped_lock lock(mutex_); + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); - if (shutdown_) - return; + // Remove the descriptor from the set of known descriptors. The descriptor + // will be automatically removed from the kqueue set when it is closed. + descriptor_data->shutdown_ = true; - if (except_op_queue_.enqueue_operation(descriptor, handler)) + op_queue<operation> ops; + for (int i = 0; i < max_ops; ++i) { - struct kevent event; - if (read_op_queue_.has_operation(descriptor)) - EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); - else - EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0); - if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + while (reactor_op* op = descriptor_data->op_queue_[i].front()) { - boost::system::error_code ec(errno, - boost::asio::error::get_system_category()); - except_op_queue_.perform_all_operations(descriptor, ec); + op->ec_ = boost::asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); } } - } - - // Start a new write operation. The handler object will be invoked when the - // given descriptor is ready to be written, or an error has occurred. - template <typename Handler> - void start_connect_op(socket_type descriptor, - per_descriptor_data& descriptor_data, Handler handler) - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - - if (shutdown_) - return; - // Speculative writes are not ok as there will be queued write operations. - descriptor_data.allow_speculative_write = false; + descriptor_lock.unlock(); - if (write_op_queue_.enqueue_operation(descriptor, handler)) - { - struct kevent event; - EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0); - if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) - { - boost::system::error_code ec(errno, - boost::asio::error::get_system_category()); - write_op_queue_.perform_all_operations(descriptor, ec); - } - } - } + registered_descriptors_.erase(descriptor); - // Cancel all operations associated with the given descriptor. The - // handlers associated with the descriptor will be invoked with the - // operation_aborted error. - void cancel_ops(socket_type descriptor, per_descriptor_data&) - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - cancel_ops_unlocked(descriptor); - } + descriptors_lock.unlock(); - // Cancel any operations that are running against the descriptor and remove - // its registration from the reactor. - void close_descriptor(socket_type descriptor, per_descriptor_data&) - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - - // Remove the descriptor from kqueue. - struct kevent event[2]; - EV_SET(&event[0], descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0); - EV_SET(&event[1], descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0); - ::kevent(kqueue_fd_, event, 2, 0, 0, 0); - - // Cancel any outstanding operations associated with the descriptor. - cancel_ops_unlocked(descriptor); + io_service_.post_deferred_completions(ops); } // Add a new timer queue to the reactor. template <typename Time_Traits> void add_timer_queue(timer_queue<Time_Traits>& timer_queue) { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - timer_queues_.push_back(&timer_queue); + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&timer_queue); } // Remove a timer queue from the reactor. template <typename Time_Traits> void remove_timer_queue(timer_queue<Time_Traits>& timer_queue) { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - if (timer_queues_[i] == &timer_queue) - { - timer_queues_.erase(timer_queues_.begin() + i); - return; - } - } + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&timer_queue); } - // Schedule a timer in the given timer queue to expire at the specified - // absolute time. The handler object will be invoked when the timer expires. - template <typename Time_Traits, typename Handler> + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template <typename Time_Traits> void schedule_timer(timer_queue<Time_Traits>& timer_queue, - const typename Time_Traits::time_type& time, Handler handler, void* token) + const typename Time_Traits::time_type& time, timer_op* op, void* token) { - boost::asio::detail::mutex::scoped_lock lock(mutex_); + mutex::scoped_lock lock(mutex_); if (!shutdown_) - if (timer_queue.enqueue_timer(time, handler, token)) - interrupter_.interrupt(); + { + bool earliest = timer_queue.enqueue_timer(time, op, token); + io_service_.work_started(); + if (earliest) + interrupt(); + } } - // Cancel the timer associated with the given token. Returns the number of - // handlers that have been posted or dispatched. + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. template <typename Time_Traits> std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token) { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - std::size_t n = timer_queue.cancel_timer(token); - if (n > 0) - interrupter_.interrupt(); + mutex::scoped_lock lock(mutex_); + op_queue<operation> ops; + std::size_t n = timer_queue.cancel_timer(token, ops); + lock.unlock(); + io_service_.post_deferred_completions(ops); return n; } -private: - friend class task_io_service<kqueue_reactor<Own_Thread> >; - // Run the kqueue loop. - void run(bool block) + void run(bool block, op_queue<operation>& ops) { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - - // Dispatch any operation cancellations that were made while the select - // loop was not running. - read_op_queue_.perform_cancellations(); - write_op_queue_.perform_cancellations(); - except_op_queue_.perform_cancellations(); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - timer_queues_[i]->dispatch_cancellations(); - - // Check if the thread is supposed to stop. - if (stop_thread_) - { - complete_operations_and_timers(lock); - return; - } - - // We can return immediately if there's no work to do and the reactor is - // not supposed to block. - if (!block && read_op_queue_.empty() && write_op_queue_.empty() - && except_op_queue_.empty() && all_timer_queues_are_empty()) - { - complete_operations_and_timers(lock); - return; - } + mutex::scoped_lock lock(mutex_); // Determine how long to block while waiting for events. timespec timeout_buf = { 0, 0 }; timespec* timeout = block ? get_timeout(timeout_buf) : &timeout_buf; - 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; + int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout); // Dispatch the waiting events. for (int i = 0; i < num_events; ++i) { int descriptor = events[i].ident; - if (descriptor == interrupter_.read_descriptor()) + void* ptr = events[i].udata; + if (ptr == &interrupter_) { - interrupter_.reset(); + // No need to reset the interrupter since we're leaving the descriptor + // in a ready-to-read state and relying on one-shot notifications. } - else if (events[i].filter == EVFILT_READ) + else { - // Dispatch operations associated with the descriptor. - bool more_reads = false; - bool more_except = false; - if (events[i].flags & EV_ERROR) + 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 filter[max_ops] = + { EVFILT_READ, EVFILT_WRITE, EVFILT_READ }; + for (int j = max_ops - 1; j >= 0; --j) { - boost::system::error_code error( - events[i].data, boost::asio::error::get_system_category()); - except_op_queue_.perform_all_operations(descriptor, error); - read_op_queue_.perform_all_operations(descriptor, error); - } - else if (events[i].flags & EV_OOBAND) - { - boost::system::error_code error; - more_except = except_op_queue_.perform_operation(descriptor, error); - if (events[i].data > 0) - more_reads = read_op_queue_.perform_operation(descriptor, error); - else - more_reads = read_op_queue_.has_operation(descriptor); - } - else - { - boost::system::error_code error; - more_reads = read_op_queue_.perform_operation(descriptor, error); - more_except = except_op_queue_.has_operation(descriptor); + if (events[i].filter == filter[j]) + { + if (j != except_op || events[i].flags & EV_OOBAND) + { + while (reactor_op* op = descriptor_data->op_queue_[j].front()) + { + if (events[i].flags & EV_ERROR) + { + op->ec_ = boost::system::error_code(events[i].data, + boost::asio::error::get_system_category()); + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + if (op->perform()) + { + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + else + break; + } + } + } } - // Update the descriptor in the kqueue. + // Renew registration for event notifications. struct kevent event; - if (more_reads) - EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); - else if (more_except) - EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0); - else - EV_SET(&event, descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0); - if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) - { - boost::system::error_code error(errno, - boost::asio::error::get_system_category()); - except_op_queue_.perform_all_operations(descriptor, error); - read_op_queue_.perform_all_operations(descriptor, error); - } - } - else if (events[i].filter == EVFILT_WRITE) - { - // Dispatch operations associated with the descriptor. - bool more_writes = false; - if (events[i].flags & EV_ERROR) - { - boost::system::error_code error( - events[i].data, boost::asio::error::get_system_category()); - write_op_queue_.perform_all_operations(descriptor, error); - } - else + switch (events[i].filter) { - boost::system::error_code error; - more_writes = write_op_queue_.perform_operation(descriptor, error); + case EVFILT_READ: + if (!descriptor_data->op_queue_[read_op].empty()) + EV_SET(&event, descriptor, EVFILT_READ, + EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + else if (!descriptor_data->op_queue_[except_op].empty()) + EV_SET(&event, descriptor, EVFILT_READ, + EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data); + else + continue; + case EVFILT_WRITE: + if (!descriptor_data->op_queue_[write_op].empty()) + EV_SET(&event, descriptor, EVFILT_WRITE, + EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + else + continue; + default: + break; } - - // Update the descriptor in the kqueue. - struct kevent event; - if (more_writes) - EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0); - else - EV_SET(&event, descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0); if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) { boost::system::error_code error(errno, boost::asio::error::get_system_category()); - write_op_queue_.perform_all_operations(descriptor, error); + for (int j = 0; j < max_ops; ++j) + { + while (reactor_op* op = descriptor_data->op_queue_[j].front()) + { + op->ec_ = error; + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + } } } } - read_op_queue_.perform_cancellations(); - write_op_queue_.perform_cancellations(); - except_op_queue_.perform_cancellations(); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - timer_queues_[i]->dispatch_timers(); - timer_queues_[i]->dispatch_cancellations(); - } - - // Issue any pending cancellations. - for (std::size_t i = 0; i < pending_cancellations_.size(); ++i) - cancel_ops_unlocked(pending_cancellations_[i]); - pending_cancellations_.clear(); - - // Determine whether kqueue needs to be called next time the reactor is run. - need_kqueue_wait_ = !read_op_queue_.empty() - || !write_op_queue_.empty() || !except_op_queue_.empty(); - - complete_operations_and_timers(lock); - } - - // Run the select loop in the thread. - void run_thread() - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - while (!stop_thread_) - { - lock.unlock(); - run(true); - lock.lock(); - } - } - - // Entry point for the select loop thread. - static void call_run_thread(kqueue_reactor* reactor) - { - reactor->run_thread(); + lock.lock(); + timer_queues_.get_ready_timers(ops); } - // Interrupt the select loop. + // Interrupt the kqueue loop. void interrupt() { - interrupter_.interrupt(); + struct kevent event; + EV_SET(&event, interrupter_.read_descriptor(), + EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, &interrupter_); + ::kevent(kqueue_fd_, &event, 1, 0, 0, 0); } +private: // Create the kqueue file descriptor. Throws an exception if the descriptor // cannot be created. static int do_kqueue_create() @@ -586,118 +433,47 @@ private: return fd; } - // Check if all timer queues are empty. - bool all_timer_queues_are_empty() const - { - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - if (!timer_queues_[i]->empty()) - return false; - return true; - } - // Get the timeout value for the kevent call. timespec* get_timeout(timespec& ts) { - if (all_timer_queues_are_empty()) - return 0; - // By default we will wait no longer than 5 minutes. This will ensure that // any changes to the system clock are detected after no longer than this. - boost::posix_time::time_duration minimum_wait_duration - = boost::posix_time::minutes(5); - - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - boost::posix_time::time_duration wait_duration - = timer_queues_[i]->wait_duration(); - if (wait_duration < minimum_wait_duration) - minimum_wait_duration = wait_duration; - } - - if (minimum_wait_duration > boost::posix_time::time_duration()) - { - ts.tv_sec = minimum_wait_duration.total_seconds(); - ts.tv_nsec = minimum_wait_duration.total_nanoseconds() % 1000000000; - } - else - { - ts.tv_sec = 0; - ts.tv_nsec = 0; - } - + long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; return &ts; } - // Cancel all operations associated with the given descriptor. The do_cancel - // function of the handler objects will be invoked. This function does not - // acquire the kqueue_reactor's mutex. - void cancel_ops_unlocked(socket_type descriptor) - { - bool interrupt = read_op_queue_.cancel_operations(descriptor); - interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; - interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; - if (interrupt) - interrupter_.interrupt(); - } - - // Clean up operations and timers. We must not hold the lock since the - // destructors may make calls back into this reactor. We make a copy of the - // vector of timer queues since the original may be modified while the lock - // is not held. - void complete_operations_and_timers( - boost::asio::detail::mutex::scoped_lock& lock) - { - timer_queues_for_cleanup_ = timer_queues_; - lock.unlock(); - read_op_queue_.complete_operations(); - write_op_queue_.complete_operations(); - except_op_queue_.complete_operations(); - for (std::size_t i = 0; i < timer_queues_for_cleanup_.size(); ++i) - timer_queues_for_cleanup_[i]->complete_timers(); - } + // The io_service implementation used to post completions. + io_service_impl& io_service_; // Mutex to protect access to internal data. - boost::asio::detail::mutex mutex_; + mutex mutex_; // The kqueue file descriptor. int kqueue_fd_; - // Whether the kqueue wait call is currently in progress - bool wait_in_progress_; - // The interrupter is used to break a blocking kevent call. select_interrupter interrupter_; - // The queue of read operations. - reactor_op_queue<socket_type> read_op_queue_; - - // The queue of write operations. - reactor_op_queue<socket_type> write_op_queue_; - - // The queue of except operations. - reactor_op_queue<socket_type> except_op_queue_; - // The timer queues. - std::vector<timer_queue_base*> timer_queues_; - - // A copy of the timer queues, used when cleaning up timers. The copy is - // stored as a class data member to avoid unnecessary memory allocation. - std::vector<timer_queue_base*> timer_queues_for_cleanup_; - - // The descriptors that are pending cancellation. - std::vector<socket_type> pending_cancellations_; - - // Does the reactor loop thread need to stop. - bool stop_thread_; - - // The thread that is running the reactor loop. - boost::asio::detail::thread* thread_; + timer_queue_set timer_queues_; // Whether the service has been shut down. bool shutdown_; - // Whether we need to call kqueue the next time the reactor is run. - bool need_kqueue_wait_; + // Mutex to protect access to the registered descriptors. + mutex registered_descriptors_mutex_; + + // Keep track of all registered descriptors. This code relies on the fact that + // the hash_map implementation pools deleted nodes, meaning that we can assume + // our descriptor_state pointer remains valid even after the entry is removed. + // Technically this is not true for C++98, as that standard says that spliced + // elements in a list are invalidated. However, C++0x fixes this shortcoming + // so we'll just assume that C++98 std::list implementations will do the right + // thing anyway. + typedef detail::hash_map<socket_type, descriptor_state> descriptor_map; + descriptor_map registered_descriptors_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/kqueue_reactor_fwd.hpp b/3rdParty/Boost/src/boost/asio/detail/kqueue_reactor_fwd.hpp index b2751bf..0471c39 100644 --- a/3rdParty/Boost/src/boost/asio/detail/kqueue_reactor_fwd.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/kqueue_reactor_fwd.hpp @@ -30,7 +30,6 @@ namespace boost { namespace asio { namespace detail { -template <bool Own_Thread> class kqueue_reactor; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/macos_fenced_block.hpp b/3rdParty/Boost/src/boost/asio/detail/macos_fenced_block.hpp new file mode 100644 index 0000000..496d7f0 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/macos_fenced_block.hpp @@ -0,0 +1,59 @@ +// +// macos_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP +#define BOOST_ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/push_options.hpp> +#include <boost/config.hpp> +#include <boost/asio/detail/pop_options.hpp> + +#if defined(__MACH__) && defined(__APPLE__) + +#include <boost/asio/detail/push_options.hpp> +#include <libkern/OSAtomic.h> +#include <boost/asio/detail/pop_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +class macos_fenced_block + : private noncopyable +{ +public: + // Constructor. + macos_fenced_block() + { + OSMemoryBarrier(); + } + + // Destructor. + ~macos_fenced_block() + { + OSMemoryBarrier(); + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(__MACH__) && defined(__APPLE__) + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/mutex.hpp b/3rdParty/Boost/src/boost/asio/detail/mutex.hpp index fc7ed83..e804ec2 100644 --- a/3rdParty/Boost/src/boost/asio/detail/mutex.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/mutex.hpp @@ -21,7 +21,7 @@ #include <boost/config.hpp> #include <boost/asio/detail/pop_options.hpp> -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) # include <boost/asio/detail/null_mutex.hpp> #elif defined(BOOST_WINDOWS) # include <boost/asio/detail/win_mutex.hpp> @@ -35,7 +35,7 @@ namespace boost { namespace asio { namespace detail { -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) typedef null_mutex mutex; #elif defined(BOOST_WINDOWS) typedef win_mutex mutex; diff --git a/3rdParty/Boost/src/boost/asio/detail/null_buffers_op.hpp b/3rdParty/Boost/src/boost/asio/detail/null_buffers_op.hpp new file mode 100644 index 0000000..02160af --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/null_buffers_op.hpp @@ -0,0 +1,79 @@ +// +// null_buffers_op.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_NULL_BUFFERS_OP_HPP +#define BOOST_ASIO_DETAIL_NULL_BUFFERS_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/fenced_block.hpp> +#include <boost/asio/detail/handler_alloc_helpers.hpp> +#include <boost/asio/detail/handler_invoke_helpers.hpp> +#include <boost/asio/detail/reactor_op.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename Handler> +class null_buffers_op : public reactor_op +{ +public: + null_buffers_op(Handler handler) + : reactor_op(&null_buffers_op::do_perform, &null_buffers_op::do_complete), + handler_(handler) + { + } + + static bool do_perform(reactor_op*) + { + return true; + } + + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + null_buffers_op* o(static_cast<null_buffers_op*>(base)); + typedef handler_alloc_traits<Handler, null_buffers_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); + + // Make the upcall if required. + if (owner) + { + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an upcall, + // a sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_NULL_BUFFERS_OP_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/null_event.hpp b/3rdParty/Boost/src/boost/asio/detail/null_event.hpp index 184f753..2dc0bf6 100644 --- a/3rdParty/Boost/src/boost/asio/detail/null_event.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/null_event.hpp @@ -21,7 +21,7 @@ #include <boost/config.hpp> #include <boost/asio/detail/pop_options.hpp> -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) #include <boost/asio/detail/noncopyable.hpp> @@ -49,6 +49,12 @@ public: { } + // Signal the event and unlock the mutex. + template <typename Lock> + void signal_and_unlock(Lock&) + { + } + // Reset the event. template <typename Lock> void clear(Lock&) @@ -66,7 +72,7 @@ public: } // namespace asio } // namespace boost -#endif // !defined(BOOST_HAS_THREADS) +#endif // !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) #include <boost/asio/detail/pop_options.hpp> diff --git a/3rdParty/Boost/src/boost/asio/detail/null_fenced_block.hpp b/3rdParty/Boost/src/boost/asio/detail/null_fenced_block.hpp new file mode 100644 index 0000000..ea8e0fc --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/null_fenced_block.hpp @@ -0,0 +1,45 @@ +// +// null_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_NULL_FENCED_BLOCK_HPP +#define BOOST_ASIO_DETAIL_NULL_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +class null_fenced_block + : private noncopyable +{ +public: + // Constructor. + null_fenced_block() + { + } + + // Destructor. + ~null_fenced_block() + { + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_NULL_FENCED_BLOCK_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/null_mutex.hpp b/3rdParty/Boost/src/boost/asio/detail/null_mutex.hpp index bdf5617..edc1358 100644 --- a/3rdParty/Boost/src/boost/asio/detail/null_mutex.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/null_mutex.hpp @@ -21,7 +21,7 @@ #include <boost/config.hpp> #include <boost/asio/detail/pop_options.hpp> -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) #include <boost/asio/detail/noncopyable.hpp> #include <boost/asio/detail/scoped_lock.hpp> @@ -61,7 +61,7 @@ public: } // namespace asio } // namespace boost -#endif // !defined(BOOST_HAS_THREADS) +#endif // !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) #include <boost/asio/detail/pop_options.hpp> diff --git a/3rdParty/Boost/src/boost/asio/detail/null_signal_blocker.hpp b/3rdParty/Boost/src/boost/asio/detail/null_signal_blocker.hpp index 7fe5c10..65b55e9 100644 --- a/3rdParty/Boost/src/boost/asio/detail/null_signal_blocker.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/null_signal_blocker.hpp @@ -21,7 +21,7 @@ #include <boost/config.hpp> #include <boost/asio/detail/pop_options.hpp> -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) #include <boost/asio/detail/noncopyable.hpp> @@ -58,7 +58,7 @@ public: } // namespace asio } // namespace boost -#endif // !defined(BOOST_HAS_THREADS) +#endif // !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) #include <boost/asio/detail/pop_options.hpp> diff --git a/3rdParty/Boost/src/boost/asio/detail/null_thread.hpp b/3rdParty/Boost/src/boost/asio/detail/null_thread.hpp index 0a508f3..ba16311 100644 --- a/3rdParty/Boost/src/boost/asio/detail/null_thread.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/null_thread.hpp @@ -22,7 +22,7 @@ #include <boost/system/system_error.hpp> #include <boost/asio/detail/pop_options.hpp> -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) #include <boost/asio/detail/push_options.hpp> #include <boost/throw_exception.hpp> @@ -63,7 +63,7 @@ public: } // namespace asio } // namespace boost -#endif // !defined(BOOST_HAS_THREADS) +#endif // !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) #include <boost/asio/detail/pop_options.hpp> diff --git a/3rdParty/Boost/src/boost/asio/detail/null_tss_ptr.hpp b/3rdParty/Boost/src/boost/asio/detail/null_tss_ptr.hpp index 7c35b15..bd83e7e 100644 --- a/3rdParty/Boost/src/boost/asio/detail/null_tss_ptr.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/null_tss_ptr.hpp @@ -21,7 +21,7 @@ #include <boost/config.hpp> #include <boost/asio/detail/pop_options.hpp> -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) #include <boost/asio/detail/noncopyable.hpp> @@ -65,7 +65,7 @@ private: } // namespace asio } // namespace boost -#endif // !defined(BOOST_HAS_THREADS) +#endif // !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) #include <boost/asio/detail/pop_options.hpp> diff --git a/3rdParty/Boost/src/boost/asio/detail/op_queue.hpp b/3rdParty/Boost/src/boost/asio/detail/op_queue.hpp new file mode 100644 index 0000000..ddf3e18 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/op_queue.hpp @@ -0,0 +1,158 @@ +// +// op_queue.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_OP_QUEUE_HPP +#define BOOST_ASIO_DETAIL_OP_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/noncopyable.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename Operation> +class op_queue; + +class op_queue_access +{ +public: + template <typename Operation> + static Operation* next(Operation* o) + { + return static_cast<Operation*>(o->next_); + } + + template <typename Operation1, typename Operation2> + static void next(Operation1*& o1, Operation2* o2) + { + o1->next_ = o2; + } + + template <typename Operation> + static void destroy(Operation* o) + { + o->destroy(); + } + + template <typename Operation> + static Operation*& front(op_queue<Operation>& q) + { + return q.front_; + } + + template <typename Operation> + static Operation*& back(op_queue<Operation>& q) + { + return q.back_; + } +}; + +template <typename Operation> +class op_queue + : private noncopyable +{ +public: + // Constructor. + op_queue() + : front_(0), + back_(0) + { + } + + // Destructor destroys all operations. + ~op_queue() + { + while (Operation* op = front_) + { + pop(); + op_queue_access::destroy(op); + } + } + + // Get the operation at the front of the queue. + Operation* front() + { + return front_; + } + + // Pop an operation from the front of the queue. + void pop() + { + if (front_) + { + Operation* tmp = front_; + front_ = op_queue_access::next(front_); + if (front_ == 0) + back_ = 0; + op_queue_access::next(tmp, static_cast<Operation*>(0)); + } + } + + // Push an operation on to the back of the queue. + void push(Operation* h) + { + op_queue_access::next(h, static_cast<Operation*>(0)); + if (back_) + { + op_queue_access::next(back_, h); + back_ = h; + } + else + { + front_ = back_ = h; + } + } + + // Push all operations from another queue on to the back of the queue. The + // source queue may contain operations of a derived type. + template <typename OtherOperation> + void push(op_queue<OtherOperation>& q) + { + if (Operation* other_front = op_queue_access::front(q)) + { + if (back_) + op_queue_access::next(back_, other_front); + else + front_ = other_front; + back_ = op_queue_access::back(q); + op_queue_access::front(q) = 0; + op_queue_access::back(q) = 0; + } + } + + // Whether the queue is empty. + bool empty() const + { + return front_ == 0; + } + +private: + friend class op_queue_access; + + // The front of the queue. + Operation* front_; + + // The back of the queue. + Operation* back_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_OP_QUEUE_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/operation.hpp b/3rdParty/Boost/src/boost/asio/detail/operation.hpp new file mode 100644 index 0000000..579b6b5 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/operation.hpp @@ -0,0 +1,45 @@ +// +// operation.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_OPERATION_HPP +#define BOOST_ASIO_DETAIL_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/win_iocp_io_service_fwd.hpp> + +#if defined(BOOST_ASIO_HAS_IOCP) +# include <boost/asio/detail/win_iocp_operation.hpp> +#else +# include <boost/asio/detail/reactor_fwd.hpp> +# include <boost/asio/detail/task_io_service_operation.hpp> +#endif + +namespace boost { +namespace asio { +namespace detail { + +#if defined(BOOST_ASIO_HAS_IOCP) +typedef win_iocp_operation operation; +#else +typedef task_io_service_operation<reactor> operation; +#endif + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_OPERATION_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/posix_event.hpp b/3rdParty/Boost/src/boost/asio/detail/posix_event.hpp index 20c0fe3..b2938cf 100644 --- a/3rdParty/Boost/src/boost/asio/detail/posix_event.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/posix_event.hpp @@ -72,6 +72,16 @@ public: ::pthread_cond_signal(&cond_); // Ignore EINVAL. } + // Signal the event and unlock the mutex. + template <typename Lock> + void signal_and_unlock(Lock& lock) + { + BOOST_ASSERT(lock.locked()); + signalled_ = true; + lock.unlock(); + ::pthread_cond_signal(&cond_); // Ignore EINVAL. + } + // Reset the event. template <typename Lock> void clear(Lock& lock) diff --git a/3rdParty/Boost/src/boost/asio/detail/posix_mutex.hpp b/3rdParty/Boost/src/boost/asio/detail/posix_mutex.hpp index e7209c1..6d9a44f 100644 --- a/3rdParty/Boost/src/boost/asio/detail/posix_mutex.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/posix_mutex.hpp @@ -62,35 +62,19 @@ public: // Destructor. ~posix_mutex() { - ::pthread_mutex_destroy(&mutex_); + ::pthread_mutex_destroy(&mutex_); // Ignore EBUSY. } // Lock the mutex. void lock() { - int error = ::pthread_mutex_lock(&mutex_); - if (error != 0) - { - boost::system::system_error e( - boost::system::error_code(error, - boost::asio::error::get_system_category()), - "mutex"); - boost::throw_exception(e); - } + (void)::pthread_mutex_lock(&mutex_); // Ignore EINVAL. } // Unlock the mutex. void unlock() { - int error = ::pthread_mutex_unlock(&mutex_); - if (error != 0) - { - boost::system::system_error e( - boost::system::error_code(error, - boost::asio::error::get_system_category()), - "mutex"); - boost::throw_exception(e); - } + (void)::pthread_mutex_unlock(&mutex_); // Ignore EINVAL. } private: diff --git a/3rdParty/Boost/src/boost/asio/detail/reactive_descriptor_service.hpp b/3rdParty/Boost/src/boost/asio/detail/reactive_descriptor_service.hpp index 61f676b..536b96a 100644 --- a/3rdParty/Boost/src/boost/asio/detail/reactive_descriptor_service.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/reactive_descriptor_service.hpp @@ -21,10 +21,13 @@ #include <boost/asio/error.hpp> #include <boost/asio/io_service.hpp> #include <boost/asio/detail/bind_handler.hpp> -#include <boost/asio/detail/handler_base_from_member.hpp> -#include <boost/asio/detail/noncopyable.hpp> -#include <boost/asio/detail/service_base.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> #include <boost/asio/detail/descriptor_ops.hpp> +#include <boost/asio/detail/fenced_block.hpp> +#include <boost/asio/detail/noncopyable.hpp> +#include <boost/asio/detail/null_buffers_op.hpp> +#include <boost/asio/detail/reactor.hpp> +#include <boost/asio/detail/reactor_op.hpp> #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) @@ -32,10 +35,7 @@ namespace boost { namespace asio { namespace detail { -template <typename Reactor> class reactive_descriptor_service - : public boost::asio::detail::service_base< - reactive_descriptor_service<Reactor> > { public: // The native type of a descriptor. @@ -55,22 +55,28 @@ public: private: // Only this service will have access to the internal values. - friend class reactive_descriptor_service<Reactor>; + friend class reactive_descriptor_service; // The native descriptor representation. int descriptor_; enum { - user_set_non_blocking = 1, // The user wants a non-blocking descriptor. - internal_non_blocking = 2 // The descriptor has been set non-blocking. + // The user wants a non-blocking descriptor. + user_set_non_blocking = 1, + + // The descriptor has been set non-blocking. + internal_non_blocking = 2, + + // Helper "flag" used to determine whether the descriptor is non-blocking. + non_blocking = user_set_non_blocking | internal_non_blocking }; // Flags indicating the current state of the descriptor. unsigned char flags_; // Per-descriptor data used by the reactor. - typename Reactor::per_descriptor_data reactor_data_; + reactor::per_descriptor_data reactor_data_; }; // The maximum number of buffers to support in a single operation. @@ -78,9 +84,8 @@ public: // Constructor. reactive_descriptor_service(boost::asio::io_service& io_service) - : boost::asio::detail::service_base< - reactive_descriptor_service<Reactor> >(io_service), - reactor_(boost::asio::use_service<Reactor>(io_service)) + : io_service_impl_(boost::asio::use_service<io_service_impl>(io_service)), + reactor_(boost::asio::use_service<reactor>(io_service)) { reactor_.init_task(); } @@ -209,19 +214,31 @@ public: return ec; } - if (command.name() == static_cast<int>(FIONBIO)) + descriptor_ops::ioctl(impl.descriptor_, command.name(), + static_cast<ioctl_arg_type*>(command.data()), ec); + + // When updating the non-blocking mode we always perform the ioctl syscall, + // even if the flags would otherwise indicate that the descriptor is + // already in the correct state. This ensures that the underlying + // descriptor is put into the state that has been requested by the user. If + // the ioctl syscall was successful then we need to update the flags to + // match. + if (!ec && command.name() == static_cast<int>(FIONBIO)) { - if (command.get()) + if (*static_cast<ioctl_arg_type*>(command.data())) + { impl.flags_ |= implementation_type::user_set_non_blocking; + } else - impl.flags_ &= ~implementation_type::user_set_non_blocking; - ec = boost::system::error_code(); - } - else - { - descriptor_ops::ioctl(impl.descriptor_, command.name(), - static_cast<ioctl_arg_type*>(command.data()), ec); + { + // Clearing the non-blocking mode always overrides any internally-set + // non-blocking flag. Any subsequent asynchronous operations will need + // to re-enable non-blocking I/O. + impl.flags_ &= ~(implementation_type::user_set_non_blocking + | implementation_type::internal_non_blocking); + } } + return ec; } @@ -236,47 +253,22 @@ public: return 0; } - // Copy buffers into array. - descriptor_ops::buf bufs[max_buffers]; - typename ConstBufferSequence::const_iterator iter = buffers.begin(); - typename ConstBufferSequence::const_iterator end = buffers.end(); - size_t i = 0; - size_t total_buffer_size = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::const_buffer buffer(*iter); - descriptor_ops::init_buf(bufs[i], - boost::asio::buffer_cast<const void*>(buffer), - boost::asio::buffer_size(buffer)); - total_buffer_size += boost::asio::buffer_size(buffer); - } + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(buffers); // A request to read_some 0 bytes on a stream is a no-op. - if (total_buffer_size == 0) + if (bufs.all_empty()) { ec = boost::system::error_code(); return 0; } - // Make descriptor non-blocking if user wants non-blocking. - if (impl.flags_ & implementation_type::user_set_non_blocking) - { - if (!(impl.flags_ & implementation_type::internal_non_blocking)) - { - ioctl_arg_type non_blocking = 1; - if (descriptor_ops::ioctl(impl.descriptor_, - FIONBIO, &non_blocking, ec)) - return 0; - impl.flags_ |= implementation_type::internal_non_blocking; - } - } - // Send the data. for (;;) { // Try to complete the operation without blocking. int bytes_sent = descriptor_ops::gather_write( - impl.descriptor_, bufs, i, ec); + impl.descriptor_, bufs.buffers(), bufs.count(), ec); // Check if operation succeeded. if (bytes_sent >= 0) @@ -310,48 +302,31 @@ public: return 0; } - template <typename ConstBufferSequence, typename Handler> - class write_operation : - public handler_base_from_member<Handler> + template <typename ConstBufferSequence> + class write_op_base : public reactor_op { public: - write_operation(int descriptor, boost::asio::io_service& io_service, - const ConstBufferSequence& buffers, Handler handler) - : handler_base_from_member<Handler>(handler), + write_op_base(int descriptor, + const ConstBufferSequence& buffers, func_type complete_func) + : reactor_op(&write_op_base::do_perform, complete_func), descriptor_(descriptor), - io_service_(io_service), - work_(io_service), buffers_(buffers) { } - bool perform(boost::system::error_code& ec, - std::size_t& bytes_transferred) + static bool do_perform(reactor_op* base) { - // Check whether the operation was successful. - if (ec) - { - bytes_transferred = 0; - return true; - } + write_op_base* o(static_cast<write_op_base*>(base)); - // Copy buffers into array. - descriptor_ops::buf bufs[max_buffers]; - typename ConstBufferSequence::const_iterator iter = buffers_.begin(); - typename ConstBufferSequence::const_iterator end = buffers_.end(); - size_t i = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::const_buffer buffer(*iter); - descriptor_ops::init_buf(bufs[i], - boost::asio::buffer_cast<const void*>(buffer), - boost::asio::buffer_size(buffer)); - } + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(o->buffers_); for (;;) { // Write the data. - int bytes = descriptor_ops::gather_write(descriptor_, bufs, i, ec); + boost::system::error_code ec; + int bytes = descriptor_ops::gather_write( + o->descriptor_, bufs.buffers(), bufs.count(), ec); // Retry operation if interrupted by signal. if (ec == boost::asio::error::interrupted) @@ -362,120 +337,89 @@ public: || ec == boost::asio::error::try_again) return false; - bytes_transferred = (bytes < 0 ? 0 : bytes); + o->ec_ = ec; + o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); return true; } } - void complete(const boost::system::error_code& ec, - std::size_t bytes_transferred) - { - io_service_.post(bind_handler(this->handler_, ec, bytes_transferred)); - } - private: int descriptor_; - boost::asio::io_service& io_service_; - boost::asio::io_service::work work_; ConstBufferSequence buffers_; }; - // Start an asynchronous write. The data being sent must be valid for the - // lifetime of the asynchronous operation. template <typename ConstBufferSequence, typename Handler> - void async_write_some(implementation_type& impl, - const ConstBufferSequence& buffers, Handler handler) - { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); - } - else - { - // Determine total size of buffers. - typename ConstBufferSequence::const_iterator iter = buffers.begin(); - typename ConstBufferSequence::const_iterator end = buffers.end(); - size_t i = 0; - size_t total_buffer_size = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::const_buffer buffer(*iter); - total_buffer_size += boost::asio::buffer_size(buffer); - } - - // A request to read_some 0 bytes on a stream is a no-op. - if (total_buffer_size == 0) - { - this->get_io_service().post(bind_handler(handler, - boost::system::error_code(), 0)); - return; - } - - // Make descriptor non-blocking. - if (!(impl.flags_ & implementation_type::internal_non_blocking)) - { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec)) - { - this->get_io_service().post(bind_handler(handler, ec, 0)); - return; - } - impl.flags_ |= implementation_type::internal_non_blocking; - } - - reactor_.start_write_op(impl.descriptor_, impl.reactor_data_, - write_operation<ConstBufferSequence, Handler>( - impl.descriptor_, this->get_io_service(), buffers, handler)); - } - } - - template <typename Handler> - class null_buffers_operation : - public handler_base_from_member<Handler> + class write_op : public write_op_base<ConstBufferSequence> { public: - null_buffers_operation(boost::asio::io_service& io_service, Handler handler) - : handler_base_from_member<Handler>(handler), - work_(io_service) + write_op(int descriptor, + const ConstBufferSequence& buffers, Handler handler) + : write_op_base<ConstBufferSequence>( + descriptor, buffers, &write_op::do_complete), + handler_(handler) { } - bool perform(boost::system::error_code&, - std::size_t& bytes_transferred) + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) { - bytes_transferred = 0; - return true; - } + // Take ownership of the handler object. + write_op* o(static_cast<write_op*>(base)); + typedef handler_alloc_traits<Handler, write_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); - void complete(const boost::system::error_code& ec, - std::size_t bytes_transferred) - { - work_.get_io_service().post(bind_handler( - this->handler_, ec, bytes_transferred)); + // Make the upcall if required. + if (owner) + { + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } private: - boost::asio::io_service::work work_; + Handler handler_; }; + // Start an asynchronous write. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template <typename ConstBufferSequence, typename Handler> + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef write_op<ConstBufferSequence, Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, impl.descriptor_, buffers, handler); + + start_op(impl, reactor::write_op, ptr.get(), true, + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence>::all_empty(buffers)); + ptr.release(); + } + // Start an asynchronous wait until data can be written without blocking. template <typename Handler> void async_write_some(implementation_type& impl, const null_buffers&, Handler handler) { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); - } - else - { - reactor_.start_write_op(impl.descriptor_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler), - false); - } + // Allocate and construct an operation to wrap the handler. + typedef null_buffers_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); + + start_op(impl, reactor::write_op, ptr.get(), false, false); + ptr.release(); } // Read some data from the stream. Returns the number of bytes read. @@ -489,46 +433,22 @@ public: return 0; } - // Copy buffers into array. - descriptor_ops::buf bufs[max_buffers]; - typename MutableBufferSequence::const_iterator iter = buffers.begin(); - typename MutableBufferSequence::const_iterator end = buffers.end(); - size_t i = 0; - size_t total_buffer_size = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::mutable_buffer buffer(*iter); - descriptor_ops::init_buf(bufs[i], - boost::asio::buffer_cast<void*>(buffer), - boost::asio::buffer_size(buffer)); - total_buffer_size += boost::asio::buffer_size(buffer); - } + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); // A request to read_some 0 bytes on a stream is a no-op. - if (total_buffer_size == 0) + if (bufs.all_empty()) { ec = boost::system::error_code(); return 0; } - // Make descriptor non-blocking if user wants non-blocking. - if (impl.flags_ & implementation_type::user_set_non_blocking) - { - if (!(impl.flags_ & implementation_type::internal_non_blocking)) - { - ioctl_arg_type non_blocking = 1; - if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec)) - return 0; - impl.flags_ |= implementation_type::internal_non_blocking; - } - } - // Read some data. for (;;) { // Try to complete the operation without blocking. int bytes_read = descriptor_ops::scatter_read( - impl.descriptor_, bufs, i, ec); + impl.descriptor_, bufs.buffers(), bufs.count(), ec); // Check if operation succeeded. if (bytes_read > 0) @@ -569,48 +489,31 @@ public: return 0; } - template <typename MutableBufferSequence, typename Handler> - class read_operation : - public handler_base_from_member<Handler> + template <typename MutableBufferSequence> + class read_op_base : public reactor_op { public: - read_operation(int descriptor, boost::asio::io_service& io_service, - const MutableBufferSequence& buffers, Handler handler) - : handler_base_from_member<Handler>(handler), + read_op_base(int descriptor, + const MutableBufferSequence& buffers, func_type complete_func) + : reactor_op(&read_op_base::do_perform, complete_func), descriptor_(descriptor), - io_service_(io_service), - work_(io_service), buffers_(buffers) { } - bool perform(boost::system::error_code& ec, - std::size_t& bytes_transferred) + static bool do_perform(reactor_op* base) { - // Check whether the operation was successful. - if (ec) - { - bytes_transferred = 0; - return true; - } + read_op_base* o(static_cast<read_op_base*>(base)); - // Copy buffers into array. - descriptor_ops::buf bufs[max_buffers]; - typename MutableBufferSequence::const_iterator iter = buffers_.begin(); - typename MutableBufferSequence::const_iterator end = buffers_.end(); - size_t i = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::mutable_buffer buffer(*iter); - descriptor_ops::init_buf(bufs[i], - boost::asio::buffer_cast<void*>(buffer), - boost::asio::buffer_size(buffer)); - } + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(o->buffers_); for (;;) { // Read some data. - int bytes = descriptor_ops::scatter_read(descriptor_, bufs, i, ec); + boost::system::error_code ec; + int bytes = descriptor_ops::scatter_read( + o->descriptor_, bufs.buffers(), bufs.count(), ec); if (bytes == 0) ec = boost::asio::error::eof; @@ -623,73 +526,75 @@ public: || ec == boost::asio::error::try_again) return false; - bytes_transferred = (bytes < 0 ? 0 : bytes); + o->ec_ = ec; + o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); return true; } } - void complete(const boost::system::error_code& ec, - std::size_t bytes_transferred) - { - io_service_.post(bind_handler(this->handler_, ec, bytes_transferred)); - } - private: int descriptor_; - boost::asio::io_service& io_service_; - boost::asio::io_service::work work_; MutableBufferSequence buffers_; }; - // Start an asynchronous read. The buffer for the data being read must be - // valid for the lifetime of the asynchronous operation. template <typename MutableBufferSequence, typename Handler> - void async_read_some(implementation_type& impl, - const MutableBufferSequence& buffers, Handler handler) + class read_op : public read_op_base<MutableBufferSequence> { - if (!is_open(impl)) + public: + read_op(int descriptor, + const MutableBufferSequence& buffers, Handler handler) + : read_op_base<MutableBufferSequence>( + descriptor, buffers, &read_op::do_complete), + handler_(handler) { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); } - else + + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) { - // Determine total size of buffers. - typename MutableBufferSequence::const_iterator iter = buffers.begin(); - typename MutableBufferSequence::const_iterator end = buffers.end(); - size_t i = 0; - size_t total_buffer_size = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::mutable_buffer buffer(*iter); - total_buffer_size += boost::asio::buffer_size(buffer); - } + // Take ownership of the handler object. + read_op* o(static_cast<read_op*>(base)); + typedef handler_alloc_traits<Handler, read_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); - // A request to read_some 0 bytes on a stream is a no-op. - if (total_buffer_size == 0) + // Make the upcall if required. + if (owner) { - this->get_io_service().post(bind_handler(handler, - boost::system::error_code(), 0)); - return; + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); } + } - // Make descriptor non-blocking. - if (!(impl.flags_ & implementation_type::internal_non_blocking)) - { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec)) - { - this->get_io_service().post(bind_handler(handler, ec, 0)); - return; - } - impl.flags_ |= implementation_type::internal_non_blocking; - } + private: + Handler handler_; + }; - reactor_.start_read_op(impl.descriptor_, impl.reactor_data_, - read_operation<MutableBufferSequence, Handler>( - impl.descriptor_, this->get_io_service(), buffers, handler)); - } + // Start an asynchronous read. The buffer for the data being read must be + // valid for the lifetime of the asynchronous operation. + template <typename MutableBufferSequence, typename Handler> + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef read_op<MutableBufferSequence, Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, + impl.descriptor_, buffers, handler); + + start_op(impl, reactor::read_op, ptr.get(), true, + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence>::all_empty(buffers)); + ptr.release(); } // Wait until data can be read without blocking. @@ -697,22 +602,61 @@ public: void async_read_some(implementation_type& impl, const null_buffers&, Handler handler) { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); - } - else + // Allocate and construct an operation to wrap the handler. + typedef null_buffers_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); + + start_op(impl, reactor::read_op, ptr.get(), false, false); + ptr.release(); + } + +private: + // Start the asynchronous operation. + void start_op(implementation_type& impl, int op_type, + reactor_op* op, bool non_blocking, bool noop) + { + if (!noop) { - reactor_.start_read_op(impl.descriptor_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler), - false); + if (is_open(impl)) + { + if (is_non_blocking(impl) || set_non_blocking(impl, op->ec_)) + { + reactor_.start_op(op_type, impl.descriptor_, + impl.reactor_data_, op, non_blocking); + return; + } + } + else + op->ec_ = boost::asio::error::bad_descriptor; } + + io_service_impl_.post_immediate_completion(op); } -private: + // Determine whether the descriptor has been set non-blocking. + bool is_non_blocking(implementation_type& impl) const + { + return (impl.flags_ & implementation_type::non_blocking); + } + + // Set the internal non-blocking flag. + bool set_non_blocking(implementation_type& impl, + boost::system::error_code& ec) + { + ioctl_arg_type non_blocking = 1; + if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec)) + return false; + impl.flags_ |= implementation_type::internal_non_blocking; + return true; + } + + // The io_service implementation used to post completions. + io_service_impl& io_service_impl_; + // The selector that performs event demultiplexing for the service. - Reactor& reactor_; + reactor& reactor_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/reactive_serial_port_service.hpp b/3rdParty/Boost/src/boost/asio/detail/reactive_serial_port_service.hpp index cd163e9..faa2149 100644 --- a/3rdParty/Boost/src/boost/asio/detail/reactive_serial_port_service.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/reactive_serial_port_service.hpp @@ -38,31 +38,24 @@ namespace asio { namespace detail { // Extend reactive_descriptor_service to provide serial port support. -template <typename Reactor> class reactive_serial_port_service - : public boost::asio::detail::service_base< - reactive_serial_port_service<Reactor> > { public: - // The native type of a stream handle. - typedef typename reactive_descriptor_service<Reactor>::native_type - native_type; + // The native type of a serial port. + typedef reactive_descriptor_service::native_type native_type; - // The implementation type of the stream handle. - typedef typename reactive_descriptor_service<Reactor>::implementation_type - implementation_type; + // The implementation type of the serial port. + typedef reactive_descriptor_service::implementation_type implementation_type; reactive_serial_port_service(boost::asio::io_service& io_service) - : boost::asio::detail::service_base< - reactive_serial_port_service>(io_service), - descriptor_service_(boost::asio::use_service< - reactive_descriptor_service<Reactor> >(io_service)) + : descriptor_service_(io_service) { } // Destroy all user-defined handler objects owned by the service. void shutdown_service() { + descriptor_service_.shutdown_service(); } // Construct a new handle implementation. @@ -254,8 +247,8 @@ public: } private: - // The handle service used for initiating asynchronous operations. - reactive_descriptor_service<Reactor>& descriptor_service_; + // The implementation used for initiating asynchronous operations. + reactive_descriptor_service descriptor_service_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/reactive_socket_service.hpp b/3rdParty/Boost/src/boost/asio/detail/reactive_socket_service.hpp index 95d39dd..5f7bbf5 100644 --- a/3rdParty/Boost/src/boost/asio/detail/reactive_socket_service.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/reactive_socket_service.hpp @@ -17,18 +17,17 @@ #include <boost/asio/detail/push_options.hpp> -#include <boost/asio/detail/push_options.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/asio/detail/pop_options.hpp> - #include <boost/asio/buffer.hpp> #include <boost/asio/error.hpp> #include <boost/asio/io_service.hpp> #include <boost/asio/socket_base.hpp> #include <boost/asio/detail/bind_handler.hpp> -#include <boost/asio/detail/handler_base_from_member.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> +#include <boost/asio/detail/fenced_block.hpp> #include <boost/asio/detail/noncopyable.hpp> -#include <boost/asio/detail/service_base.hpp> +#include <boost/asio/detail/null_buffers_op.hpp> +#include <boost/asio/detail/reactor.hpp> +#include <boost/asio/detail/reactor_op.hpp> #include <boost/asio/detail/socket_holder.hpp> #include <boost/asio/detail/socket_ops.hpp> #include <boost/asio/detail/socket_types.hpp> @@ -37,10 +36,8 @@ namespace boost { namespace asio { namespace detail { -template <typename Protocol, typename Reactor> +template <typename Protocol> class reactive_socket_service - : public boost::asio::detail::service_base< - reactive_socket_service<Protocol, Reactor> > { public: // The protocol type. @@ -67,7 +64,7 @@ public: private: // Only this service will have access to the internal values. - friend class reactive_socket_service<Protocol, Reactor>; + friend class reactive_socket_service<Protocol>; // The native socket representation. socket_type socket_; @@ -87,7 +84,7 @@ public: // User wants connection_aborted errors, which are disabled by default. enable_connection_aborted = 4, - // The user set the linger option. Needs to be checked when closing. + // The user set the linger option. Needs to be checked when closing. user_set_linger = 8 }; @@ -98,17 +95,13 @@ public: protocol_type protocol_; // Per-descriptor data used by the reactor. - typename Reactor::per_descriptor_data reactor_data_; + reactor::per_descriptor_data reactor_data_; }; - // The maximum number of buffers to support in a single operation. - enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; - // Constructor. reactive_socket_service(boost::asio::io_service& io_service) - : boost::asio::detail::service_base< - reactive_socket_service<Protocol, Reactor> >(io_service), - reactor_(boost::asio::use_service<Reactor>(io_service)) + : io_service_impl_(use_service<io_service_impl>(io_service)), + reactor_(use_service<reactor>(io_service)) { reactor_.init_task(); } @@ -452,43 +445,30 @@ public: return ec; } - if (command.name() == static_cast<int>(FIONBIO)) + socket_ops::ioctl(impl.socket_, command.name(), + static_cast<ioctl_arg_type*>(command.data()), ec); + + // When updating the non-blocking mode we always perform the ioctl + // syscall, even if the flags would otherwise indicate that the socket is + // already in the correct state. This ensures that the underlying socket + // is put into the state that has been requested by the user. If the ioctl + // syscall was successful then we need to update the flags to match. + if (!ec && command.name() == static_cast<int>(FIONBIO)) { - // Flags are manipulated in a temporary variable so that the socket - // implementation is not updated unless the ioctl operation succeeds. - unsigned char new_flags = impl.flags_; if (*static_cast<ioctl_arg_type*>(command.data())) - new_flags |= implementation_type::user_set_non_blocking; - else - new_flags &= ~implementation_type::user_set_non_blocking; - - // Perform ioctl on socket if the non-blocking state has changed. - if (!(impl.flags_ & implementation_type::non_blocking) - && (new_flags & implementation_type::non_blocking)) { - ioctl_arg_type non_blocking = 1; - socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec); - } - else if ((impl.flags_ & implementation_type::non_blocking) - && !(new_flags & implementation_type::non_blocking)) - { - ioctl_arg_type non_blocking = 0; - socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec); + impl.flags_ |= implementation_type::user_set_non_blocking; } else { - ec = boost::system::error_code(); + // Clearing the non-blocking mode always overrides any internally-set + // non-blocking flag. Any subsequent asynchronous operations will need + // to re-enable non-blocking I/O. + impl.flags_ &= ~(implementation_type::user_set_non_blocking + | implementation_type::internal_non_blocking); } - - // Update socket implementation's flags only if successful. - if (!ec) - impl.flags_ = new_flags; - } - else - { - socket_ops::ioctl(impl.socket_, command.name(), - static_cast<ioctl_arg_type*>(command.data()), ec); } + return ec; } @@ -553,23 +533,11 @@ public: return 0; } - // Copy buffers into array. - socket_ops::buf bufs[max_buffers]; - typename ConstBufferSequence::const_iterator iter = buffers.begin(); - typename ConstBufferSequence::const_iterator end = buffers.end(); - size_t i = 0; - size_t total_buffer_size = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::const_buffer buffer(*iter); - socket_ops::init_buf(bufs[i], - boost::asio::buffer_cast<const void*>(buffer), - boost::asio::buffer_size(buffer)); - total_buffer_size += boost::asio::buffer_size(buffer); - } + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(buffers); // A request to receive 0 bytes on a stream socket is a no-op. - if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + if (impl.protocol_.type() == SOCK_STREAM && bufs.all_empty()) { ec = boost::system::error_code(); return 0; @@ -579,7 +547,8 @@ public: for (;;) { // Try to complete the operation without blocking. - int bytes_sent = socket_ops::send(impl.socket_, bufs, i, flags, ec); + int bytes_sent = socket_ops::send(impl.socket_, + bufs.buffers(), bufs.count(), flags, ec); // Check if operation succeeded. if (bytes_sent >= 0) @@ -613,50 +582,32 @@ public: return 0; } - template <typename ConstBufferSequence, typename Handler> - class send_operation : - public handler_base_from_member<Handler> + template <typename ConstBufferSequence> + class send_op_base : public reactor_op { public: - send_operation(socket_type socket, boost::asio::io_service& io_service, - const ConstBufferSequence& buffers, socket_base::message_flags flags, - Handler handler) - : handler_base_from_member<Handler>(handler), + send_op_base(socket_type socket, const ConstBufferSequence& buffers, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(&send_op_base::do_perform, complete_func), socket_(socket), - io_service_(io_service), - work_(io_service), buffers_(buffers), flags_(flags) { } - bool perform(boost::system::error_code& ec, - std::size_t& bytes_transferred) + static bool do_perform(reactor_op* base) { - // Check whether the operation was successful. - if (ec) - { - bytes_transferred = 0; - return true; - } + send_op_base* o(static_cast<send_op_base*>(base)); - // Copy buffers into array. - socket_ops::buf bufs[max_buffers]; - typename ConstBufferSequence::const_iterator iter = buffers_.begin(); - typename ConstBufferSequence::const_iterator end = buffers_.end(); - size_t i = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::const_buffer buffer(*iter); - socket_ops::init_buf(bufs[i], - boost::asio::buffer_cast<const void*>(buffer), - boost::asio::buffer_size(buffer)); - } + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(o->buffers_); for (;;) { // Send the data. - int bytes = socket_ops::send(socket_, bufs, i, flags_, ec); + boost::system::error_code ec; + int bytes = socket_ops::send(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, ec); // Retry operation if interrupted by signal. if (ec == boost::asio::error::interrupted) @@ -667,127 +618,92 @@ public: || ec == boost::asio::error::try_again) return false; - bytes_transferred = (bytes < 0 ? 0 : bytes); + o->ec_ = ec; + o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); return true; } } - void complete(const boost::system::error_code& ec, - std::size_t bytes_transferred) - { - io_service_.post(bind_handler(this->handler_, ec, bytes_transferred)); - } - private: socket_type socket_; - boost::asio::io_service& io_service_; - boost::asio::io_service::work work_; ConstBufferSequence buffers_; socket_base::message_flags flags_; }; - // Start an asynchronous send. The data being sent must be valid for the - // lifetime of the asynchronous operation. template <typename ConstBufferSequence, typename Handler> - void async_send(implementation_type& impl, const ConstBufferSequence& buffers, - socket_base::message_flags flags, Handler handler) - { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); - } - else - { - if (impl.protocol_.type() == SOCK_STREAM) - { - // Determine total size of buffers. - typename ConstBufferSequence::const_iterator iter = buffers.begin(); - typename ConstBufferSequence::const_iterator end = buffers.end(); - size_t i = 0; - size_t total_buffer_size = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::const_buffer buffer(*iter); - total_buffer_size += boost::asio::buffer_size(buffer); - } - - // A request to receive 0 bytes on a stream socket is a no-op. - if (total_buffer_size == 0) - { - this->get_io_service().post(bind_handler(handler, - boost::system::error_code(), 0)); - return; - } - } - - // Make socket non-blocking. - if (!(impl.flags_ & implementation_type::internal_non_blocking)) - { - if (!(impl.flags_ & implementation_type::non_blocking)) - { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) - { - this->get_io_service().post(bind_handler(handler, ec, 0)); - return; - } - } - impl.flags_ |= implementation_type::internal_non_blocking; - } - - reactor_.start_write_op(impl.socket_, impl.reactor_data_, - send_operation<ConstBufferSequence, Handler>( - impl.socket_, this->get_io_service(), buffers, flags, handler)); - } - } - - template <typename Handler> - class null_buffers_operation : - public handler_base_from_member<Handler> + class send_op : public send_op_base<ConstBufferSequence> { public: - null_buffers_operation(boost::asio::io_service& io_service, Handler handler) - : handler_base_from_member<Handler>(handler), - work_(io_service) + send_op(socket_type socket, const ConstBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + : send_op_base<ConstBufferSequence>(socket, + buffers, flags, &send_op::do_complete), + handler_(handler) { } - bool perform(boost::system::error_code&, - std::size_t& bytes_transferred) + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) { - bytes_transferred = 0; - return true; - } + // Take ownership of the handler object. + send_op* o(static_cast<send_op*>(base)); + typedef handler_alloc_traits<Handler, send_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); - void complete(const boost::system::error_code& ec, - std::size_t bytes_transferred) - { - work_.get_io_service().post(bind_handler( - this->handler_, ec, bytes_transferred)); + // Make the upcall if required. + if (owner) + { + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } private: - boost::asio::io_service::work work_; + Handler handler_; }; + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template <typename ConstBufferSequence, typename Handler> + void async_send(implementation_type& impl, const ConstBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef send_op<ConstBufferSequence, Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, + impl.socket_, buffers, flags, handler); + + start_op(impl, reactor::write_op, ptr.get(), true, + (impl.protocol_.type() == SOCK_STREAM + && buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence>::all_empty(buffers))); + ptr.release(); + } + // Start an asynchronous wait until data can be sent without blocking. template <typename Handler> void async_send(implementation_type& impl, const null_buffers&, socket_base::message_flags, Handler handler) { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); - } - else - { - reactor_.start_write_op(impl.socket_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler), - false); - } + // Allocate and construct an operation to wrap the handler. + typedef null_buffers_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); + + start_op(impl, reactor::write_op, ptr.get(), false, false); + ptr.release(); } // Send a datagram to the specified endpoint. Returns the number of bytes @@ -803,25 +719,15 @@ public: return 0; } - // Copy buffers into array. - socket_ops::buf bufs[max_buffers]; - typename ConstBufferSequence::const_iterator iter = buffers.begin(); - typename ConstBufferSequence::const_iterator end = buffers.end(); - size_t i = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::const_buffer buffer(*iter); - socket_ops::init_buf(bufs[i], - boost::asio::buffer_cast<const void*>(buffer), - boost::asio::buffer_size(buffer)); - } + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(buffers); // Send the data. for (;;) { // Try to complete the operation without blocking. - int bytes_sent = socket_ops::sendto(impl.socket_, bufs, i, flags, - destination.data(), destination.size(), ec); + int bytes_sent = socket_ops::sendto(impl.socket_, bufs.buffers(), + bufs.count(), flags, destination.data(), destination.size(), ec); // Check if operation succeeded. if (bytes_sent >= 0) @@ -856,52 +762,34 @@ public: return 0; } - template <typename ConstBufferSequence, typename Handler> - class send_to_operation : - public handler_base_from_member<Handler> + template <typename ConstBufferSequence> + class send_to_op_base : public reactor_op { public: - send_to_operation(socket_type socket, boost::asio::io_service& io_service, - const ConstBufferSequence& buffers, const endpoint_type& endpoint, - socket_base::message_flags flags, Handler handler) - : handler_base_from_member<Handler>(handler), + send_to_op_base(socket_type socket, const ConstBufferSequence& buffers, + const endpoint_type& endpoint, socket_base::message_flags flags, + func_type complete_func) + : reactor_op(&send_to_op_base::do_perform, complete_func), socket_(socket), - io_service_(io_service), - work_(io_service), buffers_(buffers), destination_(endpoint), flags_(flags) { } - bool perform(boost::system::error_code& ec, - std::size_t& bytes_transferred) + static bool do_perform(reactor_op* base) { - // Check whether the operation was successful. - if (ec) - { - bytes_transferred = 0; - return true; - } + send_to_op_base* o(static_cast<send_to_op_base*>(base)); - // Copy buffers into array. - socket_ops::buf bufs[max_buffers]; - typename ConstBufferSequence::const_iterator iter = buffers_.begin(); - typename ConstBufferSequence::const_iterator end = buffers_.end(); - size_t i = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::const_buffer buffer(*iter); - socket_ops::init_buf(bufs[i], - boost::asio::buffer_cast<const void*>(buffer), - boost::asio::buffer_size(buffer)); - } + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(o->buffers_); for (;;) { // Send the data. - int bytes = socket_ops::sendto(socket_, bufs, i, flags_, - destination_.data(), destination_.size(), ec); + boost::system::error_code ec; + int bytes = socket_ops::sendto(o->socket_, bufs.buffers(), bufs.count(), + o->flags_, o->destination_.data(), o->destination_.size(), ec); // Retry operation if interrupted by signal. if (ec == boost::asio::error::interrupted) @@ -912,62 +800,78 @@ public: || ec == boost::asio::error::try_again) return false; - bytes_transferred = (bytes < 0 ? 0 : bytes); + o->ec_ = ec; + o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); return true; } } - void complete(const boost::system::error_code& ec, - std::size_t bytes_transferred) - { - io_service_.post(bind_handler(this->handler_, ec, bytes_transferred)); - } - private: socket_type socket_; - boost::asio::io_service& io_service_; - boost::asio::io_service::work work_; ConstBufferSequence buffers_; endpoint_type destination_; socket_base::message_flags flags_; }; - // Start an asynchronous send. The data being sent must be valid for the - // lifetime of the asynchronous operation. template <typename ConstBufferSequence, typename Handler> - void async_send_to(implementation_type& impl, - const ConstBufferSequence& buffers, - const endpoint_type& destination, socket_base::message_flags flags, - Handler handler) + class send_to_op : public send_to_op_base<ConstBufferSequence> { - if (!is_open(impl)) + public: + send_to_op(socket_type socket, const ConstBufferSequence& buffers, + const endpoint_type& endpoint, socket_base::message_flags flags, + Handler handler) + : send_to_op_base<ConstBufferSequence>(socket, + buffers, endpoint, flags, &send_to_op::do_complete), + handler_(handler) { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); } - else + + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) { - // Make socket non-blocking. - if (!(impl.flags_ & implementation_type::internal_non_blocking)) + // Take ownership of the handler object. + send_to_op* o(static_cast<send_to_op*>(base)); + typedef handler_alloc_traits<Handler, send_to_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); + + // Make the upcall if required. + if (owner) { - if (!(impl.flags_ & implementation_type::non_blocking)) - { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) - { - this->get_io_service().post(bind_handler(handler, ec, 0)); - return; - } - } - impl.flags_ |= implementation_type::internal_non_blocking; + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); } - - reactor_.start_write_op(impl.socket_, impl.reactor_data_, - send_to_operation<ConstBufferSequence, Handler>( - impl.socket_, this->get_io_service(), buffers, - destination, flags, handler)); } + + private: + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template <typename ConstBufferSequence, typename Handler> + void async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef send_to_op<ConstBufferSequence, Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, impl.socket_, + buffers, destination, flags, handler); + + start_op(impl, reactor::write_op, ptr.get(), true, false); + ptr.release(); } // Start an asynchronous wait until data can be sent without blocking. @@ -975,17 +879,14 @@ public: void async_send_to(implementation_type& impl, const null_buffers&, socket_base::message_flags, const endpoint_type&, Handler handler) { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); - } - else - { - reactor_.start_write_op(impl.socket_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler), - false); - } + // Allocate and construct an operation to wrap the handler. + typedef null_buffers_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); + + start_op(impl, reactor::write_op, ptr.get(), false, false); + ptr.release(); } // Receive some data from the peer. Returns the number of bytes received. @@ -1000,23 +901,11 @@ public: return 0; } - // Copy buffers into array. - socket_ops::buf bufs[max_buffers]; - typename MutableBufferSequence::const_iterator iter = buffers.begin(); - typename MutableBufferSequence::const_iterator end = buffers.end(); - size_t i = 0; - size_t total_buffer_size = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::mutable_buffer buffer(*iter); - socket_ops::init_buf(bufs[i], - boost::asio::buffer_cast<void*>(buffer), - boost::asio::buffer_size(buffer)); - total_buffer_size += boost::asio::buffer_size(buffer); - } + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); // A request to receive 0 bytes on a stream socket is a no-op. - if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + if (impl.protocol_.type() == SOCK_STREAM && bufs.all_empty()) { ec = boost::system::error_code(); return 0; @@ -1026,7 +915,8 @@ public: for (;;) { // Try to complete the operation without blocking. - int bytes_recvd = socket_ops::recv(impl.socket_, bufs, i, flags, ec); + int bytes_recvd = socket_ops::recv(impl.socket_, + bufs.buffers(), bufs.count(), flags, ec); // Check if operation succeeded. if (bytes_recvd > 0) @@ -1067,53 +957,35 @@ public: return 0; } - template <typename MutableBufferSequence, typename Handler> - class receive_operation : - public handler_base_from_member<Handler> + template <typename MutableBufferSequence> + class receive_op_base : public reactor_op { public: - receive_operation(socket_type socket, int protocol_type, - boost::asio::io_service& io_service, + receive_op_base(socket_type socket, int protocol_type, const MutableBufferSequence& buffers, - socket_base::message_flags flags, Handler handler) - : handler_base_from_member<Handler>(handler), + socket_base::message_flags flags, func_type complete_func) + : reactor_op(&receive_op_base::do_perform, complete_func), socket_(socket), protocol_type_(protocol_type), - io_service_(io_service), - work_(io_service), buffers_(buffers), flags_(flags) { } - bool perform(boost::system::error_code& ec, - std::size_t& bytes_transferred) + static bool do_perform(reactor_op* base) { - // Check whether the operation was successful. - if (ec) - { - bytes_transferred = 0; - return true; - } + receive_op_base* o(static_cast<receive_op_base*>(base)); - // Copy buffers into array. - socket_ops::buf bufs[max_buffers]; - typename MutableBufferSequence::const_iterator iter = buffers_.begin(); - typename MutableBufferSequence::const_iterator end = buffers_.end(); - size_t i = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::mutable_buffer buffer(*iter); - socket_ops::init_buf(bufs[i], - boost::asio::buffer_cast<void*>(buffer), - boost::asio::buffer_size(buffer)); - } + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(o->buffers_); for (;;) { // Receive some data. - int bytes = socket_ops::recv(socket_, bufs, i, flags_, ec); - if (bytes == 0 && protocol_type_ == SOCK_STREAM) + boost::system::error_code ec; + int bytes = socket_ops::recv(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, ec); + if (bytes == 0 && o->protocol_type_ == SOCK_STREAM) ec = boost::asio::error::eof; // Retry operation if interrupted by signal. @@ -1125,93 +997,84 @@ public: || ec == boost::asio::error::try_again) return false; - bytes_transferred = (bytes < 0 ? 0 : bytes); + o->ec_ = ec; + o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); return true; } } - void complete(const boost::system::error_code& ec, - std::size_t bytes_transferred) - { - io_service_.post(bind_handler(this->handler_, ec, bytes_transferred)); - } - private: socket_type socket_; int protocol_type_; - boost::asio::io_service& io_service_; - boost::asio::io_service::work work_; MutableBufferSequence buffers_; socket_base::message_flags flags_; }; - // Start an asynchronous receive. The buffer for the data being received - // must be valid for the lifetime of the asynchronous operation. template <typename MutableBufferSequence, typename Handler> - void async_receive(implementation_type& impl, - const MutableBufferSequence& buffers, - socket_base::message_flags flags, Handler handler) + class receive_op : public receive_op_base<MutableBufferSequence> { - if (!is_open(impl)) + public: + receive_op(socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + : receive_op_base<MutableBufferSequence>(socket, + protocol_type, buffers, flags, &receive_op::do_complete), + handler_(handler) { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); } - else - { - if (impl.protocol_.type() == SOCK_STREAM) - { - // Determine total size of buffers. - typename MutableBufferSequence::const_iterator iter = buffers.begin(); - typename MutableBufferSequence::const_iterator end = buffers.end(); - size_t i = 0; - size_t total_buffer_size = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::mutable_buffer buffer(*iter); - total_buffer_size += boost::asio::buffer_size(buffer); - } - - // A request to receive 0 bytes on a stream socket is a no-op. - if (total_buffer_size == 0) - { - this->get_io_service().post(bind_handler(handler, - boost::system::error_code(), 0)); - return; - } - } - // Make socket non-blocking. - if (!(impl.flags_ & implementation_type::internal_non_blocking)) - { - if (!(impl.flags_ & implementation_type::non_blocking)) - { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) - { - this->get_io_service().post(bind_handler(handler, ec, 0)); - return; - } - } - impl.flags_ |= implementation_type::internal_non_blocking; - } + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + receive_op* o(static_cast<receive_op*>(base)); + typedef handler_alloc_traits<Handler, receive_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); - if (flags & socket_base::message_out_of_band) + // Make the upcall if required. + if (owner) { - reactor_.start_except_op(impl.socket_, impl.reactor_data_, - receive_operation<MutableBufferSequence, Handler>( - impl.socket_, impl.protocol_.type(), - this->get_io_service(), buffers, flags, handler)); - } - else - { - reactor_.start_read_op(impl.socket_, impl.reactor_data_, - receive_operation<MutableBufferSequence, Handler>( - impl.socket_, impl.protocol_.type(), - this->get_io_service(), buffers, flags, handler)); + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); } } + + private: + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template <typename MutableBufferSequence, typename Handler> + void async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef receive_op<MutableBufferSequence, Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + int protocol_type = impl.protocol_.type(); + handler_ptr<alloc_traits> ptr(raw_ptr, impl.socket_, + protocol_type, buffers, flags, handler); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + ptr.get(), (flags & socket_base::message_out_of_band) == 0, + (impl.protocol_.type() == SOCK_STREAM + && buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence>::all_empty(buffers))); + ptr.release(); } // Wait until data can be received without blocking. @@ -1219,22 +1082,17 @@ public: void async_receive(implementation_type& impl, const null_buffers&, socket_base::message_flags flags, Handler handler) { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); - } - else if (flags & socket_base::message_out_of_band) - { - reactor_.start_except_op(impl.socket_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler)); - } - else - { - reactor_.start_read_op(impl.socket_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler), - false); - } + // Allocate and construct an operation to wrap the handler. + typedef null_buffers_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + ptr.get(), false, false); + ptr.release(); } // Receive a datagram with the endpoint of the sender. Returns the number of @@ -1251,26 +1109,16 @@ public: return 0; } - // Copy buffers into array. - socket_ops::buf bufs[max_buffers]; - typename MutableBufferSequence::const_iterator iter = buffers.begin(); - typename MutableBufferSequence::const_iterator end = buffers.end(); - size_t i = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::mutable_buffer buffer(*iter); - socket_ops::init_buf(bufs[i], - boost::asio::buffer_cast<void*>(buffer), - boost::asio::buffer_size(buffer)); - } + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); // Receive some data. for (;;) { // Try to complete the operation without blocking. std::size_t addr_len = sender_endpoint.capacity(); - int bytes_recvd = socket_ops::recvfrom(impl.socket_, bufs, i, flags, - sender_endpoint.data(), &addr_len, ec); + int bytes_recvd = socket_ops::recvfrom(impl.socket_, bufs.buffers(), + bufs.count(), flags, sender_endpoint.data(), &addr_len, ec); // Check if operation succeeded. if (bytes_recvd > 0) @@ -1318,56 +1166,37 @@ public: return 0; } - template <typename MutableBufferSequence, typename Handler> - class receive_from_operation : - public handler_base_from_member<Handler> + template <typename MutableBufferSequence> + class receive_from_op_base : public reactor_op { public: - receive_from_operation(socket_type socket, int protocol_type, - boost::asio::io_service& io_service, + receive_from_op_base(socket_type socket, int protocol_type, const MutableBufferSequence& buffers, endpoint_type& endpoint, - socket_base::message_flags flags, Handler handler) - : handler_base_from_member<Handler>(handler), + socket_base::message_flags flags, func_type complete_func) + : reactor_op(&receive_from_op_base::do_perform, complete_func), socket_(socket), protocol_type_(protocol_type), - io_service_(io_service), - work_(io_service), buffers_(buffers), sender_endpoint_(endpoint), flags_(flags) { } - bool perform(boost::system::error_code& ec, - std::size_t& bytes_transferred) + static bool do_perform(reactor_op* base) { - // Check whether the operation was successful. - if (ec) - { - bytes_transferred = 0; - return true; - } + receive_from_op_base* o(static_cast<receive_from_op_base*>(base)); - // Copy buffers into array. - socket_ops::buf bufs[max_buffers]; - typename MutableBufferSequence::const_iterator iter = buffers_.begin(); - typename MutableBufferSequence::const_iterator end = buffers_.end(); - size_t i = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::mutable_buffer buffer(*iter); - socket_ops::init_buf(bufs[i], - boost::asio::buffer_cast<void*>(buffer), - boost::asio::buffer_size(buffer)); - } + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(o->buffers_); for (;;) { // Receive some data. - std::size_t addr_len = sender_endpoint_.capacity(); - int bytes = socket_ops::recvfrom(socket_, bufs, i, flags_, - sender_endpoint_.data(), &addr_len, ec); - if (bytes == 0 && protocol_type_ == SOCK_STREAM) + boost::system::error_code ec; + std::size_t addr_len = o->sender_endpoint_.capacity(); + int bytes = socket_ops::recvfrom(o->socket_, bufs.buffers(), + bufs.count(), o->flags_, o->sender_endpoint_.data(), &addr_len, ec); + if (bytes == 0 && o->protocol_type_ == SOCK_STREAM) ec = boost::asio::error::eof; // Retry operation if interrupted by signal. @@ -1379,64 +1208,84 @@ public: || ec == boost::asio::error::try_again) return false; - sender_endpoint_.resize(addr_len); - bytes_transferred = (bytes < 0 ? 0 : bytes); + o->sender_endpoint_.resize(addr_len); + o->ec_ = ec; + o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); return true; } } - void complete(const boost::system::error_code& ec, - std::size_t bytes_transferred) - { - io_service_.post(bind_handler(this->handler_, ec, bytes_transferred)); - } - private: socket_type socket_; int protocol_type_; - boost::asio::io_service& io_service_; - boost::asio::io_service::work work_; MutableBufferSequence buffers_; endpoint_type& sender_endpoint_; socket_base::message_flags flags_; }; - // Start an asynchronous receive. The buffer for the data being received and - // the sender_endpoint object must both be valid for the lifetime of the - // asynchronous operation. template <typename MutableBufferSequence, typename Handler> - void async_receive_from(implementation_type& impl, - const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, - socket_base::message_flags flags, Handler handler) + class receive_from_op : public receive_from_op_base<MutableBufferSequence> { - if (!is_open(impl)) + public: + receive_from_op(socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, endpoint_type& endpoint, + socket_base::message_flags flags, Handler handler) + : receive_from_op_base<MutableBufferSequence>(socket, protocol_type, + buffers, endpoint, flags, &receive_from_op::do_complete), + handler_(handler) { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); } - else + + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) { - // Make socket non-blocking. - if (!(impl.flags_ & implementation_type::internal_non_blocking)) + // Take ownership of the handler object. + receive_from_op* o(static_cast<receive_from_op*>(base)); + typedef handler_alloc_traits<Handler, receive_from_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); + + // Make the upcall if required. + if (owner) { - if (!(impl.flags_ & implementation_type::non_blocking)) - { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) - { - this->get_io_service().post(bind_handler(handler, ec, 0)); - return; - } - } - impl.flags_ |= implementation_type::internal_non_blocking; + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); } - - reactor_.start_read_op(impl.socket_, impl.reactor_data_, - receive_from_operation<MutableBufferSequence, Handler>( - impl.socket_, impl.protocol_.type(), this->get_io_service(), - buffers, sender_endpoint, flags, handler)); } + + private: + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template <typename MutableBufferSequence, typename Handler> + void async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef receive_from_op<MutableBufferSequence, Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + int protocol_type = impl.protocol_.type(); + handler_ptr<alloc_traits> ptr(raw_ptr, impl.socket_, + protocol_type, buffers, sender_endpoint, flags, handler); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + ptr.get(), true, false); + ptr.release(); } // Wait until data can be received without blocking. @@ -1445,28 +1294,20 @@ public: const null_buffers&, endpoint_type& sender_endpoint, socket_base::message_flags flags, Handler handler) { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); - } - else - { - // Reset endpoint since it can be given no sensible value at this time. - sender_endpoint = endpoint_type(); + // Allocate and construct an operation to wrap the handler. + typedef null_buffers_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); - if (flags & socket_base::message_out_of_band) - { - reactor_.start_except_op(impl.socket_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler)); - } - else - { - reactor_.start_read_op(impl.socket_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler), - false); - } - } + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + ptr.get(), false, false); + ptr.release(); } // Accept a new connection. @@ -1546,19 +1387,15 @@ public: } } - template <typename Socket, typename Handler> - class accept_operation : - public handler_base_from_member<Handler> + template <typename Socket> + class accept_op_base : public reactor_op { public: - accept_operation(socket_type socket, boost::asio::io_service& io_service, - Socket& peer, const protocol_type& protocol, - endpoint_type* peer_endpoint, bool enable_connection_aborted, - Handler handler) - : handler_base_from_member<Handler>(handler), + accept_op_base(socket_type socket, Socket& peer, + const protocol_type& protocol, endpoint_type* peer_endpoint, + bool enable_connection_aborted, func_type complete_func) + : reactor_op(&accept_op_base::do_perform, complete_func), socket_(socket), - io_service_(io_service), - work_(io_service), peer_(peer), protocol_(protocol), peer_endpoint_(peer_endpoint), @@ -1566,27 +1403,25 @@ public: { } - bool perform(boost::system::error_code& ec, std::size_t&) + static bool do_perform(reactor_op* base) { - // Check whether the operation was successful. - if (ec) - return true; + accept_op_base* o(static_cast<accept_op_base*>(base)); for (;;) { // Accept the waiting connection. + boost::system::error_code ec; socket_holder new_socket; std::size_t addr_len = 0; - if (peer_endpoint_) - { - addr_len = peer_endpoint_->capacity(); - new_socket.reset(socket_ops::accept(socket_, - peer_endpoint_->data(), &addr_len, ec)); - } - else + std::size_t* addr_len_p = 0; + socket_addr_type* addr = 0; + if (o->peer_endpoint_) { - new_socket.reset(socket_ops::accept(socket_, 0, 0, ec)); + addr_len = o->peer_endpoint_->capacity(); + addr_len_p = &addr_len; + addr = o->peer_endpoint_->data(); } + new_socket.reset(socket_ops::accept(o->socket_, addr, addr_len_p, ec)); // Retry operation if interrupted by signal. if (ec == boost::asio::error::interrupted) @@ -1597,83 +1432,95 @@ public: || ec == boost::asio::error::try_again) return false; if (ec == boost::asio::error::connection_aborted - && !enable_connection_aborted_) + && !o->enable_connection_aborted_) return false; #if defined(EPROTO) - if (ec.value() == EPROTO && !enable_connection_aborted_) + if (ec.value() == EPROTO && !o->enable_connection_aborted_) return false; #endif // defined(EPROTO) // Transfer ownership of the new socket to the peer object. if (!ec) { - if (peer_endpoint_) - peer_endpoint_->resize(addr_len); - peer_.assign(protocol_, new_socket.get(), ec); + if (o->peer_endpoint_) + o->peer_endpoint_->resize(addr_len); + o->peer_.assign(o->protocol_, new_socket.get(), ec); if (!ec) new_socket.release(); } + o->ec_ = ec; return true; } } - void complete(const boost::system::error_code& ec, std::size_t) - { - io_service_.post(bind_handler(this->handler_, ec)); - } - private: socket_type socket_; - boost::asio::io_service& io_service_; - boost::asio::io_service::work work_; Socket& peer_; protocol_type protocol_; endpoint_type* peer_endpoint_; bool enable_connection_aborted_; }; - // Start an asynchronous accept. The peer and peer_endpoint objects - // must be valid until the accept's handler is invoked. template <typename Socket, typename Handler> - void async_accept(implementation_type& impl, Socket& peer, - endpoint_type* peer_endpoint, Handler handler) + class accept_op : public accept_op_base<Socket> { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor)); - } - else if (peer.is_open()) + public: + accept_op(socket_type socket, Socket& peer, const protocol_type& protocol, + endpoint_type* peer_endpoint, bool enable_connection_aborted, + Handler handler) + : accept_op_base<Socket>(socket, peer, protocol, peer_endpoint, + enable_connection_aborted, &accept_op::do_complete), + handler_(handler) { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::already_open)); } - else + + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) { - // Make socket non-blocking. - if (!(impl.flags_ & implementation_type::internal_non_blocking)) + // Take ownership of the handler object. + accept_op* o(static_cast<accept_op*>(base)); + typedef handler_alloc_traits<Handler, accept_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); + + // Make the upcall if required. + if (owner) { - if (!(impl.flags_ & implementation_type::non_blocking)) - { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) - { - this->get_io_service().post(bind_handler(handler, ec)); - return; - } - } - impl.flags_ |= implementation_type::internal_non_blocking; + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder1<Handler, boost::system::error_code> + handler(o->handler_, o->ec_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); } - - reactor_.start_read_op(impl.socket_, impl.reactor_data_, - accept_operation<Socket, Handler>( - impl.socket_, this->get_io_service(), - peer, impl.protocol_, peer_endpoint, - (impl.flags_ & implementation_type::enable_connection_aborted) != 0, - handler)); } + + private: + Handler handler_; + }; + + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template <typename Socket, typename Handler> + void async_accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef accept_op<Socket, Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + bool enable_connection_aborted = + (impl.flags_ & implementation_type::enable_connection_aborted) != 0; + handler_ptr<alloc_traits> ptr(raw_ptr, impl.socket_, peer, + impl.protocol_, peer_endpoint, enable_connection_aborted, handler); + + start_accept_op(impl, ptr.get(), peer.is_open()); + ptr.release(); } // Connect the socket to the specified endpoint. @@ -1713,53 +1560,77 @@ public: return ec; } - template <typename Handler> - class connect_operation : - public handler_base_from_member<Handler> + class connect_op_base : public reactor_op { public: - connect_operation(socket_type socket, - boost::asio::io_service& io_service, Handler handler) - : handler_base_from_member<Handler>(handler), - socket_(socket), - io_service_(io_service), - work_(io_service) + connect_op_base(socket_type socket, func_type complete_func) + : reactor_op(&connect_op_base::do_perform, complete_func), + socket_(socket) { } - bool perform(boost::system::error_code& ec, std::size_t&) + static bool do_perform(reactor_op* base) { - // Check whether the operation was successful. - if (ec) - return true; + connect_op_base* o(static_cast<connect_op_base*>(base)); // Get the error code from the connect operation. int connect_error = 0; size_t connect_error_len = sizeof(connect_error); - if (socket_ops::getsockopt(socket_, SOL_SOCKET, SO_ERROR, - &connect_error, &connect_error_len, ec) == socket_error_retval) + if (socket_ops::getsockopt(o->socket_, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len, o->ec_) == socket_error_retval) return true; // The connection failed so the handler will be posted with an error code. if (connect_error) { - ec = boost::system::error_code(connect_error, + o->ec_ = boost::system::error_code(connect_error, boost::asio::error::get_system_category()); - return true; } return true; } - void complete(const boost::system::error_code& ec, std::size_t) + private: + socket_type socket_; + }; + + template <typename Handler> + class connect_op : public connect_op_base + { + public: + connect_op(socket_type socket, Handler handler) + : connect_op_base(socket, &connect_op::do_complete), + handler_(handler) { - io_service_.post(bind_handler(this->handler_, ec)); + } + + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + connect_op* o(static_cast<connect_op*>(base)); + typedef handler_alloc_traits<Handler, connect_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); + + // Make the upcall if required. + if (owner) + { + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder1<Handler, boost::system::error_code> + handler(o->handler_, o->ec_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } private: - socket_type socket_; - boost::asio::io_service& io_service_; - boost::asio::io_service::work work_; + Handler handler_; }; // Start an asynchronous connect. @@ -1767,59 +1638,103 @@ public: void async_connect(implementation_type& impl, const endpoint_type& peer_endpoint, Handler handler) { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor)); - return; - } + // Allocate and construct an operation to wrap the handler. + typedef connect_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, impl.socket_, handler); + + start_connect_op(impl, ptr.get(), peer_endpoint); + ptr.release(); + } - // Make socket non-blocking. - if (!(impl.flags_ & implementation_type::internal_non_blocking)) +private: + // Start the asynchronous read or write operation. + void start_op(implementation_type& impl, int op_type, + reactor_op* op, bool non_blocking, bool noop) + { + if (!noop) { - if (!(impl.flags_ & implementation_type::non_blocking)) + if (is_open(impl)) { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + if (!non_blocking || is_non_blocking(impl) + || set_non_blocking(impl, op->ec_)) { - this->get_io_service().post(bind_handler(handler, ec)); + reactor_.start_op(op_type, impl.socket_, + impl.reactor_data_, op, non_blocking); return; } } - impl.flags_ |= implementation_type::internal_non_blocking; + else + op->ec_ = boost::asio::error::bad_descriptor; } - // Start the connect operation. The socket is already marked as non-blocking - // so the connection will take place asynchronously. - boost::system::error_code ec; - if (socket_ops::connect(impl.socket_, peer_endpoint.data(), - peer_endpoint.size(), ec) == 0) + io_service_impl_.post_immediate_completion(op); + } + + // Start the asynchronous accept operation. + void start_accept_op(implementation_type& impl, + reactor_op* op, bool peer_is_open) + { + if (!peer_is_open) + start_op(impl, reactor::read_op, op, true, false); + else { - // The connect operation has finished successfully so we need to post the - // handler immediately. - this->get_io_service().post(bind_handler(handler, - boost::system::error_code())); + op->ec_ = boost::asio::error::already_open; + io_service_impl_.post_immediate_completion(op); } - else if (ec == boost::asio::error::in_progress - || ec == boost::asio::error::would_block) + } + + // Start the asynchronous connect operation. + void start_connect_op(implementation_type& impl, + reactor_op* op, const endpoint_type& peer_endpoint) + { + if (is_open(impl)) { - // The connection is happening in the background, and we need to wait - // until the socket becomes writeable. - reactor_.start_connect_op(impl.socket_, impl.reactor_data_, - connect_operation<Handler>(impl.socket_, - this->get_io_service(), handler)); + if (is_non_blocking(impl) || set_non_blocking(impl, op->ec_)) + { + if (socket_ops::connect(impl.socket_, peer_endpoint.data(), + peer_endpoint.size(), op->ec_) != 0) + { + if (op->ec_ == boost::asio::error::in_progress + || op->ec_ == boost::asio::error::would_block) + { + op->ec_ = boost::system::error_code(); + reactor_.start_op(reactor::connect_op, + impl.socket_, impl.reactor_data_, op, false); + return; + } + } + } } else - { - // The connect operation has failed, so post the handler immediately. - this->get_io_service().post(bind_handler(handler, ec)); - } + op->ec_ = boost::asio::error::bad_descriptor; + + io_service_impl_.post_immediate_completion(op); } -private: + // Determine whether the socket has been set non-blocking. + bool is_non_blocking(implementation_type& impl) const + { + return (impl.flags_ & implementation_type::non_blocking); + } + + // Set the internal non-blocking flag. + bool set_non_blocking(implementation_type& impl, + boost::system::error_code& ec) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + return false; + impl.flags_ |= implementation_type::internal_non_blocking; + return true; + } + + // The io_service implementation used to post completions. + io_service_impl& io_service_impl_; + // The selector that performs event demultiplexing for the service. - Reactor& reactor_; + reactor& reactor_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/reactor.hpp b/3rdParty/Boost/src/boost/asio/detail/reactor.hpp new file mode 100644 index 0000000..43e432b --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/reactor.hpp @@ -0,0 +1,34 @@ +// +// reactor.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_REACTOR_HPP +#define BOOST_ASIO_DETAIL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/reactor_fwd.hpp> + +#if defined(BOOST_ASIO_HAS_EPOLL) +# include <boost/asio/detail/epoll_reactor.hpp> +#elif defined(BOOST_ASIO_HAS_KQUEUE) +# include <boost/asio/detail/kqueue_reactor.hpp> +#elif defined(BOOST_ASIO_HAS_DEV_POLL) +# include <boost/asio/detail/dev_poll_reactor.hpp> +#else +# include <boost/asio/detail/select_reactor.hpp> +#endif + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_REACTOR_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/reactor_fwd.hpp b/3rdParty/Boost/src/boost/asio/detail/reactor_fwd.hpp new file mode 100644 index 0000000..9ff05eb --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/reactor_fwd.hpp @@ -0,0 +1,48 @@ +// +// reactor_fwd.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_REACTOR_FWD_HPP +#define BOOST_ASIO_DETAIL_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/dev_poll_reactor_fwd.hpp> +#include <boost/asio/detail/epoll_reactor_fwd.hpp> +#include <boost/asio/detail/kqueue_reactor_fwd.hpp> +#include <boost/asio/detail/select_reactor_fwd.hpp> +#include <boost/asio/detail/win_iocp_io_service_fwd.hpp> + +namespace boost { +namespace asio { +namespace detail { + +#if defined(BOOST_ASIO_HAS_IOCP) +typedef select_reactor<true> reactor; +#elif defined(BOOST_ASIO_HAS_EPOLL) +typedef epoll_reactor reactor; +#elif defined(BOOST_ASIO_HAS_KQUEUE) +typedef kqueue_reactor reactor; +#elif defined(BOOST_ASIO_HAS_DEV_POLL) +typedef dev_poll_reactor reactor; +#else +typedef select_reactor<false> reactor; +#endif + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_REACTOR_FWD_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/reactor_op.hpp b/3rdParty/Boost/src/boost/asio/detail/reactor_op.hpp new file mode 100644 index 0000000..40647ec --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/reactor_op.hpp @@ -0,0 +1,62 @@ +// +// reactor_op.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_REACTOR_OP_HPP +#define BOOST_ASIO_DETAIL_REACTOR_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/operation.hpp> + +namespace boost { +namespace asio { +namespace detail { + +class reactor_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + boost::system::error_code ec_; + + // The number of bytes transferred, to be passed to the completion handler. + std::size_t bytes_transferred_; + + // Perform the operation. Returns true if it is finished. + bool perform() + { + return perform_func_(this); + } + +protected: + typedef bool (*perform_func_type)(reactor_op*); + + reactor_op(perform_func_type perform_func, func_type complete_func) + : operation(complete_func), + bytes_transferred_(0), + perform_func_(perform_func) + { + } + +private: + perform_func_type perform_func_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_REACTOR_OP_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/reactor_op_queue.hpp b/3rdParty/Boost/src/boost/asio/detail/reactor_op_queue.hpp index 46f0c10..0dd5654 100644 --- a/3rdParty/Boost/src/boost/asio/detail/reactor_op_queue.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/reactor_op_queue.hpp @@ -17,14 +17,11 @@ #include <boost/asio/detail/push_options.hpp> -#include <boost/asio/detail/push_options.hpp> -#include <memory> -#include <boost/asio/detail/pop_options.hpp> - #include <boost/asio/error.hpp> -#include <boost/asio/detail/handler_alloc_helpers.hpp> #include <boost/asio/detail/hash_map.hpp> #include <boost/asio/detail/noncopyable.hpp> +#include <boost/asio/detail/op_queue.hpp> +#include <boost/asio/detail/reactor_op.hpp> namespace boost { namespace asio { @@ -37,39 +34,21 @@ class reactor_op_queue public: // Constructor. reactor_op_queue() - : operations_(), - cancelled_operations_(0), - complete_operations_(0) + : operations_() { } // Add a new operation to the queue. Returns true if this is the only // operation for the given descriptor, in which case the reactor's event // demultiplexing function call may need to be interrupted and restarted. - template <typename Operation> - bool enqueue_operation(Descriptor descriptor, Operation operation) + bool enqueue_operation(Descriptor descriptor, reactor_op* op) { - // Allocate and construct an object to wrap the handler. - typedef handler_alloc_traits<Operation, op<Operation> > alloc_traits; - raw_handler_ptr<alloc_traits> raw_ptr(operation); - handler_ptr<alloc_traits> ptr(raw_ptr, descriptor, operation); - - typedef typename operation_map::iterator iterator; - typedef typename operation_map::value_type value_type; + typedef typename operations_map::iterator iterator; + typedef typename operations_map::value_type value_type; std::pair<iterator, bool> entry = - operations_.insert(value_type(descriptor, ptr.get())); - if (entry.second) - { - ptr.release(); - return true; - } - - op_base* current_op = entry.first->second; - while (current_op->next_) - current_op = current_op->next_; - current_op->next_ = ptr.release(); - - return false; + operations_.insert(value_type(descriptor, operations())); + entry.first->second.op_queue_.push(op); + return entry.second; } // Cancel all operations associated with the descriptor. Any operations @@ -77,16 +56,19 @@ public: // next time perform_cancellations is called. Returns true if any operations // were cancelled, in which case the reactor's event demultiplexing function // may need to be interrupted and restarted. - bool cancel_operations(Descriptor descriptor) + bool cancel_operations(Descriptor descriptor, op_queue<operation>& ops, + const boost::system::error_code& ec = + boost::asio::error::operation_aborted) { - typename operation_map::iterator i = operations_.find(descriptor); + typename operations_map::iterator i = operations_.find(descriptor); if (i != operations_.end()) { - op_base* last_op = i->second; - while (last_op->next_) - last_op = last_op->next_; - last_op->next_ = cancelled_operations_; - cancelled_operations_ = i->second; + while (reactor_op* op = i->second.op_queue_.front()) + { + op->ec_ = ec; + i->second.op_queue_.pop(); + ops.push(op); + } operations_.erase(i); return true; } @@ -106,79 +88,37 @@ public: return operations_.find(descriptor) != operations_.end(); } - // Perform the first operation corresponding to the descriptor. Returns true - // if there are more operations queued for the descriptor. - bool perform_operation(Descriptor descriptor, - const boost::system::error_code& result) + // Perform the operations corresponding to the descriptor. Returns true if + // there are still unfinished operations queued for the descriptor. + bool perform_operations(Descriptor descriptor, op_queue<operation>& ops) { - typename operation_map::iterator i = operations_.find(descriptor); + typename operations_map::iterator i = operations_.find(descriptor); if (i != operations_.end()) { - op_base* this_op = i->second; - i->second = this_op->next_; - this_op->next_ = complete_operations_; - complete_operations_ = this_op; - bool done = this_op->perform(result); - if (done) + while (reactor_op* op = i->second.op_queue_.front()) { - // Operation has finished. - if (i->second) + if (op->perform()) { - return true; + i->second.op_queue_.pop(); + ops.push(op); } else { - operations_.erase(i); - return false; - } - } - else - { - // Operation wants to be called again. Leave it at the front of the - // queue for this descriptor, and remove from the completed list. - complete_operations_ = this_op->next_; - this_op->next_ = i->second; - i->second = this_op; - return true; - } - } - return false; - } - - // Perform all operations corresponding to the descriptor. - void perform_all_operations(Descriptor descriptor, - const boost::system::error_code& result) - { - typename operation_map::iterator i = operations_.find(descriptor); - if (i != operations_.end()) - { - while (i->second) - { - op_base* this_op = i->second; - i->second = this_op->next_; - this_op->next_ = complete_operations_; - complete_operations_ = this_op; - bool done = this_op->perform(result); - if (!done) - { - // Operation has not finished yet, so leave at front of queue, and - // remove from the completed list. - complete_operations_ = this_op->next_; - this_op->next_ = i->second; - i->second = this_op; - return; + return true; } } operations_.erase(i); } + return false; } // Fill a descriptor set with the descriptors corresponding to each active - // operation. + // operation. The op_queue is used only when descriptors fail to be added to + // the descriptor set. template <typename Descriptor_Set> - void get_descriptors(Descriptor_Set& descriptors) + void get_descriptors(Descriptor_Set& descriptors, op_queue<operation>& ops) { - typename operation_map::iterator i = operations_.begin(); + typename operations_map::iterator i = operations_.begin(); while (i != operations_.end()) { Descriptor descriptor = i->first; @@ -186,7 +126,7 @@ public: if (!descriptors.set(descriptor)) { boost::system::error_code ec(error::fd_set_failure); - perform_all_operations(descriptor, ec); + cancel_operations(descriptor, ops, ec); } } } @@ -194,257 +134,62 @@ public: // Perform the operations corresponding to the ready file descriptors // contained in the given descriptor set. template <typename Descriptor_Set> - void perform_operations_for_descriptors(const Descriptor_Set& descriptors, - const boost::system::error_code& result) + void perform_operations_for_descriptors( + const Descriptor_Set& descriptors, op_queue<operation>& ops) { - typename operation_map::iterator i = operations_.begin(); + typename operations_map::iterator i = operations_.begin(); while (i != operations_.end()) { - typename operation_map::iterator op_iter = i++; + typename operations_map::iterator op_iter = i++; if (descriptors.is_set(op_iter->first)) { - op_base* this_op = op_iter->second; - op_iter->second = this_op->next_; - this_op->next_ = complete_operations_; - complete_operations_ = this_op; - bool done = this_op->perform(result); - if (done) - { - if (!op_iter->second) - operations_.erase(op_iter); - } - else + while (reactor_op* op = op_iter->second.op_queue_.front()) { - // Operation has not finished yet, so leave at front of queue, and - // remove from the completed list. - complete_operations_ = this_op->next_; - this_op->next_ = op_iter->second; - op_iter->second = this_op; + if (op->perform()) + { + op_iter->second.op_queue_.pop(); + ops.push(op); + } + else + { + break; + } } - } - } - } - - // Perform any pending cancels for operations. - void perform_cancellations() - { - while (cancelled_operations_) - { - op_base* this_op = cancelled_operations_; - cancelled_operations_ = this_op->next_; - this_op->next_ = complete_operations_; - complete_operations_ = this_op; - this_op->perform(boost::asio::error::operation_aborted); - } - } - // Complete all operations that are waiting to be completed. - void complete_operations() - { - while (complete_operations_) - { - op_base* next_op = complete_operations_->next_; - complete_operations_->next_ = 0; - complete_operations_->complete(); - complete_operations_ = next_op; + if (op_iter->second.op_queue_.empty()) + operations_.erase(op_iter); + } } } - // Destroy all operations owned by the queue. - void destroy_operations() + // Get all operations owned by the queue. + void get_all_operations(op_queue<operation>& ops) { - while (cancelled_operations_) - { - op_base* next_op = cancelled_operations_->next_; - cancelled_operations_->next_ = 0; - cancelled_operations_->destroy(); - cancelled_operations_ = next_op; - } - - while (complete_operations_) - { - op_base* next_op = complete_operations_->next_; - complete_operations_->next_ = 0; - complete_operations_->destroy(); - complete_operations_ = next_op; - } - - typename operation_map::iterator i = operations_.begin(); + typename operations_map::iterator i = operations_.begin(); while (i != operations_.end()) { - typename operation_map::iterator op_iter = i++; - op_base* curr_op = op_iter->second; + typename operations_map::iterator op_iter = i++; + ops.push(op_iter->second.op_queue_); operations_.erase(op_iter); - while (curr_op) - { - op_base* next_op = curr_op->next_; - curr_op->next_ = 0; - curr_op->destroy(); - curr_op = next_op; - } } } private: - // Base class for reactor operations. A function pointer is used instead of - // virtual functions to avoid the associated overhead. - class op_base - { - public: - // Get the descriptor associated with the operation. - Descriptor descriptor() const - { - return descriptor_; - } - - // Perform the operation. - bool perform(const boost::system::error_code& result) - { - result_ = result; - return perform_func_(this, result_, bytes_transferred_); - } - - // Destroy the operation and post the handler. - void complete() - { - complete_func_(this, result_, bytes_transferred_); - } - - // Destroy the operation. - void destroy() - { - destroy_func_(this); - } - - protected: - typedef bool (*perform_func_type)(op_base*, - boost::system::error_code&, std::size_t&); - typedef void (*complete_func_type)(op_base*, - const boost::system::error_code&, std::size_t); - typedef void (*destroy_func_type)(op_base*); - - // Construct an operation for the given descriptor. - op_base(perform_func_type perform_func, complete_func_type complete_func, - destroy_func_type destroy_func, Descriptor descriptor) - : perform_func_(perform_func), - complete_func_(complete_func), - destroy_func_(destroy_func), - descriptor_(descriptor), - result_(), - bytes_transferred_(0), - next_(0) - { - } - - // Prevent deletion through this type. - ~op_base() - { - } - - private: - friend class reactor_op_queue<Descriptor>; - - // The function to be called to perform the operation. - perform_func_type perform_func_; - - // The function to be called to delete the operation and post the handler. - complete_func_type complete_func_; - - // The function to be called to delete the operation. - destroy_func_type destroy_func_; - - // The descriptor associated with the operation. - Descriptor descriptor_; - - // The result of the operation. - boost::system::error_code result_; - - // The number of bytes transferred in the operation. - std::size_t bytes_transferred_; - - // The next operation for the same file descriptor. - op_base* next_; - }; - - // Adaptor class template for operations. - template <typename Operation> - class op - : public op_base + struct operations { - public: - // Constructor. - op(Descriptor descriptor, Operation operation) - : op_base(&op<Operation>::do_perform, &op<Operation>::do_complete, - &op<Operation>::do_destroy, descriptor), - operation_(operation) - { - } - - // Perform the operation. - static bool do_perform(op_base* base, - boost::system::error_code& result, std::size_t& bytes_transferred) - { - return static_cast<op<Operation>*>(base)->operation_.perform( - result, bytes_transferred); - } - - // Destroy the operation and post the handler. - static void do_complete(op_base* base, - const boost::system::error_code& result, std::size_t bytes_transferred) - { - // Take ownership of the operation object. - typedef op<Operation> this_type; - this_type* this_op(static_cast<this_type*>(base)); - typedef handler_alloc_traits<Operation, this_type> alloc_traits; - handler_ptr<alloc_traits> ptr(this_op->operation_, this_op); - - // Make a copy of the error_code and the operation so that the memory can - // be deallocated before the upcall is made. - boost::system::error_code ec(result); - Operation operation(this_op->operation_); - - // Free the memory associated with the operation. - ptr.reset(); - - // Make the upcall. - operation.complete(ec, bytes_transferred); - } - - // Destroy the operation. - static void do_destroy(op_base* base) - { - // Take ownership of the operation object. - typedef op<Operation> this_type; - this_type* this_op(static_cast<this_type*>(base)); - typedef handler_alloc_traits<Operation, this_type> alloc_traits; - handler_ptr<alloc_traits> ptr(this_op->operation_, this_op); - - // A sub-object of the operation may be the true owner of the memory - // associated with the operation. Consequently, a local copy of the - // operation is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - Operation operation(this_op->operation_); - (void)operation; - - // Free the memory associated with the operation. - ptr.reset(); - } + operations() {} + operations(const operations&) {} + void operator=(const operations&) {} - private: - Operation operation_; + // The operations waiting on the desccriptor. + op_queue<reactor_op> op_queue_; }; // The type for a map of operations. - typedef hash_map<Descriptor, op_base*> operation_map; + typedef hash_map<Descriptor, operations> operations_map; // The operations that are currently executing asynchronously. - operation_map operations_; - - // The list of operations that have been cancelled. - op_base* cancelled_operations_; - - // The list of operations waiting to be completed. - op_base* complete_operations_; + operations_map operations_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/resolver_service.hpp b/3rdParty/Boost/src/boost/asio/detail/resolver_service.hpp index 3270cd6..f9b7a98 100644 --- a/3rdParty/Boost/src/boost/asio/detail/resolver_service.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/resolver_service.hpp @@ -26,9 +26,13 @@ #include <boost/asio/error.hpp> #include <boost/asio/io_service.hpp> +#include <boost/asio/ip/basic_resolver_iterator.hpp> +#include <boost/asio/ip/basic_resolver_query.hpp> #include <boost/asio/detail/bind_handler.hpp> +#include <boost/asio/detail/fenced_block.hpp> #include <boost/asio/detail/mutex.hpp> #include <boost/asio/detail/noncopyable.hpp> +#include <boost/asio/detail/operation.hpp> #include <boost/asio/detail/service_base.hpp> #include <boost/asio/detail/socket_ops.hpp> #include <boost/asio/detail/socket_types.hpp> @@ -79,17 +83,20 @@ public: typedef typename Protocol::endpoint endpoint_type; // The query type. - typedef typename Protocol::resolver_query query_type; + typedef boost::asio::ip::basic_resolver_query<Protocol> query_type; // The iterator type. - typedef typename Protocol::resolver_iterator iterator_type; + typedef boost::asio::ip::basic_resolver_iterator<Protocol> iterator_type; // Constructor. resolver_service(boost::asio::io_service& io_service) : boost::asio::detail::service_base< resolver_service<Protocol> >(io_service), mutex_(), + io_service_impl_(boost::asio::use_service<io_service_impl>(io_service)), work_io_service_(new boost::asio::io_service), + work_io_service_impl_(boost::asio::use_service< + io_service_impl>(*work_io_service_)), work_(new boost::asio::io_service::work(*work_io_service_)), work_thread_(0) { @@ -143,7 +150,7 @@ public: std::string service_name = query.service_name(); boost::asio::detail::addrinfo_type hints = query.hints(); - socket_ops::getaddrinfo(host_name.length() ? host_name.c_str() : 0, + socket_ops::getaddrinfo(!host_name.empty() ? host_name.c_str() : 0, service_name.c_str(), &hints, &address_info, ec); auto_addrinfo auto_address_info(address_info); @@ -154,54 +161,84 @@ public: } template <typename Handler> - class resolve_query_handler + class resolve_op + : public operation { public: - resolve_query_handler(implementation_type impl, const query_type& query, - boost::asio::io_service& io_service, Handler handler) - : impl_(impl), + resolve_op(implementation_type impl, const query_type& query, + io_service_impl& io_service_impl, Handler handler) + : operation(&resolve_op::do_complete), + impl_(impl), query_(query), - io_service_(io_service), - work_(io_service), + io_service_impl_(io_service_impl), handler_(handler) { } - void operator()() + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) { - // Check if the operation has been cancelled. - if (impl_.expired()) + // Take ownership of the operation object. + resolve_op* o(static_cast<resolve_op*>(base)); + typedef handler_alloc_traits<Handler, resolve_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); + + if (owner) { - iterator_type iterator; - io_service_.post(boost::asio::detail::bind_handler(handler_, - boost::asio::error::operation_aborted, iterator)); - return; + if (owner != &o->io_service_impl_) + { + // The operation is being run on the worker io_service. Time to + // perform the resolver operation. + + if (o->impl_.expired()) + { + // THe operation has been cancelled. + o->ec_ = boost::asio::error::operation_aborted; + } + else + { + // Perform the blocking host resolution operation. + boost::asio::detail::addrinfo_type* address_info = 0; + std::string host_name = o->query_.host_name(); + std::string service_name = o->query_.service_name(); + boost::asio::detail::addrinfo_type hints = o->query_.hints(); + socket_ops::getaddrinfo(!host_name.empty() ? host_name.c_str() : 0, + service_name.c_str(), &hints, &address_info, o->ec_); + auto_addrinfo auto_address_info(address_info); + o->iter_ = iterator_type::create( + address_info, host_name, service_name); + } + + o->io_service_impl_.post_deferred_completion(o); + ptr.release(); + } + else + { + // The operation has been returned to the main io_serice. The + // completion handler is ready to be delivered. + + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object + // remains valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, iterator_type> + handler(o->handler_, o->ec_, o->iter_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } - - // Perform the blocking host resolution operation. - boost::asio::detail::addrinfo_type* address_info = 0; - std::string host_name = query_.host_name(); - std::string service_name = query_.service_name(); - boost::asio::detail::addrinfo_type hints = query_.hints(); - boost::system::error_code ec; - socket_ops::getaddrinfo(host_name.length() ? host_name.c_str() : 0, - service_name.c_str(), &hints, &address_info, ec); - auto_addrinfo auto_address_info(address_info); - - // Invoke the handler and pass the result. - iterator_type iterator; - if (!ec) - iterator = iterator_type::create(address_info, host_name, service_name); - io_service_.post(boost::asio::detail::bind_handler( - handler_, ec, iterator)); } private: boost::weak_ptr<void> impl_; query_type query_; - boost::asio::io_service& io_service_; - boost::asio::io_service::work work_; + io_service_impl& io_service_impl_; Handler handler_; + boost::system::error_code ec_; + iterator_type iter_; }; // Asynchronously resolve a query to a list of entries. @@ -209,12 +246,19 @@ public: void async_resolve(implementation_type& impl, const query_type& query, Handler handler) { + // Allocate and construct an operation to wrap the handler. + typedef resolve_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, + impl, query, io_service_impl_, handler); + if (work_io_service_) { start_work_thread(); - work_io_service_->post( - resolve_query_handler<Handler>( - impl, query, this->get_io_service(), handler)); + io_service_impl_.work_started(); + work_io_service_impl_.post_immediate_completion(ptr.get()); + ptr.release(); } } @@ -243,61 +287,89 @@ public: } template <typename Handler> - class resolve_endpoint_handler + class resolve_endpoint_op + : public operation { public: - resolve_endpoint_handler(implementation_type impl, - const endpoint_type& endpoint, boost::asio::io_service& io_service, - Handler handler) - : impl_(impl), - endpoint_(endpoint), - io_service_(io_service), - work_(io_service), + resolve_endpoint_op(implementation_type impl, const endpoint_type& ep, + io_service_impl& io_service_impl, Handler handler) + : operation(&resolve_endpoint_op::do_complete), + impl_(impl), + ep_(ep), + io_service_impl_(io_service_impl), handler_(handler) { } - void operator()() + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) { - // Check if the operation has been cancelled. - if (impl_.expired()) - { - iterator_type iterator; - io_service_.post(boost::asio::detail::bind_handler(handler_, - boost::asio::error::operation_aborted, iterator)); - return; - } - + // Take ownership of the operation object. + resolve_endpoint_op* o(static_cast<resolve_endpoint_op*>(base)); + typedef handler_alloc_traits<Handler, resolve_endpoint_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); - // First try resolving with the service name. If that fails try resolving - // but allow the service to be returned as a number. - char host_name[NI_MAXHOST]; - char service_name[NI_MAXSERV]; - int flags = endpoint_.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0; - boost::system::error_code ec; - socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(), - host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); - if (ec) + if (owner) { - flags |= NI_NUMERICSERV; - socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(), - host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); + if (owner != &o->io_service_impl_) + { + // The operation is being run on the worker io_service. Time to + // perform the resolver operation. + + if (o->impl_.expired()) + { + // THe operation has been cancelled. + o->ec_ = boost::asio::error::operation_aborted; + } + else + { + // Perform the blocking endoint resolution operation. + char host_name[NI_MAXHOST]; + char service_name[NI_MAXSERV]; + int flags = o->ep_.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0; + socket_ops::getnameinfo(o->ep_.data(), o->ep_.size(), + host_name, NI_MAXHOST, service_name, + NI_MAXSERV, flags, o->ec_); + if (o->ec_) + { + flags |= NI_NUMERICSERV; + socket_ops::getnameinfo(o->ep_.data(), o->ep_.size(), + host_name, NI_MAXHOST, service_name, + NI_MAXSERV, flags, o->ec_); + } + o->iter_ = iterator_type::create(o->ep_, host_name, service_name); + } + + o->io_service_impl_.post_deferred_completion(o); + ptr.release(); + } + else + { + // The operation has been returned to the main io_serice. The + // completion handler is ready to be delivered. + + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object + // remains valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, iterator_type> + handler(o->handler_, o->ec_, o->iter_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } - - // Invoke the handler and pass the result. - iterator_type iterator; - if (!ec) - iterator = iterator_type::create(endpoint_, host_name, service_name); - io_service_.post(boost::asio::detail::bind_handler( - handler_, ec, iterator)); } private: boost::weak_ptr<void> impl_; - endpoint_type endpoint_; - boost::asio::io_service& io_service_; - boost::asio::io_service::work work_; + endpoint_type ep_; + io_service_impl& io_service_impl_; Handler handler_; + boost::system::error_code ec_; + iterator_type iter_; }; // Asynchronously resolve an endpoint to a list of entries. @@ -305,12 +377,19 @@ public: void async_resolve(implementation_type& impl, const endpoint_type& endpoint, Handler handler) { + // Allocate and construct an operation to wrap the handler. + typedef resolve_endpoint_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, + impl, endpoint, io_service_impl_, handler); + if (work_io_service_) { start_work_thread(); - work_io_service_->post( - resolve_endpoint_handler<Handler>( - impl, endpoint, this->get_io_service(), handler)); + io_service_impl_.work_started(); + work_io_service_impl_.post_immediate_completion(ptr.get()); + ptr.release(); } } @@ -340,9 +419,15 @@ private: // Mutex to protect access to internal data. boost::asio::detail::mutex mutex_; + // The io_service implementation used to post completions. + io_service_impl& io_service_impl_; + // Private io_service used for performing asynchronous host resolution. boost::scoped_ptr<boost::asio::io_service> work_io_service_; + // The work io_service implementation used to post completions. + io_service_impl& work_io_service_impl_; + // Work for the private io_service to perform. boost::scoped_ptr<boost::asio::io_service::work> work_; diff --git a/3rdParty/Boost/src/boost/asio/detail/select_reactor.hpp b/3rdParty/Boost/src/boost/asio/detail/select_reactor.hpp index ba8b5d3..91030e9 100644 --- a/3rdParty/Boost/src/boost/asio/detail/select_reactor.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/select_reactor.hpp @@ -22,9 +22,6 @@ #include <boost/asio/detail/push_options.hpp> #include <cstddef> #include <boost/config.hpp> -#include <boost/date_time/posix_time/posix_time_types.hpp> -#include <boost/shared_ptr.hpp> -#include <vector> #include <boost/asio/detail/pop_options.hpp> #include <boost/asio/io_service.hpp> @@ -32,6 +29,8 @@ #include <boost/asio/detail/fd_set_adapter.hpp> #include <boost/asio/detail/mutex.hpp> #include <boost/asio/detail/noncopyable.hpp> +#include <boost/asio/detail/op_queue.hpp> +#include <boost/asio/detail/reactor_op.hpp> #include <boost/asio/detail/reactor_op_queue.hpp> #include <boost/asio/detail/select_interrupter.hpp> #include <boost/asio/detail/select_reactor_fwd.hpp> @@ -39,9 +38,11 @@ #include <boost/asio/detail/signal_blocker.hpp> #include <boost/asio/detail/socket_ops.hpp> #include <boost/asio/detail/socket_types.hpp> -#include <boost/asio/detail/task_io_service.hpp> #include <boost/asio/detail/thread.hpp> -#include <boost/asio/detail/timer_queue.hpp> +#include <boost/asio/detail/timer_op.hpp> +#include <boost/asio/detail/timer_queue_base.hpp> +#include <boost/asio/detail/timer_queue_fwd.hpp> +#include <boost/asio/detail/timer_queue_set.hpp> namespace boost { namespace asio { @@ -52,6 +53,14 @@ class select_reactor : public boost::asio::detail::service_base<select_reactor<Own_Thread> > { public: +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + enum { read_op = 0, write_op = 1, except_op = 2, + max_select_ops = 3, connect_op = 3, max_ops = 4 }; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + enum { read_op = 0, write_op = 1, except_op = 2, + max_select_ops = 3, connect_op = 1, max_ops = 3 }; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Per-descriptor data. struct per_descriptor_data { @@ -61,13 +70,9 @@ public: select_reactor(boost::asio::io_service& io_service) : boost::asio::detail::service_base< select_reactor<Own_Thread> >(io_service), + io_service_(use_service<io_service_impl>(io_service)), mutex_(), - select_in_progress_(false), interrupter_(), - read_op_queue_(), - write_op_queue_(), - except_op_queue_(), - pending_cancellations_(), stop_thread_(false), thread_(0), shutdown_(false) @@ -94,31 +99,29 @@ public: stop_thread_ = true; lock.unlock(); - if (thread_) + if (Own_Thread) { - interrupter_.interrupt(); - thread_->join(); - delete thread_; - thread_ = 0; + if (thread_) + { + interrupter_.interrupt(); + thread_->join(); + delete thread_; + thread_ = 0; + } } - read_op_queue_.destroy_operations(); - write_op_queue_.destroy_operations(); - except_op_queue_.destroy_operations(); + op_queue<operation> ops; - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - timer_queues_[i]->destroy_timers(); - timer_queues_.clear(); + for (int i = 0; i < max_ops; ++i) + op_queue_[i].get_all_operations(ops); + + timer_queues_.get_all_timers(ops); } // Initialise the task, but only if the reactor is not in its own thread. void init_task() { - if (!Own_Thread) - { - typedef task_io_service<select_reactor<Own_Thread> > task_io_service_type; - use_service<task_io_service_type>(this->get_io_service()).init_task(); - } + io_service_.init_task(); } // Register a socket with the reactor. Returns 0 on success, system error @@ -128,111 +131,17 @@ public: return 0; } - // Start a new read operation. The handler object will be invoked when the - // given descriptor is ready to be read, or an error has occurred. - template <typename Handler> - void start_read_op(socket_type descriptor, per_descriptor_data&, - Handler handler, bool /*allow_speculative_read*/ = true) - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - if (!shutdown_) - if (read_op_queue_.enqueue_operation(descriptor, handler)) - interrupter_.interrupt(); - } - - // Start a new write operation. The handler object will be invoked when the - // given descriptor is ready to be written, or an error has occurred. - template <typename Handler> - void start_write_op(socket_type descriptor, per_descriptor_data&, - Handler handler, bool /*allow_speculative_write*/ = true) + // Start a new operation. The reactor operation will be performed when the + // given descriptor is flagged as ready, or an error has occurred. + void start_op(int op_type, socket_type descriptor, + per_descriptor_data&, reactor_op* op, bool) { boost::asio::detail::mutex::scoped_lock lock(mutex_); if (!shutdown_) - if (write_op_queue_.enqueue_operation(descriptor, handler)) - interrupter_.interrupt(); - } - - // Start a new exception operation. The handler object will be invoked when - // the given descriptor has exception information, or an error has occurred. - template <typename Handler> - void start_except_op(socket_type descriptor, - per_descriptor_data&, Handler handler) - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - if (!shutdown_) - if (except_op_queue_.enqueue_operation(descriptor, handler)) - interrupter_.interrupt(); - } - - // Wrapper for connect handlers to enable the handler object to be placed - // in both the write and the except operation queues, but ensure that only - // one of the handlers is called. - template <typename Handler> - class connect_handler_wrapper - { - public: - connect_handler_wrapper(socket_type descriptor, - boost::shared_ptr<bool> completed, - select_reactor<Own_Thread>& reactor, Handler handler) - : descriptor_(descriptor), - completed_(completed), - reactor_(reactor), - handler_(handler) { - } - - bool perform(boost::system::error_code& ec, - std::size_t& bytes_transferred) - { - // Check whether one of the handlers has already been called. If it has, - // then we don't want to do anything in this handler. - if (*completed_) - { - completed_.reset(); // Indicate that this handler should not complete. - return true; - } - - // Cancel the other reactor operation for the connection. - *completed_ = true; - reactor_.enqueue_cancel_ops_unlocked(descriptor_); - - // Call the contained handler. - return handler_.perform(ec, bytes_transferred); - } - - void complete(const boost::system::error_code& ec, - std::size_t bytes_transferred) - { - if (completed_.get()) - handler_.complete(ec, bytes_transferred); - } - - private: - socket_type descriptor_; - boost::shared_ptr<bool> completed_; - select_reactor<Own_Thread>& reactor_; - Handler handler_; - }; - - // Start new write and exception operations. The handler object will be - // invoked when the given descriptor is ready for writing or has exception - // information available, or an error has occurred. The handler will be called - // only once. - template <typename Handler> - void start_connect_op(socket_type descriptor, - per_descriptor_data&, Handler handler) - { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - if (!shutdown_) - { - boost::shared_ptr<bool> completed(new bool(false)); - connect_handler_wrapper<Handler> wrapped_handler( - descriptor, completed, *this, handler); - bool interrupt = write_op_queue_.enqueue_operation( - descriptor, wrapped_handler); - interrupt = except_op_queue_.enqueue_operation( - descriptor, wrapped_handler) || interrupt; - if (interrupt) + bool first = op_queue_[op_type].enqueue_operation(descriptor, op); + io_service_.work_started(); + if (first) interrupter_.interrupt(); } } @@ -243,17 +152,7 @@ public: void cancel_ops(socket_type descriptor, per_descriptor_data&) { boost::asio::detail::mutex::scoped_lock lock(mutex_); - cancel_ops_unlocked(descriptor); - } - - // Enqueue cancellation of all operations associated with the given - // descriptor. The handlers associated with the descriptor will be invoked - // with the operation_aborted error. This function does not acquire the - // select_reactor's mutex, and so should only be used when the reactor lock is - // already held. - void enqueue_cancel_ops_unlocked(socket_type descriptor) - { - pending_cancellations_.push_back(descriptor); + cancel_ops_unlocked(descriptor, boost::asio::error::operation_aborted); } // Cancel any operations that are running against the descriptor and remove @@ -261,7 +160,7 @@ public: void close_descriptor(socket_type descriptor, per_descriptor_data&) { boost::asio::detail::mutex::scoped_lock lock(mutex_); - cancel_ops_unlocked(descriptor); + cancel_ops_unlocked(descriptor, boost::asio::error::operation_aborted); } // Add a new timer queue to the reactor. @@ -269,7 +168,7 @@ public: void add_timer_queue(timer_queue<Time_Traits>& timer_queue) { boost::asio::detail::mutex::scoped_lock lock(mutex_); - timer_queues_.push_back(&timer_queue); + timer_queues_.insert(&timer_queue); } // Remove a timer queue from the reactor. @@ -277,252 +176,186 @@ public: void remove_timer_queue(timer_queue<Time_Traits>& timer_queue) { boost::asio::detail::mutex::scoped_lock lock(mutex_); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - if (timer_queues_[i] == &timer_queue) - { - timer_queues_.erase(timer_queues_.begin() + i); - return; - } - } + timer_queues_.erase(&timer_queue); } - // Schedule a timer in the given timer queue to expire at the specified - // absolute time. The handler object will be invoked when the timer expires. - template <typename Time_Traits, typename Handler> + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template <typename Time_Traits> void schedule_timer(timer_queue<Time_Traits>& timer_queue, - const typename Time_Traits::time_type& time, Handler handler, void* token) + const typename Time_Traits::time_type& time, timer_op* op, void* token) { boost::asio::detail::mutex::scoped_lock lock(mutex_); if (!shutdown_) - if (timer_queue.enqueue_timer(time, handler, token)) + { + bool earliest = timer_queue.enqueue_timer(time, op, token); + io_service_.work_started(); + if (earliest) interrupter_.interrupt(); + } } - // Cancel the timer associated with the given token. Returns the number of - // handlers that have been posted or dispatched. + // Cancel the timer operations associated with the given token. Returns the + // number of operations that have been posted or dispatched. template <typename Time_Traits> std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token) { boost::asio::detail::mutex::scoped_lock lock(mutex_); - std::size_t n = timer_queue.cancel_timer(token); - if (n > 0) - interrupter_.interrupt(); + op_queue<operation> ops; + std::size_t n = timer_queue.cancel_timer(token, ops); + lock.unlock(); + io_service_.post_deferred_completions(ops); return n; } -private: - friend class task_io_service<select_reactor<Own_Thread> >; - // Run select once until interrupted or events are ready to be dispatched. - void run(bool block) + void run(bool block, op_queue<operation>& ops) { boost::asio::detail::mutex::scoped_lock lock(mutex_); - // Dispatch any operation cancellations that were made while the select - // loop was not running. - read_op_queue_.perform_cancellations(); - write_op_queue_.perform_cancellations(); - except_op_queue_.perform_cancellations(); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - timer_queues_[i]->dispatch_cancellations(); - // Check if the thread is supposed to stop. - if (stop_thread_) + if (Own_Thread) + if (stop_thread_) + return; + + // Set up the descriptor sets. + fd_set_adapter fds[max_select_ops]; + fds[read_op].set(interrupter_.read_descriptor()); + socket_type max_fd = 0; + bool have_work_to_do = !timer_queues_.all_empty(); + for (int i = 0; i < max_select_ops; ++i) { - complete_operations_and_timers(lock); - return; + have_work_to_do = have_work_to_do || !op_queue_[i].empty(); + op_queue_[i].get_descriptors(fds[i], ops); + if (fds[i].max_descriptor() > max_fd) + max_fd = fds[i].max_descriptor(); } +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Connection operations on Windows use both except and write fd_sets. + have_work_to_do = have_work_to_do || !op_queue_[connect_op].empty(); + op_queue_[connect_op].get_descriptors(fds[write_op], ops); + if (fds[write_op].max_descriptor() > max_fd) + max_fd = fds[write_op].max_descriptor(); + op_queue_[connect_op].get_descriptors(fds[except_op], ops); + if (fds[except_op].max_descriptor() > max_fd) + max_fd = fds[except_op].max_descriptor(); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // We can return immediately if there's no work to do and the reactor is // not supposed to block. - if (!block && read_op_queue_.empty() && write_op_queue_.empty() - && except_op_queue_.empty() && all_timer_queues_are_empty()) - { - complete_operations_and_timers(lock); + if (!block && !have_work_to_do) return; - } - // Set up the descriptor sets. - fd_set_adapter read_fds; - read_fds.set(interrupter_.read_descriptor()); - read_op_queue_.get_descriptors(read_fds); - fd_set_adapter write_fds; - write_op_queue_.get_descriptors(write_fds); - fd_set_adapter except_fds; - except_op_queue_.get_descriptors(except_fds); - socket_type max_fd = read_fds.max_descriptor(); - if (write_fds.max_descriptor() > max_fd) - max_fd = write_fds.max_descriptor(); - if (except_fds.max_descriptor() > max_fd) - max_fd = except_fds.max_descriptor(); - - // Block on the select call without holding the lock so that new - // operations can be started while the call is executing. + // Determine how long to block while waiting for events. timeval tv_buf = { 0, 0 }; timeval* tv = block ? get_timeout(tv_buf) : &tv_buf; - select_in_progress_ = true; + lock.unlock(); + + // Block on the select call until descriptors become ready. boost::system::error_code ec; int retval = socket_ops::select(static_cast<int>(max_fd + 1), - read_fds, write_fds, except_fds, tv, ec); - lock.lock(); - select_in_progress_ = false; + fds[read_op], fds[write_op], fds[except_op], tv, ec); // Reset the interrupter. - if (retval > 0 && read_fds.is_set(interrupter_.read_descriptor())) + if (retval > 0 && fds[read_op].is_set(interrupter_.read_descriptor())) interrupter_.reset(); + lock.lock(); + // Dispatch all ready operations. if (retval > 0) { +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Connection operations on Windows use both except and write fd_sets. + op_queue_[connect_op].perform_operations_for_descriptors( + fds[except_op], ops); + op_queue_[connect_op].perform_operations_for_descriptors( + fds[write_op], ops); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Exception operations must be processed first to ensure that any // out-of-band data is read before normal data. - except_op_queue_.perform_operations_for_descriptors( - except_fds, boost::system::error_code()); - read_op_queue_.perform_operations_for_descriptors( - read_fds, boost::system::error_code()); - write_op_queue_.perform_operations_for_descriptors( - write_fds, boost::system::error_code()); - except_op_queue_.perform_cancellations(); - read_op_queue_.perform_cancellations(); - write_op_queue_.perform_cancellations(); + for (int i = max_select_ops - 1; i >= 0; --i) + op_queue_[i].perform_operations_for_descriptors(fds[i], ops); } - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - timer_queues_[i]->dispatch_timers(); - timer_queues_[i]->dispatch_cancellations(); - } - - // Issue any pending cancellations. - for (size_t i = 0; i < pending_cancellations_.size(); ++i) - cancel_ops_unlocked(pending_cancellations_[i]); - pending_cancellations_.clear(); + timer_queues_.get_ready_timers(ops); + } - complete_operations_and_timers(lock); + // Interrupt the select loop. + void interrupt() + { + interrupter_.interrupt(); } +private: // Run the select loop in the thread. void run_thread() { - boost::asio::detail::mutex::scoped_lock lock(mutex_); - while (!stop_thread_) + if (Own_Thread) { - lock.unlock(); - run(true); - lock.lock(); + boost::asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + op_queue<operation> ops; + run(true, ops); + io_service_.post_deferred_completions(ops); + lock.lock(); + } } } // Entry point for the select loop thread. static void call_run_thread(select_reactor* reactor) { - reactor->run_thread(); - } - - // Interrupt the select loop. - void interrupt() - { - interrupter_.interrupt(); - } - - // Check if all timer queues are empty. - bool all_timer_queues_are_empty() const - { - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - if (!timer_queues_[i]->empty()) - return false; - return true; + if (Own_Thread) + { + reactor->run_thread(); + } } // Get the timeout value for the select call. timeval* get_timeout(timeval& tv) { - if (all_timer_queues_are_empty()) - return 0; - // By default we will wait no longer than 5 minutes. This will ensure that // any changes to the system clock are detected after no longer than this. - boost::posix_time::time_duration minimum_wait_duration - = boost::posix_time::minutes(5); - - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - boost::posix_time::time_duration wait_duration - = timer_queues_[i]->wait_duration(); - if (wait_duration < minimum_wait_duration) - minimum_wait_duration = wait_duration; - } - - if (minimum_wait_duration > boost::posix_time::time_duration()) - { - tv.tv_sec = minimum_wait_duration.total_seconds(); - tv.tv_usec = minimum_wait_duration.total_microseconds() % 1000000; - } - else - { - tv.tv_sec = 0; - tv.tv_usec = 0; - } - + long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; return &tv; } - // Cancel all operations associated with the given descriptor. The do_cancel - // function of the handler objects will be invoked. This function does not - // acquire the select_reactor's mutex. - void cancel_ops_unlocked(socket_type descriptor) + // Cancel all operations associated with the given descriptor. This function + // does not acquire the select_reactor's mutex. + void cancel_ops_unlocked(socket_type descriptor, + const boost::system::error_code& ec) { - bool interrupt = read_op_queue_.cancel_operations(descriptor); - interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; - interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; - if (interrupt) + bool need_interrupt = false; + op_queue<operation> ops; + for (int i = 0; i < max_ops; ++i) + need_interrupt = op_queue_[i].cancel_operations( + descriptor, ops, ec) || need_interrupt; + io_service_.post_deferred_completions(ops); + if (need_interrupt) interrupter_.interrupt(); } - // Clean up operations and timers. We must not hold the lock since the - // destructors may make calls back into this reactor. We make a copy of the - // vector of timer queues since the original may be modified while the lock - // is not held. - void complete_operations_and_timers( - boost::asio::detail::mutex::scoped_lock& lock) - { - timer_queues_for_cleanup_ = timer_queues_; - lock.unlock(); - read_op_queue_.complete_operations(); - write_op_queue_.complete_operations(); - except_op_queue_.complete_operations(); - for (std::size_t i = 0; i < timer_queues_for_cleanup_.size(); ++i) - timer_queues_for_cleanup_[i]->complete_timers(); - } + // The io_service implementation used to post completions. + io_service_impl& io_service_; // Mutex to protect access to internal data. boost::asio::detail::mutex mutex_; - // Whether the select loop is currently running or not. - bool select_in_progress_; - // The interrupter is used to break a blocking select call. select_interrupter interrupter_; - // The queue of read operations. - reactor_op_queue<socket_type> read_op_queue_; - - // The queue of write operations. - reactor_op_queue<socket_type> write_op_queue_; - - // The queue of exception operations. - reactor_op_queue<socket_type> except_op_queue_; + // The queues of read, write and except operations. + reactor_op_queue<socket_type> op_queue_[max_ops]; // The timer queues. - std::vector<timer_queue_base*> timer_queues_; - - // A copy of the timer queues, used when cleaning up timers. The copy is - // stored as a class data member to avoid unnecessary memory allocation. - std::vector<timer_queue_base*> timer_queues_for_cleanup_; - - // The descriptors that are pending cancellation. - std::vector<socket_type> pending_cancellations_; + timer_queue_set timer_queues_; // Does the reactor loop thread need to stop. bool stop_thread_; diff --git a/3rdParty/Boost/src/boost/asio/detail/service_registry.hpp b/3rdParty/Boost/src/boost/asio/detail/service_registry.hpp index b648939..e640ec8 100644 --- a/3rdParty/Boost/src/boost/asio/detail/service_registry.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/service_registry.hpp @@ -18,7 +18,6 @@ #include <boost/asio/detail/push_options.hpp> #include <boost/asio/detail/push_options.hpp> -#include <memory> #include <typeinfo> #include <boost/asio/detail/pop_options.hpp> @@ -80,7 +79,7 @@ public: while (first_service_) { boost::asio::io_service::service* next_service = first_service_->next_; - delete first_service_; + destroy(first_service_); first_service_ = next_service; } } @@ -91,14 +90,105 @@ public: template <typename Service> Service& use_service() { + boost::asio::io_service::service::key key; + init_key(key, Service::id); + factory_type factory = &service_registry::create<Service>; + return *static_cast<Service*>(do_use_service(key, factory)); + } + + // Add a service object. Returns false on error, in which case ownership of + // the object is retained by the caller. + template <typename Service> + bool add_service(Service* new_service) + { + boost::asio::io_service::service::key key; + init_key(key, Service::id); + return do_add_service(key, new_service); + } + + // Check whether a service object of the specified type already exists. + template <typename Service> + bool has_service() const + { + boost::asio::io_service::service::key key; + init_key(key, Service::id); + return do_has_service(key); + } + +private: + // Initialise a service's key based on its id. + void init_key(boost::asio::io_service::service::key& key, + const boost::asio::io_service::id& id) + { + key.type_info_ = 0; + key.id_ = &id; + } + +#if !defined(BOOST_ASIO_NO_TYPEID) + // Initialise a service's key based on its id. + template <typename Service> + void init_key(boost::asio::io_service::service::key& key, + const boost::asio::detail::service_id<Service>& /*id*/) + { + key.type_info_ = &typeid(typeid_wrapper<Service>); + key.id_ = 0; + } +#endif // !defined(BOOST_ASIO_NO_TYPEID) + + // Check if a service matches the given id. + static bool keys_match( + const boost::asio::io_service::service::key& key1, + const boost::asio::io_service::service::key& key2) + { + if (key1.id_ && key2.id_) + if (key1.id_ == key2.id_) + return true; + if (key1.type_info_ && key2.type_info_) + if (*key1.type_info_ == *key2.type_info_) + return true; + return false; + } + + // The type of a factory function used for creating a service instance. + typedef boost::asio::io_service::service* + (*factory_type)(boost::asio::io_service&); + + // Factory function for creating a service instance. + template <typename Service> + static boost::asio::io_service::service* create( + boost::asio::io_service& owner) + { + return new Service(owner); + } + + // Destroy a service instance. + static void destroy(boost::asio::io_service::service* service) + { + delete service; + } + + // Helper class to manage service pointers. + struct auto_service_ptr + { + boost::asio::io_service::service* ptr_; + ~auto_service_ptr() { destroy(ptr_); } + }; + + // Get the service object corresponding to the specified service key. Will + // create a new service object automatically if no such object already + // exists. Ownership of the service object is not transferred to the caller. + boost::asio::io_service::service* do_use_service( + const boost::asio::io_service::service::key& key, + factory_type factory) + { boost::asio::detail::mutex::scoped_lock lock(mutex_); - // First see if there is an existing service object for the given type. + // First see if there is an existing service object with the given key. boost::asio::io_service::service* service = first_service_; while (service) { - if (service_id_matches(*service, Service::id)) - return *static_cast<Service*>(service); + if (keys_match(service->key_, key)) + return service; service = service->next_; } @@ -106,9 +196,8 @@ public: // at this time to allow for nested calls into this function from the new // service's constructor. lock.unlock(); - std::auto_ptr<Service> new_service(new Service(owner_)); - init_service_id(*new_service, Service::id); - Service& new_service_ref = *new_service; + auto_service_ptr new_service = { factory(owner_) }; + new_service.ptr_->key_ = key; lock.lock(); // Check that nobody else created another service object of the same type @@ -116,52 +205,52 @@ public: service = first_service_; while (service) { - if (service_id_matches(*service, Service::id)) - return *static_cast<Service*>(service); + if (keys_match(service->key_, key)) + return service; service = service->next_; } // Service was successfully initialised, pass ownership to registry. - new_service->next_ = first_service_; - first_service_ = new_service.release(); - - return new_service_ref; + new_service.ptr_->next_ = first_service_; + first_service_ = new_service.ptr_; + new_service.ptr_ = 0; + return first_service_; } // Add a service object. Returns false on error, in which case ownership of // the object is retained by the caller. - template <typename Service> - bool add_service(Service* new_service) + bool do_add_service( + const boost::asio::io_service::service::key& key, + boost::asio::io_service::service* new_service) { boost::asio::detail::mutex::scoped_lock lock(mutex_); - // Check if there is an existing service object for the given type. + // Check if there is an existing service object with the given key. boost::asio::io_service::service* service = first_service_; while (service) { - if (service_id_matches(*service, Service::id)) + if (keys_match(service->key_, key)) return false; service = service->next_; } // Take ownership of the service object. - init_service_id(*new_service, Service::id); + new_service->key_ = key; new_service->next_ = first_service_; first_service_ = new_service; return true; } - // Check whether a service object of the specified type already exists. - template <typename Service> - bool has_service() const + // Check whether a service object with the specified key already exists. + bool do_has_service(const boost::asio::io_service::service::key& key) const { boost::asio::detail::mutex::scoped_lock lock(mutex_); boost::asio::io_service::service* service = first_service_; while (service) { - if (service_id_matches(*service, Service::id)) + if (keys_match(service->key_, key)) return true; service = service->next_; } @@ -169,46 +258,6 @@ public: return false; } -private: - // Set a service's id. - void init_service_id(boost::asio::io_service::service& service, - const boost::asio::io_service::id& id) - { - service.type_info_ = 0; - service.id_ = &id; - } - -#if !defined(BOOST_ASIO_NO_TYPEID) - // Set a service's id. - template <typename Service> - void init_service_id(boost::asio::io_service::service& service, - const boost::asio::detail::service_id<Service>& /*id*/) - { - service.type_info_ = &typeid(typeid_wrapper<Service>); - service.id_ = 0; - } -#endif // !defined(BOOST_ASIO_NO_TYPEID) - - // Check if a service matches the given id. - static bool service_id_matches( - const boost::asio::io_service::service& service, - const boost::asio::io_service::id& id) - { - return service.id_ == &id; - } - -#if !defined(BOOST_ASIO_NO_TYPEID) - // Check if a service matches the given id. - template <typename Service> - static bool service_id_matches( - const boost::asio::io_service::service& service, - const boost::asio::detail::service_id<Service>& /*id*/) - { - return service.type_info_ != 0 - && *service.type_info_ == typeid(typeid_wrapper<Service>); - } -#endif // !defined(BOOST_ASIO_NO_TYPEID) - // Mutex to protect access to internal data. mutable boost::asio::detail::mutex mutex_; diff --git a/3rdParty/Boost/src/boost/asio/detail/signal_blocker.hpp b/3rdParty/Boost/src/boost/asio/detail/signal_blocker.hpp index db11fe4..0197e04 100644 --- a/3rdParty/Boost/src/boost/asio/detail/signal_blocker.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/signal_blocker.hpp @@ -21,7 +21,7 @@ #include <boost/config.hpp> #include <boost/asio/detail/pop_options.hpp> -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) # include <boost/asio/detail/null_signal_blocker.hpp> #elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) # include <boost/asio/detail/win_signal_blocker.hpp> @@ -35,7 +35,7 @@ namespace boost { namespace asio { namespace detail { -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) typedef null_signal_blocker signal_blocker; #elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) typedef win_signal_blocker signal_blocker; diff --git a/3rdParty/Boost/src/boost/asio/detail/socket_types.hpp b/3rdParty/Boost/src/boost/asio/detail/socket_types.hpp index 7395ee4..1009164 100644 --- a/3rdParty/Boost/src/boost/asio/detail/socket_types.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/socket_types.hpp @@ -68,13 +68,15 @@ # define WIN32_LEAN_AND_MEAN # endif // !defined(WIN32_LEAN_AND_MEAN) # endif // !defined(BOOST_ASIO_NO_WIN32_LEAN_AND_MEAN) +# if !defined(BOOST_ASIO_NO_NOMINMAX) +# if !defined(NOMINMAX) +# define NOMINMAX 1 +# endif // !defined(NOMINMAX) +# endif // !defined(BOOST_ASIO_NO_NOMINMAX) # if defined(__CYGWIN__) # if !defined(__USE_W32_SOCKETS) # error You must add -D__USE_W32_SOCKETS to your compiler options. # endif // !defined(__USE_W32_SOCKETS) -# if !defined(NOMINMAX) -# define NOMINMAX 1 -# endif // !defined(NOMINMAX) # endif // defined(__CYGWIN__) # include <winsock2.h> # include <ws2tcpip.h> diff --git a/3rdParty/Boost/src/boost/asio/detail/solaris_fenced_block.hpp b/3rdParty/Boost/src/boost/asio/detail/solaris_fenced_block.hpp new file mode 100644 index 0000000..be9803e --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/solaris_fenced_block.hpp @@ -0,0 +1,59 @@ +// +// solaris_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP +#define BOOST_ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/push_options.hpp> +#include <boost/config.hpp> +#include <boost/asio/detail/pop_options.hpp> + +#if defined(__sun) + +#include <boost/asio/detail/push_options.hpp> +#include <atomic.h> +#include <boost/asio/detail/pop_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +class solaris_fenced_block + : private noncopyable +{ +public: + // Constructor. + solaris_fenced_block() + { + membar_consumer(); + } + + // Destructor. + ~solaris_fenced_block() + { + membar_producer(); + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(__sun) + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/strand_service.hpp b/3rdParty/Boost/src/boost/asio/detail/strand_service.hpp index b228cec..d6b45b1 100644 --- a/3rdParty/Boost/src/boost/asio/detail/strand_service.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/strand_service.hpp @@ -18,19 +18,19 @@ #include <boost/asio/detail/push_options.hpp> #include <boost/asio/detail/push_options.hpp> -#include <boost/aligned_storage.hpp> -#include <boost/assert.hpp> #include <boost/functional/hash.hpp> #include <boost/scoped_ptr.hpp> #include <boost/asio/detail/pop_options.hpp> #include <boost/asio/io_service.hpp> -#include <boost/asio/detail/bind_handler.hpp> #include <boost/asio/detail/call_stack.hpp> +#include <boost/asio/detail/completion_handler.hpp> +#include <boost/asio/detail/fenced_block.hpp> #include <boost/asio/detail/handler_alloc_helpers.hpp> #include <boost/asio/detail/handler_invoke_helpers.hpp> #include <boost/asio/detail/mutex.hpp> -#include <boost/asio/detail/noncopyable.hpp> +#include <boost/asio/detail/op_queue.hpp> +#include <boost/asio/detail/operation.hpp> #include <boost/asio/detail/service_base.hpp> namespace boost { @@ -41,252 +41,45 @@ namespace detail { class strand_service : public boost::asio::detail::service_base<strand_service> { +private: + struct on_do_complete_exit; + struct on_dispatch_exit; + public: - class handler_base; - class invoke_current_handler; - class post_next_waiter_on_exit; // The underlying implementation of a strand. class strand_impl + : public operation { public: strand_impl() - : current_handler_(0), - first_waiter_(0), - last_waiter_(0) + : operation(&strand_service::do_complete), + count_(0) { } private: // Only this service will have access to the internal values. friend class strand_service; - friend class post_next_waiter_on_exit; - friend class invoke_current_handler; + friend struct on_do_complete_exit; + friend struct on_dispatch_exit; // Mutex to protect access to internal data. boost::asio::detail::mutex mutex_; - // The handler that is ready to execute. If this pointer is non-null then it - // indicates that a handler holds the lock. - handler_base* current_handler_; - - // The start of the list of waiting handlers for the strand. - handler_base* first_waiter_; - - // The end of the list of waiting handlers for the strand. - handler_base* last_waiter_; - - // Storage for posted handlers. - typedef boost::aligned_storage<128> handler_storage_type; -#if defined(__BORLANDC__) - boost::aligned_storage<128> handler_storage_; -#else - handler_storage_type handler_storage_; -#endif - }; - - friend class strand_impl; - - typedef strand_impl* implementation_type; - - // Base class for all handler types. - class handler_base - { - public: - typedef void (*invoke_func_type)(handler_base*, - strand_service&, implementation_type&); - typedef void (*destroy_func_type)(handler_base*); - - handler_base(invoke_func_type invoke_func, destroy_func_type destroy_func) - : next_(0), - invoke_func_(invoke_func), - destroy_func_(destroy_func) - { - } - - void invoke(strand_service& service_impl, implementation_type& impl) - { - invoke_func_(this, service_impl, impl); - } - - void destroy() - { - destroy_func_(this); - } - - protected: - ~handler_base() - { - } - - private: - friend class strand_service; - friend class strand_impl; - friend class post_next_waiter_on_exit; - handler_base* next_; - invoke_func_type invoke_func_; - destroy_func_type destroy_func_; - }; - - // Helper class to allow handlers to be dispatched. - class invoke_current_handler - { - public: - invoke_current_handler(strand_service& service_impl, - const implementation_type& impl) - : service_impl_(service_impl), - impl_(impl) - { - } - - void operator()() - { - impl_->current_handler_->invoke(service_impl_, impl_); - } - - friend void* asio_handler_allocate(std::size_t size, - invoke_current_handler* this_handler) - { - return this_handler->do_handler_allocate(size); - } - - friend void asio_handler_deallocate(void*, std::size_t, - invoke_current_handler*) - { - } - - void* do_handler_allocate(std::size_t size) - { -#if defined(__BORLANDC__) - BOOST_ASSERT(size <= boost::aligned_storage<128>::size); -#else - BOOST_ASSERT(size <= strand_impl::handler_storage_type::size); -#endif - (void)size; - return impl_->handler_storage_.address(); - } - - // The asio_handler_invoke hook is not defined here since the default one - // provides the correct behaviour, and including it here breaks MSVC 7.1 - // in some situations. - - private: - strand_service& service_impl_; - implementation_type impl_; - }; - - // Helper class to automatically enqueue next waiter on block exit. - class post_next_waiter_on_exit - { - public: - post_next_waiter_on_exit(strand_service& service_impl, - implementation_type& impl) - : service_impl_(service_impl), - impl_(impl), - cancelled_(false) - { - } - - ~post_next_waiter_on_exit() - { - if (!cancelled_) - { - boost::asio::detail::mutex::scoped_lock lock(impl_->mutex_); - impl_->current_handler_ = impl_->first_waiter_; - if (impl_->current_handler_) - { - impl_->first_waiter_ = impl_->first_waiter_->next_; - if (impl_->first_waiter_ == 0) - impl_->last_waiter_ = 0; - lock.unlock(); - service_impl_.get_io_service().post( - invoke_current_handler(service_impl_, impl_)); - } - } - } - - void cancel() - { - cancelled_ = true; - } + // The count of handlers in the strand, including the upcall (if any). + std::size_t count_; - private: - strand_service& service_impl_; - implementation_type& impl_; - bool cancelled_; + // The handlers waiting on the strand. + op_queue<operation> queue_; }; - // Class template for a waiter. - template <typename Handler> - class handler_wrapper - : public handler_base - { - public: - handler_wrapper(Handler handler) - : handler_base(&handler_wrapper<Handler>::do_invoke, - &handler_wrapper<Handler>::do_destroy), - handler_(handler) - { - } - - static void do_invoke(handler_base* base, - strand_service& service_impl, implementation_type& impl) - { - // Take ownership of the handler object. - typedef handler_wrapper<Handler> this_type; - this_type* h(static_cast<this_type*>(base)); - typedef handler_alloc_traits<Handler, this_type> alloc_traits; - handler_ptr<alloc_traits> ptr(h->handler_, h); - - post_next_waiter_on_exit p1(service_impl, impl); - - // Make a copy of the handler so that the memory can be deallocated before - // the upcall is made. - Handler handler(h->handler_); - - // A handler object must still be valid when the next waiter is posted - // since destroying the last handler might cause the strand object to be - // destroyed. Therefore we create a second post_next_waiter_on_exit object - // that will be destroyed before the handler object. - p1.cancel(); - post_next_waiter_on_exit p2(service_impl, impl); - - // Free the memory associated with the handler. - ptr.reset(); - - // Indicate that this strand is executing on the current thread. - call_stack<strand_impl>::context ctx(impl); - - // Make the upcall. - boost_asio_handler_invoke_helpers::invoke(handler, handler); - } - - static void do_destroy(handler_base* base) - { - // Take ownership of the handler object. - typedef handler_wrapper<Handler> this_type; - this_type* h(static_cast<this_type*>(base)); - typedef handler_alloc_traits<Handler, this_type> alloc_traits; - handler_ptr<alloc_traits> ptr(h->handler_, h); - - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(h->handler_); - (void)handler; - - // Free the memory associated with the handler. - ptr.reset(); - } - - private: - Handler handler_; - }; + typedef strand_impl* implementation_type; // Construct a new strand service for the specified io_service. explicit strand_service(boost::asio::io_service& io_service) : boost::asio::detail::service_base<strand_service>(io_service), + io_service_(boost::asio::use_service<io_service_impl>(io_service)), mutex_(), salt_(0) { @@ -295,37 +88,13 @@ public: // Destroy all user-defined handler objects owned by the service. void shutdown_service() { - // Construct a list of all handlers to be destroyed. + op_queue<operation> ops; + boost::asio::detail::mutex::scoped_lock lock(mutex_); - handler_base* first_handler = 0; + for (std::size_t i = 0; i < num_implementations; ++i) - { if (strand_impl* impl = implementations_[i].get()) - { - if (impl->current_handler_) - { - impl->current_handler_->next_ = first_handler; - first_handler = impl->current_handler_; - impl->current_handler_ = 0; - } - if (impl->first_waiter_) - { - impl->last_waiter_->next_ = first_handler; - first_handler = impl->first_waiter_; - impl->first_waiter_ = 0; - impl->last_waiter_ = 0; - } - } - } - - // Destroy all handlers without holding the lock. - lock.unlock(); - while (first_handler) - { - handler_base* next = first_handler->next_; - first_handler->destroy(); - first_handler = next; - } + ops.push(impl->queue_); } // Construct a new strand implementation. @@ -352,45 +121,54 @@ public: template <typename Handler> void dispatch(implementation_type& impl, Handler handler) { + // If we are already in the strand then the handler can run immediately. if (call_stack<strand_impl>::contains(impl)) { + boost::asio::detail::fenced_block b; boost_asio_handler_invoke_helpers::invoke(handler, handler); + return; } - else + + // Allocate and construct an object to wrap the handler. + typedef completion_handler<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); + + // If we are running inside the io_service, and no other handler is queued + // or running, then the handler can run immediately. + bool can_dispatch = call_stack<io_service_impl>::contains(&io_service_); + impl->mutex_.lock(); + bool first = (++impl->count_ == 1); + if (can_dispatch && first) { - // Allocate and construct an object to wrap the handler. - typedef handler_wrapper<Handler> value_type; - typedef handler_alloc_traits<Handler, value_type> alloc_traits; - raw_handler_ptr<alloc_traits> raw_ptr(handler); - handler_ptr<alloc_traits> ptr(raw_ptr, handler); - - boost::asio::detail::mutex::scoped_lock lock(impl->mutex_); - - if (impl->current_handler_ == 0) - { - // This handler now has the lock, so can be dispatched immediately. - impl->current_handler_ = ptr.release(); - lock.unlock(); - this->get_io_service().dispatch(invoke_current_handler(*this, impl)); - } - else - { - // Another handler already holds the lock, so this handler must join - // the list of waiters. The handler will be posted automatically when - // its turn comes. - if (impl->last_waiter_) - { - impl->last_waiter_->next_ = ptr.get(); - impl->last_waiter_ = impl->last_waiter_->next_; - } - else - { - impl->first_waiter_ = ptr.get(); - impl->last_waiter_ = ptr.get(); - } - ptr.release(); - } + // Immediate invocation is allowed. + impl->mutex_.unlock(); + + // Memory must be releaesed before any upcall is made. + ptr.reset(); + + // Indicate that this strand is executing on the current thread. + call_stack<strand_impl>::context ctx(impl); + + // Ensure the next handler, if any, is scheduled on block exit. + on_dispatch_exit on_exit = { &io_service_, impl }; + (void)on_exit; + + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + return; } + + // Immediate invocation is not allowed, so enqueue for later. + impl->queue_.push(ptr.get()); + impl->mutex_.unlock(); + ptr.release(); + + // The first handler to be enqueued is responsible for scheduling the + // strand. + if (first) + io_service_.post_immediate_completion(impl); } // Request the io_service to invoke the given handler and return immediately. @@ -398,40 +176,85 @@ public: void post(implementation_type& impl, Handler handler) { // Allocate and construct an object to wrap the handler. - typedef handler_wrapper<Handler> value_type; + typedef completion_handler<Handler> value_type; typedef handler_alloc_traits<Handler, value_type> alloc_traits; raw_handler_ptr<alloc_traits> raw_ptr(handler); handler_ptr<alloc_traits> ptr(raw_ptr, handler); - boost::asio::detail::mutex::scoped_lock lock(impl->mutex_); + // Add the handler to the queue. + impl->mutex_.lock(); + bool first = (++impl->count_ == 1); + impl->queue_.push(ptr.get()); + impl->mutex_.unlock(); + ptr.release(); + + // The first handler to be enqueue is responsible for scheduling the strand. + if (first) + io_service_.post_immediate_completion(impl); + } + +private: + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + if (owner) + { + strand_impl* impl = static_cast<strand_impl*>(base); + + // Get the next handler to be executed. + impl->mutex_.lock(); + operation* o = impl->queue_.front(); + impl->queue_.pop(); + impl->mutex_.unlock(); + + // Indicate that this strand is executing on the current thread. + call_stack<strand_impl>::context ctx(impl); + + // Ensure the next handler, if any, is scheduled on block exit. + on_do_complete_exit on_exit = { owner, impl }; + (void)on_exit; + + o->complete(*owner); + } + } + + // Helper class to re-post the strand on exit. + struct on_do_complete_exit + { + io_service_impl* owner_; + strand_impl* impl_; - if (impl->current_handler_ == 0) + ~on_do_complete_exit() { - // This handler now has the lock, so can be dispatched immediately. - impl->current_handler_ = ptr.release(); - lock.unlock(); - this->get_io_service().post(invoke_current_handler(*this, impl)); + impl_->mutex_.lock(); + bool more_handlers = (--impl_->count_ > 0); + impl_->mutex_.unlock(); + + if (more_handlers) + owner_->post_immediate_completion(impl_); } - else + }; + + // Helper class to re-post the strand on exit. + struct on_dispatch_exit + { + io_service_impl* io_service_; + strand_impl* impl_; + + ~on_dispatch_exit() { - // Another handler already holds the lock, so this handler must join the - // list of waiters. The handler will be posted automatically when its turn - // comes. - if (impl->last_waiter_) - { - impl->last_waiter_->next_ = ptr.get(); - impl->last_waiter_ = impl->last_waiter_->next_; - } - else - { - impl->first_waiter_ = ptr.get(); - impl->last_waiter_ = ptr.get(); - } - ptr.release(); + impl_->mutex_.lock(); + bool more_handlers = (--impl_->count_ > 0); + impl_->mutex_.unlock(); + + if (more_handlers) + io_service_->post_immediate_completion(impl_); } - } + }; + + // The io_service implementation used to post completions. + io_service_impl& io_service_; -private: // Mutex to protect access to the array of implementations. boost::asio::detail::mutex mutex_; diff --git a/3rdParty/Boost/src/boost/asio/detail/task_io_service.hpp b/3rdParty/Boost/src/boost/asio/detail/task_io_service.hpp index ddfea72..f6de370 100644 --- a/3rdParty/Boost/src/boost/asio/detail/task_io_service.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/task_io_service.hpp @@ -15,21 +15,25 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#if defined(BOOST_ASIO_ENABLE_TWO_LOCK_QUEUE) -#include <boost/asio/detail/task_io_service_2lock.hpp> -#else // defined(BOOST_ASIO_ENABLE_TWO_LOCK_QUEUE) - #include <boost/asio/detail/push_options.hpp> #include <boost/asio/io_service.hpp> #include <boost/asio/detail/call_stack.hpp> +#include <boost/asio/detail/completion_handler.hpp> #include <boost/asio/detail/event.hpp> +#include <boost/asio/detail/fenced_block.hpp> #include <boost/asio/detail/handler_alloc_helpers.hpp> #include <boost/asio/detail/handler_invoke_helpers.hpp> -#include <boost/asio/detail/handler_queue.hpp> #include <boost/asio/detail/mutex.hpp> +#include <boost/asio/detail/op_queue.hpp> #include <boost/asio/detail/service_base.hpp> #include <boost/asio/detail/task_io_service_fwd.hpp> +#include <boost/asio/detail/task_io_service_operation.hpp> + +#include <boost/asio/detail/push_options.hpp> +#include <boost/detail/atomic_count.hpp> +#include <boost/system/error_code.hpp> +#include <boost/asio/detail/pop_options.hpp> namespace boost { namespace asio { @@ -40,6 +44,8 @@ class task_io_service : public boost::asio::detail::service_base<task_io_service<Task> > { public: + typedef task_io_service_operation<Task> operation; + // Constructor. task_io_service(boost::asio::io_service& io_service) : boost::asio::detail::service_base<task_io_service<Task> >(io_service), @@ -65,12 +71,12 @@ public: lock.unlock(); // Destroy handler objects. - while (!handler_queue_.empty()) + while (!op_queue_.empty()) { - handler_queue::handler* h = handler_queue_.front(); - handler_queue_.pop(); - if (h != &task_handler_) - h->destroy(); + operation* o = op_queue_.front(); + op_queue_.pop(); + if (o != &task_operation_) + o->destroy(); } // Reset to initial state. @@ -84,14 +90,21 @@ public: if (!shutdown_ && !task_) { task_ = &use_service<Task>(this->get_io_service()); - handler_queue_.push(&task_handler_); - interrupt_one_idle_thread(lock); + op_queue_.push(&task_operation_); + wake_one_thread_and_unlock(lock); } } // Run the event loop until interrupted or no more work. size_t run(boost::system::error_code& ec) { + ec = boost::system::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + typename call_stack<task_io_service>::context ctx(this); idle_thread_info this_idle_thread; @@ -100,7 +113,7 @@ public: boost::asio::detail::mutex::scoped_lock lock(mutex_); size_t n = 0; - while (do_one(lock, &this_idle_thread, ec)) + for (; do_one(lock, &this_idle_thread); lock.lock()) if (n != (std::numeric_limits<size_t>::max)()) ++n; return n; @@ -109,6 +122,13 @@ public: // Run until interrupted or one operation is performed. size_t run_one(boost::system::error_code& ec) { + ec = boost::system::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + typename call_stack<task_io_service>::context ctx(this); idle_thread_info this_idle_thread; @@ -116,18 +136,25 @@ public: boost::asio::detail::mutex::scoped_lock lock(mutex_); - return do_one(lock, &this_idle_thread, ec); + return do_one(lock, &this_idle_thread); } // Poll for operations without blocking. size_t poll(boost::system::error_code& ec) { + if (outstanding_work_ == 0) + { + stop(); + ec = boost::system::error_code(); + return 0; + } + typename call_stack<task_io_service>::context ctx(this); boost::asio::detail::mutex::scoped_lock lock(mutex_); size_t n = 0; - while (do_one(lock, 0, ec)) + for (; do_one(lock, 0); lock.lock()) if (n != (std::numeric_limits<size_t>::max)()) ++n; return n; @@ -136,11 +163,18 @@ public: // Poll for one operation without blocking. size_t poll_one(boost::system::error_code& ec) { + ec = boost::system::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + typename call_stack<task_io_service>::context ctx(this); boost::asio::detail::mutex::scoped_lock lock(mutex_); - return do_one(lock, 0, ec); + return do_one(lock, 0); } // Interrupt the event processing loop. @@ -160,16 +194,14 @@ public: // Notify that some work has started. void work_started() { - boost::asio::detail::mutex::scoped_lock lock(mutex_); ++outstanding_work_; } // Notify that some work has finished. void work_finished() { - boost::asio::detail::mutex::scoped_lock lock(mutex_); if (--outstanding_work_ == 0) - stop_all_threads(lock); + stop(); } // Request invocation of the given handler. @@ -177,7 +209,10 @@ public: void dispatch(Handler handler) { if (call_stack<task_io_service>::contains(this)) + { + boost::asio::detail::fenced_block b; boost_asio_handler_invoke_helpers::invoke(handler, handler); + } else post(handler); } @@ -187,29 +222,41 @@ public: void post(Handler handler) { // Allocate and construct an operation to wrap the handler. - handler_queue::scoped_ptr ptr(handler_queue::wrap(handler)); - - boost::asio::detail::mutex::scoped_lock lock(mutex_); - - // If the service has been shut down we silently discard the handler. - if (shutdown_) - return; + typedef completion_handler<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); - // Add the handler to the end of the queue. - handler_queue_.push(ptr.get()); + post_immediate_completion(ptr.get()); ptr.release(); + } - // An undelivered handler is treated as unfinished work. - ++outstanding_work_; + // Request invocation of the given operation and return immediately. Assumes + // that work_started() has not yet been called for the operation. + void post_immediate_completion(operation* op) + { + work_started(); + post_deferred_completion(op); + } + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() was previously called for the operation. + void post_deferred_completion(operation* op) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + op_queue_.push(op); + wake_one_thread_and_unlock(lock); + } - // Wake up a thread to execute the handler. - if (!interrupt_one_idle_thread(lock)) + // Request invocation of the given operations and return immediately. Assumes + // that work_started() was previously called for each operation. + void post_deferred_completions(op_queue<operation>& ops) + { + if (!ops.empty()) { - if (!task_interrupted_ && task_) - { - task_interrupted_ = true; - task_->interrupt(); - } + boost::asio::detail::mutex::scoped_lock lock(mutex_); + op_queue_.push(ops); + wake_one_thread_and_unlock(lock); } } @@ -217,57 +264,58 @@ private: struct idle_thread_info; size_t do_one(boost::asio::detail::mutex::scoped_lock& lock, - idle_thread_info* this_idle_thread, boost::system::error_code& ec) + idle_thread_info* this_idle_thread) { - if (outstanding_work_ == 0 && !stopped_) - { - stop_all_threads(lock); - ec = boost::system::error_code(); - return 0; - } - bool polling = !this_idle_thread; bool task_has_run = false; while (!stopped_) { - if (!handler_queue_.empty()) + if (!op_queue_.empty()) { // Prepare to execute first handler from queue. - handler_queue::handler* h = handler_queue_.front(); - handler_queue_.pop(); + operation* o = op_queue_.front(); + op_queue_.pop(); + bool more_handlers = (!op_queue_.empty()); - if (h == &task_handler_) + if (o == &task_operation_) { - bool more_handlers = (!handler_queue_.empty()); task_interrupted_ = more_handlers || polling; // If the task has already run and we're polling then we're done. if (task_has_run && polling) { task_interrupted_ = true; - handler_queue_.push(&task_handler_); - ec = boost::system::error_code(); + op_queue_.push(&task_operation_); return 0; } task_has_run = true; - lock.unlock(); - task_cleanup c(lock, *this); + if (!more_handlers || !wake_one_idle_thread_and_unlock(lock)) + lock.unlock(); - // Run the task. May throw an exception. Only block if the handler - // queue is empty and we have an idle_thread_info object, otherwise - // we want to return as soon as possible. - task_->run(!more_handlers && !polling); + op_queue<operation> completed_ops; + task_cleanup c = { this, &lock, &completed_ops }; + (void)c; + + // Run the task. May throw an exception. Only block if the operation + // queue is empty and we're not polling, otherwise we want to return + // as soon as possible. + task_->run(!more_handlers && !polling, completed_ops); } else { - lock.unlock(); - handler_cleanup c(lock, *this); + if (more_handlers) + wake_one_thread_and_unlock(lock); + else + lock.unlock(); + + // Ensure the count of outstanding work is decremented on block exit. + work_finished_on_block_exit on_exit = { this }; + (void)on_exit; - // Invoke the handler. May throw an exception. - h->invoke(); // invoke() deletes the handler object + // Complete the operation. May throw an exception. + o->complete(*this); // deletes the operation object - ec = boost::system::error_code(); return 1; } } @@ -281,12 +329,10 @@ private: } else { - ec = boost::system::error_code(); return 0; } } - ec = boost::system::error_code(); return 0; } @@ -295,7 +341,15 @@ private: boost::asio::detail::mutex::scoped_lock& lock) { stopped_ = true; - interrupt_all_idle_threads(lock); + + while (first_idle_thread_) + { + idle_thread_info* idle_thread = first_idle_thread_; + first_idle_thread_ = idle_thread->next; + idle_thread->next = 0; + idle_thread->wakeup_event.signal(lock); + } + if (!task_interrupted_ && task_) { task_interrupted_ = true; @@ -303,9 +357,10 @@ private: } } - // Interrupt a single idle thread. Returns true if a thread was interrupted, - // false if no running thread could be found to interrupt. - bool interrupt_one_idle_thread( + // Wakes a single idle thread and unlocks the mutex. Returns true if an idle + // thread was found. If there is no idle thread, returns false and leaves the + // mutex locked. + bool wake_one_idle_thread_and_unlock( boost::asio::detail::mutex::scoped_lock& lock) { if (first_idle_thread_) @@ -313,74 +368,56 @@ private: idle_thread_info* idle_thread = first_idle_thread_; first_idle_thread_ = idle_thread->next; idle_thread->next = 0; - idle_thread->wakeup_event.signal(lock); + idle_thread->wakeup_event.signal_and_unlock(lock); return true; } return false; } - // Interrupt all idle threads. - void interrupt_all_idle_threads( + // Wake a single idle thread, or the task, and always unlock the mutex. + void wake_one_thread_and_unlock( boost::asio::detail::mutex::scoped_lock& lock) { - while (first_idle_thread_) + if (!wake_one_idle_thread_and_unlock(lock)) { - idle_thread_info* idle_thread = first_idle_thread_; - first_idle_thread_ = idle_thread->next; - idle_thread->next = 0; - idle_thread->wakeup_event.signal(lock); + if (!task_interrupted_ && task_) + { + task_interrupted_ = true; + task_->interrupt(); + } + lock.unlock(); } } // Helper class to perform task-related operations on block exit. - class task_cleanup; - friend class task_cleanup; - class task_cleanup + struct task_cleanup; + friend struct task_cleanup; + struct task_cleanup { - public: - task_cleanup(boost::asio::detail::mutex::scoped_lock& lock, - task_io_service& task_io_svc) - : lock_(lock), - task_io_service_(task_io_svc) - { - } - ~task_cleanup() { - // Reinsert the task at the end of the handler queue. - lock_.lock(); - task_io_service_.task_interrupted_ = true; - task_io_service_.handler_queue_.push(&task_io_service_.task_handler_); + // Enqueue the completed operations and reinsert the task at the end of + // the operation queue. + lock_->lock(); + task_io_service_->task_interrupted_ = true; + task_io_service_->op_queue_.push(*ops_); + task_io_service_->op_queue_.push(&task_io_service_->task_operation_); } - private: - boost::asio::detail::mutex::scoped_lock& lock_; - task_io_service& task_io_service_; + task_io_service* task_io_service_; + boost::asio::detail::mutex::scoped_lock* lock_; + op_queue<operation>* ops_; }; - // Helper class to perform handler-related operations on block exit. - class handler_cleanup; - friend class handler_cleanup; - class handler_cleanup + // Helper class to call work_finished() on block exit. + struct work_finished_on_block_exit { - public: - handler_cleanup(boost::asio::detail::mutex::scoped_lock& lock, - task_io_service& task_io_svc) - : lock_(lock), - task_io_service_(task_io_svc) - { - } - - ~handler_cleanup() + ~work_finished_on_block_exit() { - lock_.lock(); - if (--task_io_service_.outstanding_work_ == 0) - task_io_service_.stop_all_threads(lock_); + task_io_service_->work_finished(); } - private: - boost::asio::detail::mutex::scoped_lock& lock_; - task_io_service& task_io_service_; + task_io_service* task_io_service_; }; // Mutex to protect access to internal data. @@ -389,25 +426,20 @@ private: // The task to be run by this service. Task* task_; - // Handler object to represent the position of the task in the queue. - class task_handler - : public handler_queue::handler + // Operation object to represent the position of the task in the queue. + struct task_operation : public operation { - public: - task_handler() - : handler_queue::handler(0, 0) - { - } - } task_handler_; + task_operation() : operation(0) {} + } task_operation_; // Whether the task has been interrupted. bool task_interrupted_; // The count of unfinished work. - int outstanding_work_; + boost::detail::atomic_count outstanding_work_; // The queue of handlers that are ready to be delivered. - handler_queue handler_queue_; + op_queue<operation> op_queue_; // Flag to indicate that the dispatcher has been stopped. bool stopped_; @@ -422,7 +454,7 @@ private: idle_thread_info* next; }; - // The number of threads that are currently idle. + // The threads that are currently idle. idle_thread_info* first_idle_thread_; }; @@ -430,9 +462,6 @@ private: } // namespace asio } // namespace boost -#include <boost/system/error_code.hpp> #include <boost/asio/detail/pop_options.hpp> -#endif // defined(BOOST_ASIO_ENABLE_TWO_LOCK_QUEUE) - #endif // BOOST_ASIO_DETAIL_TASK_IO_SERVICE_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/task_io_service_2lock.hpp b/3rdParty/Boost/src/boost/asio/detail/task_io_service_2lock.hpp deleted file mode 100644 index 71bceef..0000000 --- a/3rdParty/Boost/src/boost/asio/detail/task_io_service_2lock.hpp +++ /dev/null @@ -1,475 +0,0 @@ -// -// task_io_service_2lock.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2010 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) -// - -#ifndef BOOST_ASIO_DETAIL_TASK_IO_SERVICE_2LOCK_HPP -#define BOOST_ASIO_DETAIL_TASK_IO_SERVICE_2LOCK_HPP - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -#include <boost/asio/detail/push_options.hpp> - -#include <boost/asio/io_service.hpp> -#include <boost/asio/detail/call_stack.hpp> -#include <boost/asio/detail/event.hpp> -#include <boost/asio/detail/handler_alloc_helpers.hpp> -#include <boost/asio/detail/handler_invoke_helpers.hpp> -#include <boost/asio/detail/indirect_handler_queue.hpp> -#include <boost/asio/detail/mutex.hpp> -#include <boost/asio/detail/service_base.hpp> -#include <boost/asio/detail/task_io_service_fwd.hpp> - -#include <boost/asio/detail/push_options.hpp> -#include <boost/detail/atomic_count.hpp> -#include <boost/system/error_code.hpp> -#include <boost/asio/detail/pop_options.hpp> - -namespace boost { -namespace asio { -namespace detail { - -// An alternative task_io_service implementation based on a two-lock queue. - -template <typename Task> -class task_io_service - : public boost::asio::detail::service_base<task_io_service<Task> > -{ -public: - typedef indirect_handler_queue handler_queue; - - // Constructor. - task_io_service(boost::asio::io_service& io_service) - : boost::asio::detail::service_base<task_io_service<Task> >(io_service), - front_mutex_(), - back_mutex_(), - task_(0), - outstanding_work_(0), - front_stopped_(false), - back_stopped_(false), - back_shutdown_(false), - back_first_idle_thread_(0), - back_task_thread_(0) - { - } - - void init(size_t /*concurrency_hint*/) - { - } - - // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - boost::asio::detail::mutex::scoped_lock back_lock(back_mutex_); - back_shutdown_ = true; - back_lock.unlock(); - - // Destroy handler objects. - while (handler_queue::handler* h = handler_queue_.pop()) - if (h != &task_handler_) - h->destroy(); - - // Reset to initial state. - task_ = 0; - } - - // Initialise the task, if required. - void init_task() - { - boost::asio::detail::mutex::scoped_lock back_lock(back_mutex_); - if (!back_shutdown_ && !task_) - { - task_ = &use_service<Task>(this->get_io_service()); - handler_queue_.push(&task_handler_); - interrupt_one_idle_thread(back_lock); - } - } - - // Run the event loop until interrupted or no more work. - size_t run(boost::system::error_code& ec) - { - if (outstanding_work_ == 0) - { - stop(); - ec = boost::system::error_code(); - return 0; - } - - typename call_stack<task_io_service>::context ctx(this); - - idle_thread_info this_idle_thread; - this_idle_thread.next = 0; - - size_t n = 0; - while (do_one(&this_idle_thread, ec)) - if (n != (std::numeric_limits<size_t>::max)()) - ++n; - return n; - } - - // Run until interrupted or one operation is performed. - size_t run_one(boost::system::error_code& ec) - { - if (outstanding_work_ == 0) - { - stop(); - ec = boost::system::error_code(); - return 0; - } - - typename call_stack<task_io_service>::context ctx(this); - - idle_thread_info this_idle_thread; - this_idle_thread.next = 0; - - return do_one(&this_idle_thread, ec); - } - - // Poll for operations without blocking. - size_t poll(boost::system::error_code& ec) - { - if (outstanding_work_ == 0) - { - stop(); - ec = boost::system::error_code(); - return 0; - } - - typename call_stack<task_io_service>::context ctx(this); - - size_t n = 0; - while (do_one(0, ec)) - if (n != (std::numeric_limits<size_t>::max)()) - ++n; - return n; - } - - // Poll for one operation without blocking. - size_t poll_one(boost::system::error_code& ec) - { - if (outstanding_work_ == 0) - { - stop(); - ec = boost::system::error_code(); - return 0; - } - - typename call_stack<task_io_service>::context ctx(this); - - return do_one(0, ec); - } - - // Interrupt the event processing loop. - void stop() - { - boost::asio::detail::mutex::scoped_lock front_lock(front_mutex_); - front_stopped_ = true; - front_lock.unlock(); - - boost::asio::detail::mutex::scoped_lock back_lock(back_mutex_); - back_stopped_ = true; - interrupt_all_idle_threads(back_lock); - } - - // Reset in preparation for a subsequent run invocation. - void reset() - { - boost::asio::detail::mutex::scoped_lock front_lock(front_mutex_); - front_stopped_ = false; - front_lock.unlock(); - - boost::asio::detail::mutex::scoped_lock back_lock(back_mutex_); - back_stopped_ = false; - } - - // Notify that some work has started. - void work_started() - { - ++outstanding_work_; - } - - // Notify that some work has finished. - void work_finished() - { - if (--outstanding_work_ == 0) - stop(); - } - - // Request invocation of the given handler. - template <typename Handler> - void dispatch(Handler handler) - { - if (call_stack<task_io_service>::contains(this)) - boost_asio_handler_invoke_helpers::invoke(handler, handler); - else - post(handler); - } - - // Request invocation of the given handler and return immediately. - template <typename Handler> - void post(Handler handler) - { - // Allocate and construct an operation to wrap the handler. - handler_queue::scoped_ptr ptr(handler_queue::wrap(handler)); - - boost::asio::detail::mutex::scoped_lock back_lock(back_mutex_); - - // If the service has been shut down we silently discard the handler. - if (back_shutdown_) - return; - - // Add the handler to the end of the queue. - handler_queue_.push(ptr.get()); - ptr.release(); - - // An undelivered handler is treated as unfinished work. - ++outstanding_work_; - - // Wake up a thread to execute the handler. - interrupt_one_idle_thread(back_lock); - } - -private: - struct idle_thread_info; - - size_t do_one(idle_thread_info* this_idle_thread, - boost::system::error_code& ec) - { - bool task_has_run = false; - for (;;) - { - // The front lock must be held before we can pop items from the queue. - boost::asio::detail::mutex::scoped_lock front_lock(front_mutex_); - if (front_stopped_) - { - ec = boost::system::error_code(); - return 0; - } - - if (handler_queue::handler* h = handler_queue_.pop()) - { - if (h == &task_handler_) - { - bool more_handlers = handler_queue_.poppable(); - unsigned long front_version = handler_queue_.front_version(); - front_lock.unlock(); - - // The task is always added to the back of the queue when we exit - // this block. - task_cleanup c(*this); - - // If we're polling and the task has already run then we're done. - bool polling = !this_idle_thread; - if (task_has_run && polling) - { - ec = boost::system::error_code(); - return 0; - } - - // If we're considering going idle we need to check whether the queue - // is still empty. If it is, add the thread to the list of idle - // threads. - if (!more_handlers && !polling) - { - boost::asio::detail::mutex::scoped_lock back_lock(back_mutex_); - if (back_stopped_) - { - ec = boost::system::error_code(); - return 0; - } - else if (front_version == handler_queue_.back_version()) - { - back_task_thread_ = this_idle_thread; - } - else - { - more_handlers = true; - } - } - - // Run the task. May throw an exception. Only block if the handler - // queue is empty and we're not polling, otherwise we want to return - // as soon as possible. - task_has_run = true; - task_->run(!more_handlers && !polling); - } - else - { - front_lock.unlock(); - handler_cleanup c(*this); - - // Invoke the handler. May throw an exception. - h->invoke(); // invoke() deletes the handler object - - ec = boost::system::error_code(); - return 1; - } - } - else if (this_idle_thread) - { - unsigned long front_version = handler_queue_.front_version(); - front_lock.unlock(); - - // If we're considering going idle we need to check whether the queue - // is still empty. If it is, add the thread to the list of idle - // threads. - boost::asio::detail::mutex::scoped_lock back_lock(back_mutex_); - if (back_stopped_) - { - ec = boost::system::error_code(); - return 0; - } - else if (front_version == handler_queue_.back_version()) - { - this_idle_thread->next = back_first_idle_thread_; - back_first_idle_thread_ = this_idle_thread; - this_idle_thread->wakeup_event.clear(back_lock); - this_idle_thread->wakeup_event.wait(back_lock); - } - } - else - { - ec = boost::system::error_code(); - return 0; - } - } - } - - // Interrupt a single idle thread. - void interrupt_one_idle_thread( - boost::asio::detail::mutex::scoped_lock& back_lock) - { - if (back_first_idle_thread_) - { - idle_thread_info* idle_thread = back_first_idle_thread_; - back_first_idle_thread_ = idle_thread->next; - idle_thread->next = 0; - idle_thread->wakeup_event.signal(back_lock); - } - else if (back_task_thread_ && task_) - { - back_task_thread_ = 0; - task_->interrupt(); - } - } - - // Interrupt all idle threads. - void interrupt_all_idle_threads( - boost::asio::detail::mutex::scoped_lock& back_lock) - { - while (back_first_idle_thread_) - { - idle_thread_info* idle_thread = back_first_idle_thread_; - back_first_idle_thread_ = idle_thread->next; - idle_thread->next = 0; - idle_thread->wakeup_event.signal(back_lock); - } - - if (back_task_thread_ && task_) - { - back_task_thread_ = 0; - task_->interrupt(); - } - } - - // Helper class to perform task-related operations on block exit. - class task_cleanup; - friend class task_cleanup; - class task_cleanup - { - public: - task_cleanup(task_io_service& task_io_svc) - : task_io_service_(task_io_svc) - { - } - - ~task_cleanup() - { - // Reinsert the task at the end of the handler queue. - boost::asio::detail::mutex::scoped_lock back_lock( - task_io_service_.back_mutex_); - task_io_service_.back_task_thread_ = 0; - task_io_service_.handler_queue_.push(&task_io_service_.task_handler_); - } - - private: - task_io_service& task_io_service_; - }; - - // Helper class to perform handler-related operations on block exit. - class handler_cleanup - { - public: - handler_cleanup(task_io_service& task_io_svc) - : task_io_service_(task_io_svc) - { - } - - ~handler_cleanup() - { - task_io_service_.work_finished(); - } - - private: - task_io_service& task_io_service_; - }; - - // Mutexes to protect access to internal data. - boost::asio::detail::mutex front_mutex_; - boost::asio::detail::mutex back_mutex_; - - // The task to be run by this service. - Task* task_; - - // Handler object to represent the position of the task in the queue. - class task_handler - : public handler_queue::handler - { - public: - task_handler() - : handler_queue::handler(0, 0) - { - } - } task_handler_; - - // The count of unfinished work. - boost::detail::atomic_count outstanding_work_; - - // The queue of handlers that are ready to be delivered. - handler_queue handler_queue_; - - // Flag to indicate that the dispatcher has been stopped. - bool front_stopped_; - bool back_stopped_; - - // Flag to indicate that the dispatcher has been shut down. - bool back_shutdown_; - - // Structure containing information about an idle thread. - struct idle_thread_info - { - event wakeup_event; - idle_thread_info* next; - }; - - // The number of threads that are currently idle. - idle_thread_info* back_first_idle_thread_; - - // The thread that is currently blocked on the task. - idle_thread_info* back_task_thread_; -}; - -} // namespace detail -} // namespace asio -} // namespace boost - -#include <boost/asio/detail/pop_options.hpp> - -#endif // BOOST_ASIO_DETAIL_TASK_IO_SERVICE_2LOCK_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/task_io_service_operation.hpp b/3rdParty/Boost/src/boost/asio/detail/task_io_service_operation.hpp new file mode 100644 index 0000000..557f673 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/task_io_service_operation.hpp @@ -0,0 +1,71 @@ +// +// task_io_service_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_TASK_IO_SERVICE_OPERATION_HPP +#define BOOST_ASIO_DETAIL_TASK_IO_SERVICE_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/op_queue.hpp> +#include <boost/asio/detail/task_io_service_fwd.hpp> + +namespace boost { +namespace asio { +namespace detail { + +// Base class for all operations. A function pointer is used instead of virtual +// functions to avoid the associated overhead. +template <typename Task> +class task_io_service_operation +{ +public: + void complete(task_io_service<Task>& owner) + { + func_(&owner, this, boost::system::error_code(), 0); + } + + void destroy() + { + func_(0, this, boost::system::error_code(), 0); + } + +protected: + typedef void (*func_type)(task_io_service<Task>*, + task_io_service_operation*, boost::system::error_code, std::size_t); + + task_io_service_operation(func_type func) + : next_(0), + func_(func) + { + } + + // Prevents deletion through this type. + ~task_io_service_operation() + { + } + +private: + friend class op_queue_access; + task_io_service_operation* next_; + func_type func_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/system/error_code.hpp> +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_TASK_IO_SERVICE_OPERATION_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/thread.hpp b/3rdParty/Boost/src/boost/asio/detail/thread.hpp index 24a7a8c..3e2e215 100644 --- a/3rdParty/Boost/src/boost/asio/detail/thread.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/thread.hpp @@ -21,7 +21,7 @@ #include <boost/config.hpp> #include <boost/asio/detail/pop_options.hpp> -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) # include <boost/asio/detail/null_thread.hpp> #elif defined(BOOST_WINDOWS) # if defined(UNDER_CE) @@ -39,7 +39,7 @@ namespace boost { namespace asio { namespace detail { -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) typedef null_thread thread; #elif defined(BOOST_WINDOWS) # if defined(UNDER_CE) diff --git a/3rdParty/Boost/src/boost/asio/detail/timer_op.hpp b/3rdParty/Boost/src/boost/asio/detail/timer_op.hpp new file mode 100644 index 0000000..9973d89 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/timer_op.hpp @@ -0,0 +1,46 @@ +// +// timer_op.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_TIMER_OP_HPP +#define BOOST_ASIO_DETAIL_TIMER_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/operation.hpp> + +namespace boost { +namespace asio { +namespace detail { + +class timer_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + boost::system::error_code ec_; + +protected: + timer_op(func_type func) + : operation(func) + { + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_TIMER_OP_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/timer_queue.hpp b/3rdParty/Boost/src/boost/asio/detail/timer_queue.hpp index 35c8a77..164b342 100644 --- a/3rdParty/Boost/src/boost/asio/detail/timer_queue.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/timer_queue.hpp @@ -19,7 +19,6 @@ #include <boost/asio/detail/push_options.hpp> #include <cstddef> -#include <functional> #include <memory> #include <vector> #include <boost/config.hpp> @@ -27,9 +26,9 @@ #include <boost/asio/detail/pop_options.hpp> #include <boost/asio/error.hpp> -#include <boost/asio/detail/handler_alloc_helpers.hpp> #include <boost/asio/detail/hash_map.hpp> -#include <boost/asio/detail/noncopyable.hpp> +#include <boost/asio/detail/op_queue.hpp> +#include <boost/asio/detail/timer_op.hpp> #include <boost/asio/detail/timer_queue_base.hpp> namespace boost { @@ -50,50 +49,36 @@ public: // Constructor. timer_queue() : timers_(), - heap_(), - cancelled_timers_(0), - complete_timers_(0) + heap_() { } // Add a new timer to the queue. Returns true if this is the timer that is // earliest in the queue, in which case the reactor's event demultiplexing // function call may need to be interrupted and restarted. - template <typename Handler> - bool enqueue_timer(const time_type& time, Handler handler, void* token) + bool enqueue_timer(const time_type& time, timer_op* op, void* token) { // Ensure that there is space for the timer in the heap. We reserve here so // that the push_back below will not throw due to a reallocation failure. heap_.reserve(heap_.size() + 1); - // Create a new timer object. - typedef timer<Handler> timer_type; - typedef handler_alloc_traits<Handler, timer_type> alloc_traits; - raw_handler_ptr<alloc_traits> raw_ptr(handler); - handler_ptr<alloc_traits> new_timer(raw_ptr, time, handler, token); - // Insert the new timer into the hash. - typedef typename hash_map<void*, timer_base*>::iterator iterator; - typedef typename hash_map<void*, timer_base*>::value_type value_type; + typedef typename hash_map<void*, timer>::iterator iterator; + typedef typename hash_map<void*, timer>::value_type value_type; std::pair<iterator, bool> result = - timers_.insert(value_type(token, new_timer.get())); - if (!result.second) + timers_.insert(value_type(token, timer())); + result.first->second.op_queue_.push(op); + if (result.second) { - result.first->second->prev_ = new_timer.get(); - new_timer.get()->next_ = result.first->second; - result.first->second = new_timer.get(); + // Put the new timer at the correct position in the heap. + result.first->second.time_ = time; + result.first->second.heap_index_ = heap_.size(); + result.first->second.token_ = token; + heap_.push_back(&result.first->second); + up_heap(heap_.size() - 1); } - // Put the timer at the correct position in the heap. - new_timer.get()->heap_index_ = heap_.size(); - heap_.push_back(new_timer.get()); - up_heap(heap_.size() - 1); - bool is_first = (heap_[0] == new_timer.get()); - - // Ownership of the timer is transferred to the timer queue. - new_timer.release(); - - return is_first; + return (heap_[0] == &result.first->second); } // Whether there are no timers in the queue. @@ -103,226 +88,106 @@ public: } // Get the time for the timer that is earliest in the queue. - virtual boost::posix_time::time_duration wait_duration() const + virtual long wait_duration_msec(long max_duration) const { if (heap_.empty()) - return boost::posix_time::pos_infin; - return Time_Traits::to_posix_duration( + return max_duration; + + boost::posix_time::time_duration duration = Time_Traits::to_posix_duration( Time_Traits::subtract(heap_[0]->time_, Time_Traits::now())); - } - // Dispatch the timers that are earlier than the specified time. - virtual void dispatch_timers() - { - const time_type now = Time_Traits::now(); - while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0]->time_)) - { - timer_base* t = heap_[0]; - remove_timer(t); - t->result_ = boost::system::error_code(); - t->prev_ = 0; - t->next_ = complete_timers_; - complete_timers_ = t; - } - } + if (duration > boost::posix_time::milliseconds(max_duration)) + duration = boost::posix_time::milliseconds(max_duration); + else if (duration < boost::posix_time::milliseconds(0)) + duration = boost::posix_time::milliseconds(0); - // Cancel the timers with the given token. Any timers pending for the token - // will be notified that they have been cancelled next time - // dispatch_cancellations is called. Returns the number of timers that were - // cancelled. - std::size_t cancel_timer(void* timer_token) - { - std::size_t num_cancelled = 0; - typedef typename hash_map<void*, timer_base*>::iterator iterator; - iterator it = timers_.find(timer_token); - if (it != timers_.end()) - { - timer_base* t = it->second; - while (t) - { - timer_base* next = t->next_; - remove_timer(t); - t->prev_ = 0; - t->next_ = cancelled_timers_; - cancelled_timers_ = t; - t = next; - ++num_cancelled; - } - } - return num_cancelled; + return duration.total_milliseconds(); } - // Dispatch any pending cancels for timers. - virtual void dispatch_cancellations() + // Get the time for the timer that is earliest in the queue. + virtual long wait_duration_usec(long max_duration) const { - while (cancelled_timers_) - { - timer_base* this_timer = cancelled_timers_; - this_timer->result_ = boost::asio::error::operation_aborted; - cancelled_timers_ = this_timer->next_; - this_timer->next_ = complete_timers_; - complete_timers_ = this_timer; - } + if (heap_.empty()) + return max_duration; + + boost::posix_time::time_duration duration = Time_Traits::to_posix_duration( + Time_Traits::subtract(heap_[0]->time_, Time_Traits::now())); + + if (duration > boost::posix_time::microseconds(max_duration)) + duration = boost::posix_time::microseconds(max_duration); + else if (duration < boost::posix_time::microseconds(0)) + duration = boost::posix_time::microseconds(0); + + return duration.total_microseconds(); } - // Complete any timers that are waiting to be completed. - virtual void complete_timers() + // Dequeue all timers not later than the current time. + virtual void get_ready_timers(op_queue<operation>& ops) { - while (complete_timers_) + const time_type now = Time_Traits::now(); + while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0]->time_)) { - timer_base* this_timer = complete_timers_; - complete_timers_ = this_timer->next_; - this_timer->next_ = 0; - this_timer->complete(); + timer* t = heap_[0]; + ops.push(t->op_queue_); + remove_timer(t); } } - // Destroy all timers. - virtual void destroy_timers() + // Dequeue all timers. + virtual void get_all_timers(op_queue<operation>& ops) { - typename hash_map<void*, timer_base*>::iterator i = timers_.begin(); - typename hash_map<void*, timer_base*>::iterator end = timers_.end(); + typename hash_map<void*, timer>::iterator i = timers_.begin(); + typename hash_map<void*, timer>::iterator end = timers_.end(); while (i != end) { - timer_base* t = i->second; - typename hash_map<void*, timer_base*>::iterator old_i = i++; + ops.push(i->second.op_queue_); + typename hash_map<void*, timer>::iterator old_i = i++; timers_.erase(old_i); - destroy_timer_list(t); } + heap_.clear(); timers_.clear(); - destroy_timer_list(cancelled_timers_); - destroy_timer_list(complete_timers_); } -private: - // Base class for timer operations. Function pointers are used instead of - // virtual functions to avoid the associated overhead. - class timer_base + // Cancel and dequeue the timers with the given token. + std::size_t cancel_timer(void* timer_token, op_queue<operation>& ops) { - public: - // Delete the timer and post the handler. - void complete() - { - complete_func_(this, result_); - } - - // Delete the timer. - void destroy() - { - destroy_func_(this); - } - - protected: - typedef void (*complete_func_type)(timer_base*, - const boost::system::error_code&); - typedef void (*destroy_func_type)(timer_base*); - - // Constructor. - timer_base(complete_func_type complete_func, destroy_func_type destroy_func, - const time_type& time, void* token) - : complete_func_(complete_func), - destroy_func_(destroy_func), - time_(time), - token_(token), - next_(0), - prev_(0), - heap_index_( - std::numeric_limits<size_t>::max BOOST_PREVENT_MACRO_SUBSTITUTION()) - { - } - - // Prevent deletion through this type. - ~timer_base() + std::size_t num_cancelled = 0; + typedef typename hash_map<void*, timer>::iterator iterator; + iterator it = timers_.find(timer_token); + if (it != timers_.end()) { + while (timer_op* op = it->second.op_queue_.front()) + { + op->ec_ = boost::asio::error::operation_aborted; + it->second.op_queue_.pop(); + ops.push(op); + ++num_cancelled; + } + remove_timer(&it->second); } + return num_cancelled; + } - private: - friend class timer_queue<Time_Traits>; - - // The function to be called to delete the timer and post the handler. - complete_func_type complete_func_; - - // The function to be called to delete the timer. - destroy_func_type destroy_func_; - - // The result of the timer operation. - boost::system::error_code result_; +private: + // Structure representing a single outstanding timer. + struct timer + { + timer() {} + timer(const timer&) {} + void operator=(const timer&) {} // The time when the timer should fire. time_type time_; - // The token associated with the timer. - void* token_; - - // The next timer known to the queue. - timer_base* next_; - - // The previous timer known to the queue. - timer_base* prev_; + // The operations waiting on the timer. + op_queue<timer_op> op_queue_; // The index of the timer in the heap. size_t heap_index_; - }; - // Adaptor class template for using handlers in timers. - template <typename Handler> - class timer - : public timer_base - { - public: - // Constructor. - timer(const time_type& time, Handler handler, void* token) - : timer_base(&timer<Handler>::complete_handler, - &timer<Handler>::destroy_handler, time, token), - handler_(handler) - { - } - - // Delete the timer and post the handler. - static void complete_handler(timer_base* base, - const boost::system::error_code& result) - { - // Take ownership of the timer object. - typedef timer<Handler> this_type; - this_type* this_timer(static_cast<this_type*>(base)); - typedef handler_alloc_traits<Handler, this_type> alloc_traits; - handler_ptr<alloc_traits> ptr(this_timer->handler_, this_timer); - - // Make a copy of the error_code and the handler so that the memory can - // be deallocated before the upcall is made. - boost::system::error_code ec(result); - Handler handler(this_timer->handler_); - - // Free the memory associated with the handler. - ptr.reset(); - - // Make the upcall. - handler(ec); - } - - // Delete the timer. - static void destroy_handler(timer_base* base) - { - // Take ownership of the timer object. - typedef timer<Handler> this_type; - this_type* this_timer(static_cast<this_type*>(base)); - typedef handler_alloc_traits<Handler, this_type> alloc_traits; - handler_ptr<alloc_traits> ptr(this_timer->handler_, this_timer); - - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(this_timer->handler_); - (void)handler; - - // Free the memory associated with the handler. - ptr.reset(); - } - - private: - Handler handler_; + // The token associated with the timer. + void* token_; }; // Move the item at the given index up the heap to its correct position. @@ -359,7 +224,7 @@ private: // Swap two entries in the heap. void swap_heap(size_t index1, size_t index2) { - timer_base* tmp = heap_[index1]; + timer* tmp = heap_[index1]; heap_[index1] = heap_[index2]; heap_[index2] = tmp; heap_[index1]->heap_index_ = index1; @@ -367,7 +232,7 @@ private: } // Remove a timer from the heap and list of timers. - void remove_timer(timer_base* t) + void remove_timer(timer* t) { // Remove the timer from the heap. size_t index = t->heap_index_; @@ -391,44 +256,17 @@ private: } // Remove the timer from the hash. - typedef typename hash_map<void*, timer_base*>::iterator iterator; + typedef typename hash_map<void*, timer>::iterator iterator; iterator it = timers_.find(t->token_); if (it != timers_.end()) - { - if (it->second == t) - it->second = t->next_; - if (t->prev_) - t->prev_->next_ = t->next_; - if (t->next_) - t->next_->prev_ = t->prev_; - if (it->second == 0) - timers_.erase(it); - } - } - - // Destroy all timers in a linked list. - void destroy_timer_list(timer_base*& t) - { - while (t) - { - timer_base* next = t->next_; - t->next_ = 0; - t->destroy(); - t = next; - } + timers_.erase(it); } // A hash of timer token to linked lists of timers. - hash_map<void*, timer_base*> timers_; + hash_map<void*, timer> timers_; // The heap of timers, with the earliest timer at the front. - std::vector<timer_base*> heap_; - - // The list of timers to be cancelled. - timer_base* cancelled_timers_; - - // The list of timers waiting to be completed. - timer_base* complete_timers_; + std::vector<timer*> heap_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/timer_queue_base.hpp b/3rdParty/Boost/src/boost/asio/detail/timer_queue_base.hpp index cd6a0d3..074c2e1 100644 --- a/3rdParty/Boost/src/boost/asio/detail/timer_queue_base.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/timer_queue_base.hpp @@ -17,13 +17,9 @@ #include <boost/asio/detail/push_options.hpp> -#include <boost/asio/detail/socket_types.hpp> // Must come before posix_time. - -#include <boost/asio/detail/push_options.hpp> -#include <boost/date_time/posix_time/posix_time_types.hpp> -#include <boost/asio/detail/pop_options.hpp> - #include <boost/asio/detail/noncopyable.hpp> +#include <boost/asio/detail/op_queue.hpp> +#include <boost/asio/detail/operation.hpp> namespace boost { namespace asio { @@ -33,6 +29,9 @@ class timer_queue_base : private noncopyable { public: + // Constructor. + timer_queue_base() : next_(0) {} + // Destructor. virtual ~timer_queue_base() {} @@ -40,19 +39,22 @@ public: virtual bool empty() const = 0; // Get the time to wait until the next timer. - virtual boost::posix_time::time_duration wait_duration() const = 0; + virtual long wait_duration_msec(long max_duration) const = 0; + + // Get the time to wait until the next timer. + virtual long wait_duration_usec(long max_duration) const = 0; - // Dispatch all ready timers. - virtual void dispatch_timers() = 0; + // Dequeue all ready timers. + virtual void get_ready_timers(op_queue<operation>& ops) = 0; - // Dispatch any pending cancels for timers. - virtual void dispatch_cancellations() = 0; + // Dequeue all timers. + virtual void get_all_timers(op_queue<operation>& ops) = 0; - // Complete all timers that are waiting to be completed. - virtual void complete_timers() = 0; +private: + friend class timer_queue_set; - // Destroy all timers. - virtual void destroy_timers() = 0; + // Next timer queue in the set. + timer_queue_base* next_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/timer_queue_fwd.hpp b/3rdParty/Boost/src/boost/asio/detail/timer_queue_fwd.hpp new file mode 100644 index 0000000..b8bc5f2 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/timer_queue_fwd.hpp @@ -0,0 +1,33 @@ +// +// timer_queue_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_TIMER_QUEUE_FWD_HPP +#define BOOST_ASIO_DETAIL_TIMER_QUEUE_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename Time_Traits> +class timer_queue; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_TIMER_QUEUE_FWD_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/timer_queue_set.hpp b/3rdParty/Boost/src/boost/asio/detail/timer_queue_set.hpp new file mode 100644 index 0000000..a92a6b9 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/timer_queue_set.hpp @@ -0,0 +1,117 @@ +// +// timer_queue_set.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_TIMER_QUEUE_SET_HPP +#define BOOST_ASIO_DETAIL_TIMER_QUEUE_SET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/timer_queue_base.hpp> + +namespace boost { +namespace asio { +namespace detail { + +class timer_queue_set +{ +public: + // Constructor. + timer_queue_set() + : first_(0) + { + } + + // Add a timer queue to the set. + void insert(timer_queue_base* q) + { + q->next_ = first_; + first_ = q; + } + + // Remove a timer queue from the set. + void erase(timer_queue_base* q) + { + if (first_) + { + if (q == first_) + { + first_ = q->next_; + q->next_ = 0; + return; + } + + for (timer_queue_base* p = first_; p->next_; p = p->next_) + { + if (p->next_ == q) + { + p->next_ = q->next_; + q->next_ = 0; + return; + } + } + } + } + + // Determine whether all queues are empty. + bool all_empty() const + { + for (timer_queue_base* p = first_; p; p = p->next_) + if (!p->empty()) + return false; + return true; + } + + // Get the wait duration in milliseconds. + long wait_duration_msec(long max_duration) const + { + long min_duration = max_duration; + for (timer_queue_base* p = first_; p; p = p->next_) + min_duration = p->wait_duration_msec(min_duration); + return min_duration; + } + + // Get the wait duration in microseconds. + long wait_duration_usec(long max_duration) const + { + long min_duration = max_duration; + for (timer_queue_base* p = first_; p; p = p->next_) + min_duration = p->wait_duration_usec(min_duration); + return min_duration; + } + + // Dequeue all ready timers. + void get_ready_timers(op_queue<operation>& ops) + { + for (timer_queue_base* p = first_; p; p = p->next_) + p->get_ready_timers(ops); + } + + // Dequeue all timers. + void get_all_timers(op_queue<operation>& ops) + { + for (timer_queue_base* p = first_; p; p = p->next_) + p->get_all_timers(ops); + } + +private: + timer_queue_base* first_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_TIMER_QUEUE_SET_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/timer_scheduler.hpp b/3rdParty/Boost/src/boost/asio/detail/timer_scheduler.hpp new file mode 100644 index 0000000..bc837ee --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/timer_scheduler.hpp @@ -0,0 +1,36 @@ +// +// timer_scheduler.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_TIMER_SCHEDULER_HPP +#define BOOST_ASIO_DETAIL_TIMER_SCHEDULER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/timer_scheduler_fwd.hpp> + +#if defined(BOOST_ASIO_HAS_IOCP) +# include <boost/asio/detail/win_iocp_io_service.hpp> +#elif defined(BOOST_ASIO_HAS_EPOLL) +# include <boost/asio/detail/epoll_reactor.hpp> +#elif defined(BOOST_ASIO_HAS_KQUEUE) +# include <boost/asio/detail/kqueue_reactor.hpp> +#elif defined(BOOST_ASIO_HAS_DEV_POLL) +# include <boost/asio/detail/dev_poll_reactor.hpp> +#else +# include <boost/asio/detail/select_reactor.hpp> +#endif + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_TIMER_SCHEDULER_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/timer_scheduler_fwd.hpp b/3rdParty/Boost/src/boost/asio/detail/timer_scheduler_fwd.hpp new file mode 100644 index 0000000..9d0b1e6 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/timer_scheduler_fwd.hpp @@ -0,0 +1,48 @@ +// +// timer_scheduler_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP +#define BOOST_ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/dev_poll_reactor_fwd.hpp> +#include <boost/asio/detail/epoll_reactor_fwd.hpp> +#include <boost/asio/detail/kqueue_reactor_fwd.hpp> +#include <boost/asio/detail/select_reactor_fwd.hpp> +#include <boost/asio/detail/win_iocp_io_service_fwd.hpp> + +namespace boost { +namespace asio { +namespace detail { + +#if defined(BOOST_ASIO_HAS_IOCP) +typedef win_iocp_io_service timer_scheduler; +#elif defined(BOOST_ASIO_HAS_EPOLL) +typedef epoll_reactor timer_scheduler; +#elif defined(BOOST_ASIO_HAS_KQUEUE) +typedef kqueue_reactor timer_scheduler; +#elif defined(BOOST_ASIO_HAS_DEV_POLL) +typedef dev_poll_reactor timer_scheduler; +#else +typedef select_reactor<false> timer_scheduler; +#endif + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/tss_ptr.hpp b/3rdParty/Boost/src/boost/asio/detail/tss_ptr.hpp index 9af4965..7ccfcf3 100644 --- a/3rdParty/Boost/src/boost/asio/detail/tss_ptr.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/tss_ptr.hpp @@ -21,7 +21,7 @@ #include <boost/config.hpp> #include <boost/asio/detail/pop_options.hpp> -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) # include <boost/asio/detail/null_tss_ptr.hpp> #elif defined(BOOST_WINDOWS) # include <boost/asio/detail/win_tss_ptr.hpp> @@ -37,7 +37,7 @@ namespace detail { template <typename T> class tss_ptr -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) : public null_tss_ptr<T> #elif defined(BOOST_WINDOWS) : public win_tss_ptr<T> @@ -48,7 +48,7 @@ class tss_ptr public: void operator=(T* value) { -#if !defined(BOOST_HAS_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(BOOST_ASIO_DISABLE_THREADS) null_tss_ptr<T>::operator=(value); #elif defined(BOOST_WINDOWS) win_tss_ptr<T>::operator=(value); diff --git a/3rdParty/Boost/src/boost/asio/detail/win_event.hpp b/3rdParty/Boost/src/boost/asio/detail/win_event.hpp index 637b580..bddf09d 100644 --- a/3rdParty/Boost/src/boost/asio/detail/win_event.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/win_event.hpp @@ -71,6 +71,15 @@ public: ::SetEvent(event_); } + // Signal the event and unlock the mutex. + template <typename Lock> + void signal_and_unlock(Lock& lock) + { + BOOST_ASSERT(lock.locked()); + lock.unlock(); + ::SetEvent(event_); + } + // Reset the event. template <typename Lock> void clear(Lock& lock) diff --git a/3rdParty/Boost/src/boost/asio/detail/win_fenced_block.hpp b/3rdParty/Boost/src/boost/asio/detail/win_fenced_block.hpp new file mode 100644 index 0000000..ff4a21c --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/win_fenced_block.hpp @@ -0,0 +1,77 @@ +// +// win_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_WIN_FENCED_BLOCK_HPP +#define BOOST_ASIO_DETAIL_WIN_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/push_options.hpp> +#include <boost/config.hpp> +#include <boost/asio/detail/pop_options.hpp> + +#if defined(BOOST_WINDOWS) && !defined(UNDER_CE) + +#include <boost/asio/detail/socket_types.hpp> + +namespace boost { +namespace asio { +namespace detail { + +class win_fenced_block + : private noncopyable +{ +public: + // Constructor. + win_fenced_block() + { +#if defined(BOOST_MSVC) && (BOOST_MSVC < 1400) +# if defined(_M_IX86) +# pragma warning(push) +# pragma warning(disable:4793) + LONG barrier; + __asm { xchg barrier, eax } +# pragma warning(pop) +# endif // defined(_M_IX86) +#else // defined(BOOST_MSVC) && (BOOST_MSVC < 1400) + MemoryBarrier(); +#endif // defined(BOOST_MSVC) && (BOOST_MSVC < 1400) + } + + // Destructor. + ~win_fenced_block() + { +#if defined(BOOST_MSVC) && (BOOST_MSVC < 1400) +# if defined(_M_IX86) +# pragma warning(push) +# pragma warning(disable:4793) + LONG barrier; + __asm { xchg barrier, eax } +# pragma warning(pop) +# endif // defined(_M_IX86) +#else // defined(BOOST_MSVC) && (BOOST_MSVC < 1400) + MemoryBarrier(); +#endif // defined(BOOST_MSVC) && (BOOST_MSVC < 1400) + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE) + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_WIN_FENCED_BLOCK_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/win_iocp_handle_service.hpp b/3rdParty/Boost/src/boost/asio/detail/win_iocp_handle_service.hpp index 6998d57..a6d6599 100644 --- a/3rdParty/Boost/src/boost/asio/detail/win_iocp_handle_service.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/win_iocp_handle_service.hpp @@ -26,13 +26,14 @@ #include <boost/cstdint.hpp> #include <boost/asio/detail/pop_options.hpp> -#include <boost/asio/buffer.hpp> #include <boost/asio/error.hpp> #include <boost/asio/io_service.hpp> #include <boost/asio/detail/bind_handler.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> #include <boost/asio/detail/handler_alloc_helpers.hpp> #include <boost/asio/detail/handler_invoke_helpers.hpp> #include <boost/asio/detail/mutex.hpp> +#include <boost/asio/detail/operation.hpp> #include <boost/asio/detail/win_iocp_io_service.hpp> namespace boost { @@ -40,12 +41,8 @@ namespace asio { namespace detail { class win_iocp_handle_service - : public boost::asio::detail::service_base<win_iocp_handle_service> { public: - // Base class for all operations. - typedef win_iocp_io_service::operation operation; - // The native type of a stream handle. typedef HANDLE native_type; @@ -81,8 +78,7 @@ public: }; win_iocp_handle_service(boost::asio::io_service& io_service) - : boost::asio::detail::service_base<win_iocp_handle_service>(io_service), - iocp_service_(boost::asio::use_service<win_iocp_io_service>(io_service)), + : iocp_service_(boost::asio::use_service<win_iocp_io_service>(io_service)), mutex_(), impl_list_(0) { @@ -307,16 +303,9 @@ public: return 0; } - // Find first buffer of non-zero length. - boost::asio::const_buffer buffer; - typename ConstBufferSequence::const_iterator iter = buffers.begin(); - typename ConstBufferSequence::const_iterator end = buffers.end(); - for (DWORD i = 0; iter != end; ++iter, ++i) - { - buffer = boost::asio::const_buffer(*iter); - if (boost::asio::buffer_size(buffer) != 0) - break; - } + boost::asio::const_buffer buffer = + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence>::first(buffers); // A request to write 0 bytes on a handle is a no-op. if (boost::asio::buffer_size(buffer) == 0) @@ -365,79 +354,48 @@ public: } template <typename ConstBufferSequence, typename Handler> - class write_operation - : public operation + class write_op : public operation { public: - write_operation(win_iocp_io_service& io_service, - const ConstBufferSequence& buffers, Handler handler) - : operation(io_service, - &write_operation<ConstBufferSequence, Handler>::do_completion_impl, - &write_operation<ConstBufferSequence, Handler>::destroy_impl), - work_(io_service.get_io_service()), + write_op(const ConstBufferSequence& buffers, Handler handler) + : operation(&write_op::do_complete), buffers_(buffers), handler_(handler) { } - private: - static void do_completion_impl(operation* op, - DWORD last_error, size_t bytes_transferred) + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. - typedef write_operation<ConstBufferSequence, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); + write_op* o(static_cast<write_op*>(base)); + typedef handler_alloc_traits<Handler, write_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); -#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) - // Check whether buffers are still valid. - typename ConstBufferSequence::const_iterator iter - = handler_op->buffers_.begin(); - typename ConstBufferSequence::const_iterator end - = handler_op->buffers_.end(); - while (iter != end) + // Make the upcall if required. + if (owner) { - boost::asio::const_buffer buffer(*iter); - boost::asio::buffer_cast<const char*>(buffer); - ++iter; - } +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence>::validate(o->buffers_); #endif // defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) - // Make a copy of the handler so that the memory can be deallocated before - // the upcall is made. - Handler handler(handler_op->handler_); - - // Free the memory associated with the handler. - ptr.reset(); - - // Call the handler. - boost::system::error_code ec(last_error, - boost::asio::error::get_system_category()); - boost_asio_handler_invoke_helpers::invoke( - bind_handler(handler, ec, bytes_transferred), handler); - } - - static void destroy_impl(operation* op) - { - // Take ownership of the operation object. - typedef write_operation<ConstBufferSequence, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); - - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(handler_op->handler_); - (void)handler; - - // Free the memory associated with the handler. - ptr.reset(); + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } - boost::asio::io_service::work work_; + private: ConstBufferSequence buffers_; Handler handler_; }; @@ -457,65 +415,16 @@ public: void async_write_some_at(implementation_type& impl, boost::uint64_t offset, const ConstBufferSequence& buffers, Handler handler) { - // Update the ID of the thread from which cancellation is safe. - if (impl.safe_cancellation_thread_id_ == 0) - impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); - else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) - impl.safe_cancellation_thread_id_ = ~DWORD(0); - // Allocate and construct an operation to wrap the handler. - typedef write_operation<ConstBufferSequence, Handler> value_type; + typedef write_op<ConstBufferSequence, Handler> value_type; typedef handler_alloc_traits<Handler, value_type> alloc_traits; raw_handler_ptr<alloc_traits> raw_ptr(handler); - handler_ptr<alloc_traits> ptr(raw_ptr, iocp_service_, buffers, handler); - - if (!is_open(impl)) - { - ptr.get()->on_immediate_completion(WSAEBADF, 0); - ptr.release(); - return; - } - - // Find first buffer of non-zero length. - boost::asio::const_buffer buffer; - typename ConstBufferSequence::const_iterator iter = buffers.begin(); - typename ConstBufferSequence::const_iterator end = buffers.end(); - for (DWORD i = 0; iter != end; ++iter, ++i) - { - buffer = boost::asio::const_buffer(*iter); - if (boost::asio::buffer_size(buffer) != 0) - break; - } - - // A request to write 0 bytes on a handle is a no-op. - if (boost::asio::buffer_size(buffer) == 0) - { - ptr.get()->on_immediate_completion(0, 0); - ptr.release(); - return; - } - - // Write the data. - DWORD bytes_transferred = 0; - ptr.get()->Offset = offset & 0xFFFFFFFF; - ptr.get()->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; - BOOL ok = ::WriteFile(impl.handle_, - boost::asio::buffer_cast<LPCVOID>(buffer), - static_cast<DWORD>(boost::asio::buffer_size(buffer)), - &bytes_transferred, ptr.get()); - DWORD last_error = ::GetLastError(); + handler_ptr<alloc_traits> ptr(raw_ptr, buffers, handler); - // Check if the operation completed immediately. - if (!ok && last_error != ERROR_IO_PENDING) - { - ptr.get()->on_immediate_completion(last_error, bytes_transferred); - ptr.release(); - } - else - { - ptr.get()->on_pending(); - ptr.release(); - } + start_write_op(impl, offset, + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence>::first(buffers), ptr.get()); + ptr.release(); } // Read some data. Returns the number of bytes received. @@ -537,16 +446,9 @@ public: return 0; } - // Find first buffer of non-zero length. - boost::asio::mutable_buffer buffer; - typename MutableBufferSequence::const_iterator iter = buffers.begin(); - typename MutableBufferSequence::const_iterator end = buffers.end(); - for (DWORD i = 0; iter != end; ++iter, ++i) - { - buffer = boost::asio::mutable_buffer(*iter); - if (boost::asio::buffer_size(buffer) != 0) - break; - } + boost::asio::mutable_buffer buffer = + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence>::first(buffers); // A request to read 0 bytes on a stream handle is a no-op. if (boost::asio::buffer_size(buffer) == 0) @@ -609,89 +511,54 @@ public: } template <typename MutableBufferSequence, typename Handler> - class read_operation - : public operation + class read_op : public operation { public: - read_operation(win_iocp_io_service& io_service, - const MutableBufferSequence& buffers, Handler handler) - : operation(io_service, - &read_operation< - MutableBufferSequence, Handler>::do_completion_impl, - &read_operation< - MutableBufferSequence, Handler>::destroy_impl), - work_(io_service.get_io_service()), + read_op(const MutableBufferSequence& buffers, Handler handler) + : operation(&read_op::do_complete), buffers_(buffers), handler_(handler) { } - private: - static void do_completion_impl(operation* op, - DWORD last_error, size_t bytes_transferred) + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. - typedef read_operation<MutableBufferSequence, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); + read_op* o(static_cast<read_op*>(base)); + typedef handler_alloc_traits<Handler, read_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); -#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) - // Check whether buffers are still valid. - typename MutableBufferSequence::const_iterator iter - = handler_op->buffers_.begin(); - typename MutableBufferSequence::const_iterator end - = handler_op->buffers_.end(); - while (iter != end) + // Make the upcall if required. + if (owner) { - boost::asio::mutable_buffer buffer(*iter); - boost::asio::buffer_cast<char*>(buffer); - ++iter; - } +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence>::validate(o->buffers_); #endif // defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) - // Check for the end-of-file condition. - boost::system::error_code ec(last_error, - boost::asio::error::get_system_category()); - if (!ec && bytes_transferred == 0 || last_error == ERROR_HANDLE_EOF) - { - ec = boost::asio::error::eof; - } - - // Make a copy of the handler so that the memory can be deallocated before - // the upcall is made. - Handler handler(handler_op->handler_); - - // Free the memory associated with the handler. - ptr.reset(); - - // Call the handler. - boost_asio_handler_invoke_helpers::invoke( - bind_handler(handler, ec, bytes_transferred), handler); - } - - static void destroy_impl(operation* op) - { - // Take ownership of the operation object. - typedef read_operation<MutableBufferSequence, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef boost::asio::detail::handler_alloc_traits< - Handler, op_type> alloc_traits; - boost::asio::detail::handler_ptr<alloc_traits> ptr( - handler_op->handler_, handler_op); - - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(handler_op->handler_); - (void)handler; + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_HANDLE_EOF) + { + ec = boost::asio::error::eof; + } - // Free the memory associated with the handler. - ptr.reset(); + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } - boost::asio::io_service::work work_; + private: MutableBufferSequence buffers_; Handler handler_; }; @@ -712,63 +579,16 @@ public: void async_read_some_at(implementation_type& impl, boost::uint64_t offset, const MutableBufferSequence& buffers, Handler handler) { - // Update the ID of the thread from which cancellation is safe. - if (impl.safe_cancellation_thread_id_ == 0) - impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); - else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) - impl.safe_cancellation_thread_id_ = ~DWORD(0); - // Allocate and construct an operation to wrap the handler. - typedef read_operation<MutableBufferSequence, Handler> value_type; + typedef read_op<MutableBufferSequence, Handler> value_type; typedef handler_alloc_traits<Handler, value_type> alloc_traits; raw_handler_ptr<alloc_traits> raw_ptr(handler); - handler_ptr<alloc_traits> ptr(raw_ptr, iocp_service_, buffers, handler); + handler_ptr<alloc_traits> ptr(raw_ptr, buffers, handler); - if (!is_open(impl)) - { - ptr.get()->on_immediate_completion(WSAEBADF, 0); - ptr.release(); - return; - } - - // Find first buffer of non-zero length. - boost::asio::mutable_buffer buffer; - typename MutableBufferSequence::const_iterator iter = buffers.begin(); - typename MutableBufferSequence::const_iterator end = buffers.end(); - for (DWORD i = 0; iter != end; ++iter, ++i) - { - buffer = boost::asio::mutable_buffer(*iter); - if (boost::asio::buffer_size(buffer) != 0) - break; - } - - // A request to receive 0 bytes on a stream handle is a no-op. - if (boost::asio::buffer_size(buffer) == 0) - { - ptr.get()->on_immediate_completion(0, 0); - ptr.release(); - return; - } - - // Read some data. - DWORD bytes_transferred = 0; - ptr.get()->Offset = offset & 0xFFFFFFFF; - ptr.get()->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; - BOOL ok = ::ReadFile(impl.handle_, - boost::asio::buffer_cast<LPVOID>(buffer), - static_cast<DWORD>(boost::asio::buffer_size(buffer)), - &bytes_transferred, ptr.get()); - DWORD last_error = ::GetLastError(); - if (!ok && last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA) - { - ptr.get()->on_immediate_completion(last_error, bytes_transferred); - ptr.release(); - } - else - { - ptr.get()->on_pending(); - ptr.release(); - } + start_read_op(impl, offset, + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence>::first(buffers), ptr.get()); + ptr.release(); } private: @@ -794,6 +614,95 @@ private: void async_read_some_at(implementation_type& impl, boost::uint64_t offset, const null_buffers& buffers, Handler handler); + // Helper function to start a write operation. + void start_write_op(implementation_type& impl, boost::uint64_t offset, + const boost::asio::const_buffer& buffer, operation* op) + { + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + { + iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); + } + else if (boost::asio::buffer_size(buffer) == 0) + { + // A request to write 0 bytes on a handle is a no-op. + iocp_service_.on_completion(op); + } + else + { + DWORD bytes_transferred = 0; + op->Offset = offset & 0xFFFFFFFF; + op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::WriteFile(impl.handle_, + boost::asio::buffer_cast<LPCVOID>(buffer), + static_cast<DWORD>(boost::asio::buffer_size(buffer)), + &bytes_transferred, op); + DWORD last_error = ::GetLastError(); + if (!ok && last_error != ERROR_IO_PENDING + && last_error != ERROR_MORE_DATA) + { + iocp_service_.on_completion(op, last_error, bytes_transferred); + } + else + { + iocp_service_.on_pending(op); + } + } + } + + // Helper function to start a read operation. + void start_read_op(implementation_type& impl, boost::uint64_t offset, + const boost::asio::mutable_buffer& buffer, operation* op) + { + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + { + iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); + } + else if (boost::asio::buffer_size(buffer) == 0) + { + // A request to read 0 bytes on a handle is a no-op. + iocp_service_.on_completion(op); + } + else + { + DWORD bytes_transferred = 0; + op->Offset = offset & 0xFFFFFFFF; + op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::ReadFile(impl.handle_, + boost::asio::buffer_cast<LPVOID>(buffer), + static_cast<DWORD>(boost::asio::buffer_size(buffer)), + &bytes_transferred, op); + DWORD last_error = ::GetLastError(); + if (!ok && last_error != ERROR_IO_PENDING + && last_error != ERROR_MORE_DATA) + { + iocp_service_.on_completion(op, last_error, bytes_transferred); + } + else + { + iocp_service_.on_pending(op); + } + } + } + + // Update the ID of the thread from which cancellation is safe. + void update_cancellation_thread_id(implementation_type& impl) + { +#if defined(BOOST_ASIO_ENABLE_CANCELIO) + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); +#else // defined(BOOST_ASIO_ENABLE_CANCELIO) + (void)impl; +#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) + } + // Helper function to close a handle when the associated object is being // destroyed. void close_for_destruction(implementation_type& impl) diff --git a/3rdParty/Boost/src/boost/asio/detail/win_iocp_io_service.hpp b/3rdParty/Boost/src/boost/asio/detail/win_iocp_io_service.hpp index 1dfb9a1..83b16bf 100644 --- a/3rdParty/Boost/src/boost/asio/detail/win_iocp_io_service.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/win_iocp_io_service.hpp @@ -29,122 +29,37 @@ #include <boost/asio/io_service.hpp> #include <boost/asio/detail/call_stack.hpp> +#include <boost/asio/detail/completion_handler.hpp> +#include <boost/asio/detail/fenced_block.hpp> #include <boost/asio/detail/handler_alloc_helpers.hpp> #include <boost/asio/detail/handler_invoke_helpers.hpp> +#include <boost/asio/detail/mutex.hpp> +#include <boost/asio/detail/op_queue.hpp> #include <boost/asio/detail/service_base.hpp> #include <boost/asio/detail/socket_types.hpp> -#include <boost/asio/detail/timer_queue.hpp> -#include <boost/asio/detail/mutex.hpp> +#include <boost/asio/detail/timer_op.hpp> +#include <boost/asio/detail/timer_queue_base.hpp> +#include <boost/asio/detail/timer_queue_fwd.hpp> +#include <boost/asio/detail/timer_queue_set.hpp> +#include <boost/asio/detail/win_iocp_operation.hpp> namespace boost { namespace asio { namespace detail { +class timer_op; + class win_iocp_io_service : public boost::asio::detail::service_base<win_iocp_io_service> { public: - // Base class for all operations. A function pointer is used instead of - // virtual functions to avoid the associated overhead. - // - // This class inherits from OVERLAPPED so that we can downcast to get back to - // the operation pointer from the LPOVERLAPPED out parameter of - // GetQueuedCompletionStatus. - class operation; - friend class operation; - class operation - : public OVERLAPPED - { - public: - typedef void (*invoke_func_type)(operation*, DWORD, size_t); - typedef void (*destroy_func_type)(operation*); - - operation(win_iocp_io_service& iocp_service, - invoke_func_type invoke_func, destroy_func_type destroy_func) - : iocp_service_(iocp_service), - ready_(0), - last_error_(~DWORD(0)), - bytes_transferred_(0), - invoke_func_(invoke_func), - destroy_func_(destroy_func) - { - Internal = 0; - InternalHigh = 0; - Offset = 0; - OffsetHigh = 0; - hEvent = 0; - - ::InterlockedIncrement(&iocp_service_.outstanding_operations_); - } - - void reset() - { - Internal = 0; - InternalHigh = 0; - Offset = 0; - OffsetHigh = 0; - hEvent = 0; - ready_ = 0; - last_error_ = ~DWORD(0); - bytes_transferred_ = 0; - } - - void on_pending() - { - if (::InterlockedCompareExchange(&ready_, 1, 0) == 1) - iocp_service_.post_completion(this, last_error_, bytes_transferred_); - } - - void on_immediate_completion(DWORD last_error, DWORD bytes_transferred) - { - ready_ = 1; - iocp_service_.post_completion(this, last_error, bytes_transferred); - } - - bool on_completion(DWORD last_error, DWORD bytes_transferred) - { - if (last_error_ == ~DWORD(0)) - { - last_error_ = last_error; - bytes_transferred_ = bytes_transferred; - } - - if (::InterlockedCompareExchange(&ready_, 1, 0) == 1) - { - invoke_func_(this, last_error_, bytes_transferred_); - return true; - } - - return false; - } - - void destroy() - { - destroy_func_(this); - } - - protected: - // Prevent deletion through this type. - ~operation() - { - ::InterlockedDecrement(&iocp_service_.outstanding_operations_); - } - - private: - win_iocp_io_service& iocp_service_; - long ready_; - DWORD last_error_; - DWORD bytes_transferred_; - invoke_func_type invoke_func_; - destroy_func_type destroy_func_; - }; + typedef win_iocp_operation operation; // Constructor. win_iocp_io_service(boost::asio::io_service& io_service) : boost::asio::detail::service_base<win_iocp_io_service>(io_service), iocp_(), outstanding_work_(0), - outstanding_operations_(0), stopped_(0), shutdown_(0), timer_thread_(0), @@ -172,24 +87,34 @@ public: { ::InterlockedExchange(&shutdown_, 1); - while (::InterlockedExchangeAdd(&outstanding_operations_, 0) > 0) + while (::InterlockedExchangeAdd(&outstanding_work_, 0) > 0) { - DWORD bytes_transferred = 0; -#if defined(WINVER) && (WINVER < 0x0500) - DWORD completion_key = 0; -#else - DWORD_PTR completion_key = 0; -#endif - LPOVERLAPPED overlapped = 0; - ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, - &completion_key, &overlapped, INFINITE); - if (overlapped) - static_cast<operation*>(overlapped)->destroy(); + op_queue<operation> ops; + timer_queues_.get_all_timers(ops); + ops.push(completed_ops_); + if (!ops.empty()) + { + while (operation* op = ops.front()) + { + ops.pop(); + ::InterlockedDecrement(&outstanding_work_); + op->destroy(); + } + } + else + { + DWORD bytes_transferred = 0; + dword_ptr_t completion_key = 0; + LPOVERLAPPED overlapped = 0; + ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, + &completion_key, &overlapped, max_timeout); + if (overlapped) + { + ::InterlockedDecrement(&outstanding_work_); + static_cast<operation*>(overlapped)->destroy(); + } + } } - - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - timer_queues_[i]->destroy_timers(); - timer_queues_.clear(); } // Initialise the task. Nothing to do here. @@ -219,6 +144,7 @@ public: { if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) { + stop(); ec = boost::system::error_code(); return 0; } @@ -237,6 +163,7 @@ public: { if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) { + stop(); ec = boost::system::error_code(); return 0; } @@ -251,6 +178,7 @@ public: { if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) { + stop(); ec = boost::system::error_code(); return 0; } @@ -269,6 +197,7 @@ public: { if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) { + stop(); ec = boost::system::error_code(); return 0; } @@ -319,7 +248,10 @@ public: void dispatch(Handler handler) { if (call_stack<win_iocp_io_service>::contains(this)) + { + boost::asio::detail::fenced_block b; boost_asio_handler_invoke_helpers::invoke(handler, handler); + } else post(handler); } @@ -328,37 +260,128 @@ public: template <typename Handler> void post(Handler handler) { - // If the service has been shut down we silently discard the handler. - if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) - return; - // Allocate and construct an operation to wrap the handler. - typedef handler_operation<Handler> value_type; + typedef completion_handler<Handler> value_type; typedef handler_alloc_traits<Handler, value_type> alloc_traits; raw_handler_ptr<alloc_traits> raw_ptr(handler); - handler_ptr<alloc_traits> ptr(raw_ptr, *this, handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); + + post_immediate_completion(ptr.get()); + ptr.release(); + } + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() has not yet been called for the operation. + void post_immediate_completion(operation* op) + { + work_started(); + post_deferred_completion(op); + } + + // Request invocation of the given operation and return immediately. Assumes + // that work_started() was previously called for the operation. + void post_deferred_completion(operation* op) + { + // Flag the operation as ready. + op->ready_ = 1; // Enqueue the operation on the I/O completion port. - ptr.get()->on_immediate_completion(0, 0); + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + boost::asio::detail::mutex::scoped_lock lock(timer_mutex_); + completed_ops_.push(op); + } + } - // Operation has been successfully posted. - ptr.release(); + // Request invocation of the given operation and return immediately. Assumes + // that work_started() was previously called for the operations. + void post_deferred_completions(op_queue<operation>& ops) + { + while (operation* op = ops.front()) + { + ops.pop(); + + // Flag the operation as ready. + op->ready_ = 1; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + boost::asio::detail::mutex::scoped_lock lock(timer_mutex_); + completed_ops_.push(op); + completed_ops_.push(ops); + } + } } - // Request invocation of the given OVERLAPPED-derived operation. - void post_completion(operation* op, DWORD op_last_error, - DWORD bytes_transferred) + // Called after starting an overlapped I/O operation that did not complete + // immediately. The caller must have already called work_started() prior to + // starting the operation. + void on_pending(operation* op) { + if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1) + { + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + boost::asio::detail::mutex::scoped_lock lock(timer_mutex_); + completed_ops_.push(op); + } + } + } + + // Called after starting an overlapped I/O operation that completed + // immediately. The caller must have already called work_started() prior to + // starting the operation. + void on_completion(operation* op, + DWORD last_error = 0, DWORD bytes_transferred = 0) + { + // Flag that the operation is ready for invocation. + op->ready_ = 1; + + // Store results in the OVERLAPPED structure. + op->Internal = reinterpret_cast<ulong_ptr_t>( + &boost::asio::error::get_system_category()); + op->Offset = last_error; + op->OffsetHigh = bytes_transferred; + // Enqueue the operation on the I/O completion port. if (!::PostQueuedCompletionStatus(iocp_.handle, - bytes_transferred, op_last_error, op)) + 0, overlapped_contains_result, op)) { - DWORD last_error = ::GetLastError(); - boost::system::system_error e( - boost::system::error_code(last_error, - boost::asio::error::get_system_category()), - "pqcs"); - boost::throw_exception(e); + // Out of resources. Put on completed queue instead. + boost::asio::detail::mutex::scoped_lock lock(timer_mutex_); + completed_ops_.push(op); + } + } + + // Called after starting an overlapped I/O operation that completed + // immediately. The caller must have already called work_started() prior to + // starting the operation. + void on_completion(operation* op, + const boost::system::error_code& ec, DWORD bytes_transferred = 0) + { + // Flag that the operation is ready for invocation. + op->ready_ = 1; + + // Store results in the OVERLAPPED structure. + op->Internal = reinterpret_cast<ulong_ptr_t>(&ec.category()); + op->Offset = ec.value(); + op->OffsetHigh = bytes_transferred; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + boost::asio::detail::mutex::scoped_lock lock(timer_mutex_); + completed_ops_.push(op); } } @@ -367,7 +390,7 @@ public: void add_timer_queue(timer_queue<Time_Traits>& timer_queue) { boost::asio::detail::mutex::scoped_lock lock(timer_mutex_); - timer_queues_.push_back(&timer_queue); + timer_queues_.insert(&timer_queue); } // Remove a timer queue from the service. @@ -375,36 +398,28 @@ public: void remove_timer_queue(timer_queue<Time_Traits>& timer_queue) { boost::asio::detail::mutex::scoped_lock lock(timer_mutex_); - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - if (timer_queues_[i] == &timer_queue) - { - timer_queues_.erase(timer_queues_.begin() + i); - return; - } - } + timer_queues_.erase(&timer_queue); } - // Schedule a timer in the given timer queue to expire at the specified - // absolute time. The handler object will be invoked when the timer expires. - template <typename Time_Traits, typename Handler> + // Schedule a new operation in the given timer queue to expire at the + // specified absolute time. + template <typename Time_Traits> void schedule_timer(timer_queue<Time_Traits>& timer_queue, - const typename Time_Traits::time_type& time, Handler handler, void* token) + const typename Time_Traits::time_type& time, timer_op* op, void* token) { // If the service has been shut down we silently discard the timer. if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) return; boost::asio::detail::mutex::scoped_lock lock(timer_mutex_); - if (timer_queue.enqueue_timer(time, handler, token)) + bool interrupt = timer_queue.enqueue_timer(time, op, token); + work_started(); + if (interrupt && !timer_interrupt_issued_) { - if (!timer_interrupt_issued_) - { - timer_interrupt_issued_ = true; - lock.unlock(); - ::PostQueuedCompletionStatus(iocp_.handle, - 0, steal_timer_dispatching, 0); - } + timer_interrupt_issued_ = true; + lock.unlock(); + ::PostQueuedCompletionStatus(iocp_.handle, + 0, steal_timer_dispatching, 0); } } @@ -418,7 +433,9 @@ public: return 0; boost::asio::detail::mutex::scoped_lock lock(timer_mutex_); - std::size_t n = timer_queue.cancel_timer(token); + op_queue<operation> ops; + std::size_t n = timer_queue.cancel_timer(token, ops); + post_deferred_completions(ops); if (n > 0 && !timer_interrupt_issued_) { timer_interrupt_issued_ = true; @@ -430,6 +447,14 @@ public: } private: +#if defined(WINVER) && (WINVER < 0x0500) + typedef DWORD dword_ptr_t; + typedef ULONG ulong_ptr_t; +#else // defined(WINVER) && (WINVER < 0x0500) + typedef DWORD_PTR dword_ptr_t; + typedef ULONG_PTR ulong_ptr_t; +#endif // defined(WINVER) && (WINVER < 0x0500) + // Dequeues at most one operation from the I/O completion port, and then // executes it. Returns the number of operations that were dequeued (i.e. // either 0 or 1). @@ -454,11 +479,7 @@ private: // Get the next operation from the queue. DWORD bytes_transferred = 0; -#if defined(WINVER) && (WINVER < 0x0500) - DWORD completion_key = 0; -#else - DWORD_PTR completion_key = 0; -#endif + dword_ptr_t completion_key = 0; LPOVERLAPPED overlapped = 0; ::SetLastError(0); BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, @@ -468,32 +489,11 @@ private: // Dispatch any pending timers. if (dispatching_timers) { - try - { - boost::asio::detail::mutex::scoped_lock lock(timer_mutex_); - if (!timer_queues_.empty()) - { - timer_queues_copy_ = timer_queues_; - for (std::size_t i = 0; i < timer_queues_copy_.size(); ++i) - { - timer_queues_copy_[i]->dispatch_timers(); - timer_queues_copy_[i]->dispatch_cancellations(); - timer_queues_copy_[i]->complete_timers(); - } - } - } - catch (...) - { - // Transfer responsibility for dispatching timers to another thread. - if (::InterlockedCompareExchange(&timer_thread_, - 0, this_thread_id) == this_thread_id) - { - ::PostQueuedCompletionStatus(iocp_.handle, - 0, transfer_timer_dispatching, 0); - } - - throw; - } + boost::asio::detail::mutex::scoped_lock lock(timer_mutex_); + op_queue<operation> ops; + ops.push(completed_ops_); + timer_queues_.get_ready_timers(ops); + post_deferred_completions(ops); } if (!ok && overlapped == 0) @@ -522,11 +522,9 @@ private: } else if (overlapped) { - // We may have been passed a last_error value in the completion_key. - if (last_error == 0) - { - last_error = completion_key; - } + operation* op = static_cast<operation*>(overlapped); + boost::system::error_code result_ec(last_error, + boost::asio::error::get_system_category()); // Transfer responsibility for dispatching timers to another thread. if (dispatching_timers && ::InterlockedCompareExchange( @@ -536,14 +534,35 @@ private: 0, transfer_timer_dispatching, 0); } - // Ensure that the io_service does not exit due to running out of work - // while we make the upcall. - auto_work work(*this); + // We may have been passed the last_error and bytes_transferred in the + // OVERLAPPED structure itself. + if (completion_key == overlapped_contains_result) + { + result_ec = boost::system::error_code(static_cast<int>(op->Offset), + *reinterpret_cast<boost::system::error_category*>(op->Internal)); + bytes_transferred = op->OffsetHigh; + } - // Dispatch the operation. - operation* op = static_cast<operation*>(overlapped); - if (op->on_completion(last_error, bytes_transferred)) + // Otherwise ensure any result has been saved into the OVERLAPPED + // structure. + else { + op->Internal = reinterpret_cast<ulong_ptr_t>(&result_ec.category()); + op->Offset = result_ec.value(); + op->OffsetHigh = bytes_transferred; + } + + // Dispatch the operation only if ready. The operation may not be ready + // if the initiating function (e.g. a call to WSARecv) has not yet + // returned. This is because the initiating function still wants access + // to the operation's OVERLAPPED structure. + if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1) + { + // Ensure the count of outstanding work is decremented on block exit. + work_finished_on_block_exit on_exit = { this }; + (void)on_exit; + + op->complete(*this, result_ec, bytes_transferred); ec = boost::system::error_code(); return 1; } @@ -588,126 +607,23 @@ private: } } - // Check if all timer queues are empty. - bool all_timer_queues_are_empty() const - { - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - if (!timer_queues_[i]->empty()) - return false; - return true; - } - // Get the timeout value for the GetQueuedCompletionStatus call. The timeout // value is returned as a number of milliseconds. We will wait no longer than // 1000 milliseconds. DWORD get_timeout() { - if (all_timer_queues_are_empty()) - return max_timeout; - - boost::posix_time::time_duration minimum_wait_duration - = boost::posix_time::milliseconds(max_timeout); - - for (std::size_t i = 0; i < timer_queues_.size(); ++i) - { - boost::posix_time::time_duration wait_duration - = timer_queues_[i]->wait_duration(); - if (wait_duration < minimum_wait_duration) - minimum_wait_duration = wait_duration; - } - - if (minimum_wait_duration > boost::posix_time::time_duration()) - { - int milliseconds = minimum_wait_duration.total_milliseconds(); - return static_cast<DWORD>(milliseconds > 0 ? milliseconds : 1); - } - else - { - return 0; - } + return timer_queues_.wait_duration_msec(max_timeout); } - struct auto_work + // Helper class to call work_finished() on block exit. + struct work_finished_on_block_exit { - auto_work(win_iocp_io_service& io_service) - : io_service_(io_service) + ~work_finished_on_block_exit() { - io_service_.work_started(); + io_service_->work_finished(); } - ~auto_work() - { - io_service_.work_finished(); - } - - private: - win_iocp_io_service& io_service_; - }; - - template <typename Handler> - struct handler_operation - : public operation - { - handler_operation(win_iocp_io_service& io_service, - Handler handler) - : operation(io_service, &handler_operation<Handler>::do_completion_impl, - &handler_operation<Handler>::destroy_impl), - io_service_(io_service), - handler_(handler) - { - io_service_.work_started(); - } - - ~handler_operation() - { - io_service_.work_finished(); - } - - private: - // Prevent copying and assignment. - handler_operation(const handler_operation&); - void operator=(const handler_operation&); - - static void do_completion_impl(operation* op, DWORD, size_t) - { - // Take ownership of the operation object. - typedef handler_operation<Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); - - // Make a copy of the handler so that the memory can be deallocated before - // the upcall is made. - Handler handler(handler_op->handler_); - - // Free the memory associated with the handler. - ptr.reset(); - - // Make the upcall. - boost_asio_handler_invoke_helpers::invoke(handler, handler); - } - - static void destroy_impl(operation* op) - { - // Take ownership of the operation object. - typedef handler_operation<Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); - - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(handler_op->handler_); - (void)handler; - - // Free the memory associated with the handler. - ptr.reset(); - } - - win_iocp_io_service& io_service_; - Handler handler_; + win_iocp_io_service* io_service_; }; // The IO completion port used for queueing operations. @@ -721,10 +637,6 @@ private: // The count of unfinished work. long outstanding_work_; - // The count of unfinished operations. - long outstanding_operations_; - friend class operation; - // Flag to indicate whether the event loop has been stopped. long stopped_; @@ -742,7 +654,12 @@ private: // Completion key value to indicate that responsibility for dispatching // timers should be stolen from another thread. - steal_timer_dispatching = 2 + steal_timer_dispatching = 2, + + // Completion key value to indicate that an operation has posted with the + // original last_error and bytes_transferred values stored in the fields of + // the OVERLAPPED structure. + overlapped_contains_result = 3 }; // The thread that's currently in charge of dispatching timers. @@ -755,12 +672,10 @@ private: bool timer_interrupt_issued_; // The timer queues. - std::vector<timer_queue_base*> timer_queues_; + timer_queue_set timer_queues_; - // A copy of the timer queues, used when dispatching, cancelling and cleaning - // up timers. The copy is stored as a class data member to avoid unnecessary - // memory allocation. - std::vector<timer_queue_base*> timer_queues_copy_; + // The operations that are ready to dispatch. + op_queue<operation> completed_ops_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/win_iocp_operation.hpp b/3rdParty/Boost/src/boost/asio/detail/win_iocp_operation.hpp new file mode 100644 index 0000000..d7d5723 --- /dev/null +++ b/3rdParty/Boost/src/boost/asio/detail/win_iocp_operation.hpp @@ -0,0 +1,91 @@ +// +// win_iocp_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 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) +// + +#ifndef BOOST_ASIO_DETAIL_WIN_IOCP_OPERATION_HPP +#define BOOST_ASIO_DETAIL_WIN_IOCP_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/push_options.hpp> + +#include <boost/asio/detail/win_iocp_io_service_fwd.hpp> + +#if defined(BOOST_ASIO_HAS_IOCP) + +#include <boost/asio/detail/op_queue.hpp> + +namespace boost { +namespace asio { +namespace detail { + +// Base class for all operations. A function pointer is used instead of virtual +// functions to avoid the associated overhead. +class win_iocp_operation + : public OVERLAPPED +{ +public: + void complete(win_iocp_io_service& owner, + const boost::system::error_code& ec = boost::system::error_code(), + std::size_t bytes_transferred = 0) + { + func_(&owner, this, ec, bytes_transferred); + } + + void destroy() + { + func_(0, this, boost::system::error_code(), 0); + } + +protected: + typedef void (*func_type)(win_iocp_io_service*, + win_iocp_operation*, boost::system::error_code, std::size_t); + + win_iocp_operation(func_type func) + : next_(0), + func_(func) + { + reset(); + } + + // Prevents deletion through this type. + ~win_iocp_operation() + { + } + + void reset() + { + Internal = 0; + InternalHigh = 0; + Offset = 0; + OffsetHigh = 0; + hEvent = 0; + ready_ = 0; + } + +private: + friend class op_queue_access; + friend class win_iocp_io_service; + win_iocp_operation* next_; + func_type func_; + long ready_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_ASIO_HAS_IOCP) + +#include <boost/system/error_code.hpp> +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_WIN_IOCP_OPERATION_HPP diff --git a/3rdParty/Boost/src/boost/asio/detail/win_iocp_overlapped_ptr.hpp b/3rdParty/Boost/src/boost/asio/detail/win_iocp_overlapped_ptr.hpp index bb64014..082a5b1 100644 --- a/3rdParty/Boost/src/boost/asio/detail/win_iocp_overlapped_ptr.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/win_iocp_overlapped_ptr.hpp @@ -21,8 +21,10 @@ #if defined(BOOST_ASIO_HAS_IOCP) +#include <boost/asio/detail/fenced_block.hpp> #include <boost/asio/detail/noncopyable.hpp> #include <boost/asio/detail/win_iocp_io_service.hpp> +#include <boost/asio/detail/win_iocp_operation.hpp> namespace boost { namespace asio { @@ -35,7 +37,8 @@ class win_iocp_overlapped_ptr public: // Construct an empty win_iocp_overlapped_ptr. win_iocp_overlapped_ptr() - : ptr_(0) + : ptr_(0), + iocp_service_(0) { } @@ -43,7 +46,8 @@ public: template <typename Handler> explicit win_iocp_overlapped_ptr( boost::asio::io_service& io_service, Handler handler) - : ptr_(0) + : ptr_(0), + iocp_service_(0) { this->reset(io_service, handler); } @@ -61,6 +65,8 @@ public: { ptr_->destroy(); ptr_ = 0; + iocp_service_->work_finished(); + iocp_service_ = 0; } } @@ -69,12 +75,14 @@ public: template <typename Handler> void reset(boost::asio::io_service& io_service, Handler handler) { - typedef overlapped_operation<Handler> value_type; + typedef overlapped_op<Handler> value_type; typedef handler_alloc_traits<Handler, value_type> alloc_traits; raw_handler_ptr<alloc_traits> raw_ptr(handler); - handler_ptr<alloc_traits> ptr(raw_ptr, io_service.impl_, handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); + io_service.impl_.work_started(); reset(); ptr_ = ptr.release(); + iocp_service_ = &io_service.impl_; } // Get the contained OVERLAPPED object. @@ -93,10 +101,11 @@ public: OVERLAPPED* release() { if (ptr_) - ptr_->on_pending(); + iocp_service_->on_pending(ptr_); OVERLAPPED* tmp = ptr_; ptr_ = 0; + iocp_service_ = 0; return tmp; } @@ -106,99 +115,54 @@ public: { if (ptr_) { - ptr_->ec_ = ec; - ptr_->on_immediate_completion(0, static_cast<DWORD>(bytes_transferred)); + iocp_service_->on_completion(ptr_, ec, + static_cast<DWORD>(bytes_transferred)); ptr_ = 0; + iocp_service_ = 0; } } private: - struct overlapped_operation_base - : public win_iocp_io_service::operation - { - overlapped_operation_base(win_iocp_io_service& io_service, - invoke_func_type invoke_func, destroy_func_type destroy_func) - : win_iocp_io_service::operation(io_service, invoke_func, destroy_func), - io_service_(io_service) - { - io_service_.work_started(); - } - - ~overlapped_operation_base() - { - io_service_.work_finished(); - } - - win_iocp_io_service& io_service_; - boost::system::error_code ec_; - }; - template <typename Handler> - struct overlapped_operation - : public overlapped_operation_base + struct overlapped_op : public win_iocp_operation { - overlapped_operation(win_iocp_io_service& io_service, - Handler handler) - : overlapped_operation_base(io_service, - &overlapped_operation<Handler>::do_completion_impl, - &overlapped_operation<Handler>::destroy_impl), + overlapped_op(Handler handler) + : win_iocp_operation(&overlapped_op::do_complete), handler_(handler) { } - private: - // Prevent copying and assignment. - overlapped_operation(const overlapped_operation&); - void operator=(const overlapped_operation&); - - static void do_completion_impl(win_iocp_io_service::operation* op, - DWORD last_error, size_t bytes_transferred) + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. - typedef overlapped_operation<Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); - - // Make a copy of the handler and error_code so that the memory can be - // deallocated before the upcall is made. - Handler handler(handler_op->handler_); - boost::system::error_code ec(handler_op->ec_); - if (last_error) - ec = boost::system::error_code(last_error, - boost::asio::error::get_system_category()); - - // Free the memory associated with the handler. - ptr.reset(); - - // Make the upcall. - boost_asio_handler_invoke_helpers::invoke( - bind_handler(handler, ec, bytes_transferred), handler); - } - - static void destroy_impl(win_iocp_io_service::operation* op) - { - // Take ownership of the operation object. - typedef overlapped_operation<Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); - - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(handler_op->handler_); - (void)handler; - - // Free the memory associated with the handler. - ptr.reset(); + overlapped_op* o(static_cast<overlapped_op*>(base)); + typedef handler_alloc_traits<Handler, overlapped_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); + + // Make the upcall if required. + if (owner) + { + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } + private: Handler handler_; }; - overlapped_operation_base* ptr_; + win_iocp_operation* ptr_; + win_iocp_io_service* iocp_service_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/win_iocp_serial_port_service.hpp b/3rdParty/Boost/src/boost/asio/detail/win_iocp_serial_port_service.hpp index 4d6d2e9..288287c 100644 --- a/3rdParty/Boost/src/boost/asio/detail/win_iocp_serial_port_service.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/win_iocp_serial_port_service.hpp @@ -37,7 +37,6 @@ namespace detail { // Extend win_iocp_handle_service to provide serial port support. class win_iocp_serial_port_service - : public boost::asio::detail::service_base<win_iocp_serial_port_service> { public: // The native type of a stream handle. @@ -47,10 +46,7 @@ public: typedef win_iocp_handle_service::implementation_type implementation_type; win_iocp_serial_port_service(boost::asio::io_service& io_service) - : boost::asio::detail::service_base< - win_iocp_serial_port_service>(io_service), - handle_service_( - boost::asio::use_service<win_iocp_handle_service>(io_service)) + : handle_service_(io_service) { } @@ -279,8 +275,8 @@ public: } private: - // The handle service used for initiating asynchronous operations. - win_iocp_handle_service& handle_service_; + // The implementation used for initiating asynchronous operations. + win_iocp_handle_service handle_service_; }; } // namespace detail diff --git a/3rdParty/Boost/src/boost/asio/detail/win_iocp_socket_service.hpp b/3rdParty/Boost/src/boost/asio/detail/win_iocp_socket_service.hpp index e0c0806..5d545a8 100644 --- a/3rdParty/Boost/src/boost/asio/detail/win_iocp_socket_service.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/win_iocp_socket_service.hpp @@ -28,15 +28,19 @@ #include <boost/weak_ptr.hpp> #include <boost/asio/detail/pop_options.hpp> -#include <boost/asio/buffer.hpp> #include <boost/asio/error.hpp> #include <boost/asio/io_service.hpp> #include <boost/asio/socket_base.hpp> #include <boost/asio/detail/bind_handler.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> +#include <boost/asio/detail/fenced_block.hpp> #include <boost/asio/detail/handler_alloc_helpers.hpp> #include <boost/asio/detail/handler_invoke_helpers.hpp> #include <boost/asio/detail/mutex.hpp> -#include <boost/asio/detail/select_reactor.hpp> +#include <boost/asio/detail/null_buffers_op.hpp> +#include <boost/asio/detail/operation.hpp> +#include <boost/asio/detail/reactor.hpp> +#include <boost/asio/detail/reactor_op.hpp> #include <boost/asio/detail/socket_holder.hpp> #include <boost/asio/detail/socket_ops.hpp> #include <boost/asio/detail/socket_types.hpp> @@ -48,7 +52,6 @@ namespace detail { template <typename Protocol> class win_iocp_socket_service - : public boost::asio::detail::service_base<win_iocp_socket_service<Protocol> > { public: // The protocol type. @@ -57,9 +60,6 @@ public: // The endpoint type. typedef typename Protocol::endpoint endpoint_type; - // Base class for all operations. - typedef win_iocp_io_service::operation operation; - struct noop_deleter { void operator()(void*) {} }; typedef boost::shared_ptr<void> shared_cancel_token_type; typedef boost::weak_ptr<void> weak_cancel_token_type; @@ -114,9 +114,6 @@ public: endpoint_type remote_endpoint_; }; - // The type of the reactor used for connect operations. - typedef detail::select_reactor<true> reactor_type; - // The implementation type of the socket. class implementation_type { @@ -161,7 +158,7 @@ public: protocol_type protocol_; // Per-descriptor data used by the reactor. - reactor_type::per_descriptor_data reactor_data_; + reactor::per_descriptor_data reactor_data_; #if defined(BOOST_ASIO_ENABLE_CANCELIO) // The ID of the thread from which it is safe to cancel asynchronous @@ -176,14 +173,10 @@ public: implementation_type* prev_; }; - // The maximum number of buffers to support in a single operation. - enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; - // Constructor. win_iocp_socket_service(boost::asio::io_service& io_service) - : boost::asio::detail::service_base< - win_iocp_socket_service<Protocol> >(io_service), - iocp_service_(boost::asio::use_service<win_iocp_io_service>(io_service)), + : io_service_(io_service), + iocp_service_(use_service<win_iocp_io_service>(io_service)), reactor_(0), mutex_(), impl_list_(0) @@ -304,11 +297,11 @@ public: // Check if the reactor was created, in which case we need to close the // socket on the reactor as well to cancel any operations that might be // running there. - reactor_type* reactor = static_cast<reactor_type*>( + reactor* r = static_cast<reactor*>( interlocked_compare_exchange_pointer( reinterpret_cast<void**>(&reactor_), 0, 0)); - if (reactor) - reactor->close_descriptor(impl.socket_, impl.reactor_data_); + if (r) + r->close_descriptor(impl.socket_, impl.reactor_data_); if (socket_ops::close(impl.socket_, ec) == socket_error_retval) return ec; @@ -665,23 +658,11 @@ public: return 0; } - // Copy buffers into WSABUF array. - ::WSABUF bufs[max_buffers]; - typename ConstBufferSequence::const_iterator iter = buffers.begin(); - typename ConstBufferSequence::const_iterator end = buffers.end(); - DWORD i = 0; - size_t total_buffer_size = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::const_buffer buffer(*iter); - bufs[i].len = static_cast<u_long>(boost::asio::buffer_size(buffer)); - bufs[i].buf = const_cast<char*>( - boost::asio::buffer_cast<const char*>(buffer)); - total_buffer_size += boost::asio::buffer_size(buffer); - } + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(buffers); // A request to receive 0 bytes on a stream socket is a no-op. - if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + if (impl.protocol_.type() == SOCK_STREAM && bufs.all_empty()) { ec = boost::system::error_code(); return 0; @@ -689,8 +670,8 @@ public: // Send the data. DWORD bytes_transferred = 0; - int result = ::WSASend(impl.socket_, bufs, - i, &bytes_transferred, flags, 0, 0); + int result = ::WSASend(impl.socket_, bufs.buffers(), + bufs.count(), &bytes_transferred, flags, 0, 0); if (result != 0) { DWORD last_error = ::WSAGetLastError(); @@ -724,94 +705,63 @@ public: } template <typename ConstBufferSequence, typename Handler> - class send_operation - : public operation + class send_op : public operation { public: - send_operation(win_iocp_io_service& io_service, - weak_cancel_token_type cancel_token, + send_op(weak_cancel_token_type cancel_token, const ConstBufferSequence& buffers, Handler handler) - : operation(io_service, - &send_operation<ConstBufferSequence, Handler>::do_completion_impl, - &send_operation<ConstBufferSequence, Handler>::destroy_impl), - work_(io_service.get_io_service()), + : operation(&send_op::do_complete), cancel_token_(cancel_token), buffers_(buffers), handler_(handler) { } - private: - static void do_completion_impl(operation* op, - DWORD last_error, size_t bytes_transferred) + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. - typedef send_operation<ConstBufferSequence, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); + send_op* o(static_cast<send_op*>(base)); + typedef handler_alloc_traits<Handler, send_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); -#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) - // Check whether buffers are still valid. - typename ConstBufferSequence::const_iterator iter - = handler_op->buffers_.begin(); - typename ConstBufferSequence::const_iterator end - = handler_op->buffers_.end(); - while (iter != end) + // Make the upcall if required. + if (owner) { - boost::asio::const_buffer buffer(*iter); - boost::asio::buffer_cast<const char*>(buffer); - ++iter; - } +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence>::validate(o->buffers_); #endif // defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) - // Map non-portable errors to their portable counterparts. - boost::system::error_code ec(last_error, - boost::asio::error::get_system_category()); - if (ec.value() == ERROR_NETNAME_DELETED) - { - if (handler_op->cancel_token_.expired()) - ec = boost::asio::error::operation_aborted; - else - ec = boost::asio::error::connection_reset; - } - else if (ec.value() == ERROR_PORT_UNREACHABLE) - { - ec = boost::asio::error::connection_refused; - } - - // Make a copy of the handler so that the memory can be deallocated before - // the upcall is made. - Handler handler(handler_op->handler_); - - // Free the memory associated with the handler. - ptr.reset(); - - // Call the handler. - boost_asio_handler_invoke_helpers::invoke( - detail::bind_handler(handler, ec, bytes_transferred), handler); - } - - static void destroy_impl(operation* op) - { - // Take ownership of the operation object. - typedef send_operation<ConstBufferSequence, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); - - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(handler_op->handler_); - (void)handler; + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (o->cancel_token_.expired()) + ec = boost::asio::error::operation_aborted; + else + ec = boost::asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = boost::asio::error::connection_refused; + } - // Free the memory associated with the handler. - ptr.reset(); + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } - boost::asio::io_service::work work_; + private: weak_cancel_token_type cancel_token_; ConstBufferSequence buffers_; Handler handler_; @@ -823,127 +773,34 @@ public: void async_send(implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, Handler handler) { -#if defined(BOOST_ASIO_ENABLE_CANCELIO) - // Update the ID of the thread from which cancellation is safe. - if (impl.safe_cancellation_thread_id_ == 0) - impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); - else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) - impl.safe_cancellation_thread_id_ = ~DWORD(0); -#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) - // Allocate and construct an operation to wrap the handler. - typedef send_operation<ConstBufferSequence, Handler> value_type; + typedef send_op<ConstBufferSequence, Handler> value_type; typedef handler_alloc_traits<Handler, value_type> alloc_traits; raw_handler_ptr<alloc_traits> raw_ptr(handler); - handler_ptr<alloc_traits> ptr(raw_ptr, iocp_service_, + handler_ptr<alloc_traits> ptr(raw_ptr, impl.cancel_token_, buffers, handler); - if (!is_open(impl)) - { - ptr.get()->on_immediate_completion(WSAEBADF, 0); - ptr.release(); - return; - } - - // Copy buffers into WSABUF array. - ::WSABUF bufs[max_buffers]; - typename ConstBufferSequence::const_iterator iter = buffers.begin(); - typename ConstBufferSequence::const_iterator end = buffers.end(); - DWORD i = 0; - size_t total_buffer_size = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::const_buffer buffer(*iter); - bufs[i].len = static_cast<u_long>(boost::asio::buffer_size(buffer)); - bufs[i].buf = const_cast<char*>( - boost::asio::buffer_cast<const char*>(buffer)); - total_buffer_size += boost::asio::buffer_size(buffer); - } - - // A request to receive 0 bytes on a stream socket is a no-op. - if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) - { - ptr.get()->on_immediate_completion(0, 0); - ptr.release(); - return; - } - - // Send the data. - DWORD bytes_transferred = 0; - int result = ::WSASend(impl.socket_, bufs, i, - &bytes_transferred, flags, ptr.get(), 0); - DWORD last_error = ::WSAGetLastError(); + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(buffers); - // Check if the operation completed immediately. - if (result != 0 && last_error != WSA_IO_PENDING) - { - ptr.get()->on_immediate_completion(last_error, bytes_transferred); - ptr.release(); - } - else - { - ptr.get()->on_pending(); - ptr.release(); - } + start_send_op(impl, bufs.buffers(), bufs.count(), flags, + impl.protocol_.type() == SOCK_STREAM && bufs.all_empty(), ptr.get()); + ptr.release(); } - template <typename Handler> - class null_buffers_operation - { - public: - null_buffers_operation(boost::asio::io_service& io_service, Handler handler) - : work_(io_service), - handler_(handler) - { - } - - bool perform(boost::system::error_code&, - std::size_t& bytes_transferred) - { - bytes_transferred = 0; - return true; - } - - void complete(const boost::system::error_code& ec, - std::size_t bytes_transferred) - { - work_.get_io_service().post(bind_handler( - handler_, ec, bytes_transferred)); - } - - private: - boost::asio::io_service::work work_; - Handler handler_; - }; - // Start an asynchronous wait until data can be sent without blocking. template <typename Handler> void async_send(implementation_type& impl, const null_buffers&, socket_base::message_flags, Handler handler) { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); - } - else - { - // Check if the reactor was already obtained from the io_service. - reactor_type* reactor = static_cast<reactor_type*>( - interlocked_compare_exchange_pointer( - reinterpret_cast<void**>(&reactor_), 0, 0)); - if (!reactor) - { - reactor = &(boost::asio::use_service<reactor_type>( - this->get_io_service())); - interlocked_exchange_pointer( - reinterpret_cast<void**>(&reactor_), reactor); - } + // Allocate and construct an operation to wrap the handler. + typedef null_buffers_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); - reactor->start_write_op(impl.socket_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler), - false); - } + start_reactor_op(impl, reactor::write_op, ptr.get()); + ptr.release(); } // Send a datagram to the specified endpoint. Returns the number of bytes @@ -959,23 +816,14 @@ public: return 0; } - // Copy buffers into WSABUF array. - ::WSABUF bufs[max_buffers]; - typename ConstBufferSequence::const_iterator iter = buffers.begin(); - typename ConstBufferSequence::const_iterator end = buffers.end(); - DWORD i = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::const_buffer buffer(*iter); - bufs[i].len = static_cast<u_long>(boost::asio::buffer_size(buffer)); - bufs[i].buf = const_cast<char*>( - boost::asio::buffer_cast<const char*>(buffer)); - } + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(buffers); // Send the data. DWORD bytes_transferred = 0; - int result = ::WSASendTo(impl.socket_, bufs, i, &bytes_transferred, - flags, destination.data(), static_cast<int>(destination.size()), 0, 0); + int result = ::WSASendTo(impl.socket_, bufs.buffers(), bufs.count(), + &bytes_transferred, flags, destination.data(), + static_cast<int>(destination.size()), 0, 0); if (result != 0) { DWORD last_error = ::WSAGetLastError(); @@ -1008,85 +856,57 @@ public: } template <typename ConstBufferSequence, typename Handler> - class send_to_operation - : public operation + class send_to_op : public operation { public: - send_to_operation(win_iocp_io_service& io_service, + send_to_op(weak_cancel_token_type cancel_token, const ConstBufferSequence& buffers, Handler handler) - : operation(io_service, - &send_to_operation<ConstBufferSequence, Handler>::do_completion_impl, - &send_to_operation<ConstBufferSequence, Handler>::destroy_impl), - work_(io_service.get_io_service()), + : operation(&send_to_op::do_complete), + cancel_token_(cancel_token), buffers_(buffers), handler_(handler) { } - private: - static void do_completion_impl(operation* op, - DWORD last_error, size_t bytes_transferred) + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. - typedef send_to_operation<ConstBufferSequence, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); + send_to_op* o(static_cast<send_to_op*>(base)); + typedef handler_alloc_traits<Handler, send_to_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); -#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) - // Check whether buffers are still valid. - typename ConstBufferSequence::const_iterator iter - = handler_op->buffers_.begin(); - typename ConstBufferSequence::const_iterator end - = handler_op->buffers_.end(); - while (iter != end) + // Make the upcall if required. + if (owner) { - boost::asio::const_buffer buffer(*iter); - boost::asio::buffer_cast<const char*>(buffer); - ++iter; - } +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence>::validate(o->buffers_); #endif // defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) - // Map non-portable errors to their portable counterparts. - boost::system::error_code ec(last_error, - boost::asio::error::get_system_category()); - if (ec.value() == ERROR_PORT_UNREACHABLE) - { - ec = boost::asio::error::connection_refused; - } - - // Make a copy of the handler so that the memory can be deallocated before - // the upcall is made. - Handler handler(handler_op->handler_); - - // Free the memory associated with the handler. - ptr.reset(); - - // Call the handler. - boost_asio_handler_invoke_helpers::invoke( - detail::bind_handler(handler, ec, bytes_transferred), handler); - } - - static void destroy_impl(operation* op) - { - // Take ownership of the operation object. - typedef send_to_operation<ConstBufferSequence, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); - - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(handler_op->handler_); - (void)handler; + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = boost::asio::error::connection_refused; + } - // Free the memory associated with the handler. - ptr.reset(); + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } - boost::asio::io_service::work work_; + private: + weak_cancel_token_type cancel_token_; ConstBufferSequence buffers_; Handler handler_; }; @@ -1098,57 +918,19 @@ public: const ConstBufferSequence& buffers, const endpoint_type& destination, socket_base::message_flags flags, Handler handler) { -#if defined(BOOST_ASIO_ENABLE_CANCELIO) - // Update the ID of the thread from which cancellation is safe. - if (impl.safe_cancellation_thread_id_ == 0) - impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); - else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) - impl.safe_cancellation_thread_id_ = ~DWORD(0); -#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) - // Allocate and construct an operation to wrap the handler. - typedef send_to_operation<ConstBufferSequence, Handler> value_type; + typedef send_to_op<ConstBufferSequence, Handler> value_type; typedef handler_alloc_traits<Handler, value_type> alloc_traits; raw_handler_ptr<alloc_traits> raw_ptr(handler); - handler_ptr<alloc_traits> ptr(raw_ptr, iocp_service_, buffers, handler); - - if (!is_open(impl)) - { - ptr.get()->on_immediate_completion(WSAEBADF, 0); - ptr.release(); - return; - } - - // Copy buffers into WSABUF array. - ::WSABUF bufs[max_buffers]; - typename ConstBufferSequence::const_iterator iter = buffers.begin(); - typename ConstBufferSequence::const_iterator end = buffers.end(); - DWORD i = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::const_buffer buffer(*iter); - bufs[i].len = static_cast<u_long>(boost::asio::buffer_size(buffer)); - bufs[i].buf = const_cast<char*>( - boost::asio::buffer_cast<const char*>(buffer)); - } + handler_ptr<alloc_traits> ptr(raw_ptr, + impl.cancel_token_, buffers, handler); - // Send the data. - DWORD bytes_transferred = 0; - int result = ::WSASendTo(impl.socket_, bufs, i, &bytes_transferred, flags, - destination.data(), static_cast<int>(destination.size()), ptr.get(), 0); - DWORD last_error = ::WSAGetLastError(); + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(buffers); - // Check if the operation completed immediately. - if (result != 0 && last_error != WSA_IO_PENDING) - { - ptr.get()->on_immediate_completion(last_error, bytes_transferred); - ptr.release(); - } - else - { - ptr.get()->on_pending(); - ptr.release(); - } + start_send_to_op(impl, bufs.buffers(), + bufs.count(), destination, flags, ptr.get()); + ptr.release(); } // Start an asynchronous wait until data can be sent without blocking. @@ -1156,29 +938,14 @@ public: void async_send_to(implementation_type& impl, const null_buffers&, socket_base::message_flags, const endpoint_type&, Handler handler) { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); - } - else - { - // Check if the reactor was already obtained from the io_service. - reactor_type* reactor = static_cast<reactor_type*>( - interlocked_compare_exchange_pointer( - reinterpret_cast<void**>(&reactor_), 0, 0)); - if (!reactor) - { - reactor = &(boost::asio::use_service<reactor_type>( - this->get_io_service())); - interlocked_exchange_pointer( - reinterpret_cast<void**>(&reactor_), reactor); - } + // Allocate and construct an operation to wrap the handler. + typedef null_buffers_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); - reactor->start_write_op(impl.socket_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler), - false); - } + start_reactor_op(impl, reactor::write_op, ptr.get()); + ptr.release(); } // Receive some data from the peer. Returns the number of bytes received. @@ -1193,22 +960,11 @@ public: return 0; } - // Copy buffers into WSABUF array. - ::WSABUF bufs[max_buffers]; - typename MutableBufferSequence::const_iterator iter = buffers.begin(); - typename MutableBufferSequence::const_iterator end = buffers.end(); - DWORD i = 0; - size_t total_buffer_size = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::mutable_buffer buffer(*iter); - bufs[i].len = static_cast<u_long>(boost::asio::buffer_size(buffer)); - bufs[i].buf = boost::asio::buffer_cast<char*>(buffer); - total_buffer_size += boost::asio::buffer_size(buffer); - } + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); // A request to receive 0 bytes on a stream socket is a no-op. - if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + if (impl.protocol_.type() == SOCK_STREAM && bufs.all_empty()) { ec = boost::system::error_code(); return 0; @@ -1217,8 +973,8 @@ public: // Receive some data. DWORD bytes_transferred = 0; DWORD recv_flags = flags; - int result = ::WSARecv(impl.socket_, bufs, i, - &bytes_transferred, &recv_flags, 0, 0); + int result = ::WSARecv(impl.socket_, bufs.buffers(), + bufs.count(), &bytes_transferred, &recv_flags, 0, 0); if (result != 0) { DWORD last_error = ::WSAGetLastError(); @@ -1257,106 +1013,75 @@ public: } template <typename MutableBufferSequence, typename Handler> - class receive_operation - : public operation + class receive_op : public operation { public: - receive_operation(int protocol_type, win_iocp_io_service& io_service, - weak_cancel_token_type cancel_token, + receive_op(int protocol_type, weak_cancel_token_type cancel_token, const MutableBufferSequence& buffers, Handler handler) - : operation(io_service, - &receive_operation< - MutableBufferSequence, Handler>::do_completion_impl, - &receive_operation< - MutableBufferSequence, Handler>::destroy_impl), + : operation(&receive_op::do_complete), protocol_type_(protocol_type), - work_(io_service.get_io_service()), cancel_token_(cancel_token), buffers_(buffers), handler_(handler) { } - private: - static void do_completion_impl(operation* op, - DWORD last_error, size_t bytes_transferred) + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. - typedef receive_operation<MutableBufferSequence, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); + receive_op* o(static_cast<receive_op*>(base)); + typedef handler_alloc_traits<Handler, receive_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); -#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) - // Check whether buffers are still valid. - typename MutableBufferSequence::const_iterator iter - = handler_op->buffers_.begin(); - typename MutableBufferSequence::const_iterator end - = handler_op->buffers_.end(); - while (iter != end) + // Make the upcall if required. + if (owner) { - boost::asio::mutable_buffer buffer(*iter); - boost::asio::buffer_cast<char*>(buffer); - ++iter; - } +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence>::validate(o->buffers_); #endif // defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) - // Map non-portable errors to their portable counterparts. - boost::system::error_code ec(last_error, - boost::asio::error::get_system_category()); - if (ec.value() == ERROR_NETNAME_DELETED) - { - if (handler_op->cancel_token_.expired()) - ec = boost::asio::error::operation_aborted; - else - ec = boost::asio::error::connection_reset; - } - else if (ec.value() == ERROR_PORT_UNREACHABLE) - { - ec = boost::asio::error::connection_refused; - } - - // Check for connection closed. - else if (!ec && bytes_transferred == 0 - && handler_op->protocol_type_ == SOCK_STREAM - && !boost::is_same<MutableBufferSequence, null_buffers>::value) - { - ec = boost::asio::error::eof; - } - - // Make a copy of the handler so that the memory can be deallocated before - // the upcall is made. - Handler handler(handler_op->handler_); - - // Free the memory associated with the handler. - ptr.reset(); - - // Call the handler. - boost_asio_handler_invoke_helpers::invoke( - detail::bind_handler(handler, ec, bytes_transferred), handler); - } - - static void destroy_impl(operation* op) - { - // Take ownership of the operation object. - typedef receive_operation<MutableBufferSequence, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (o->cancel_token_.expired()) + ec = boost::asio::error::operation_aborted; + else + ec = boost::asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = boost::asio::error::connection_refused; + } - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(handler_op->handler_); - (void)handler; + // Check for connection closed. + else if (!ec && bytes_transferred == 0 + && o->protocol_type_ == SOCK_STREAM + && !buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence>::all_empty(o->buffers_) + && !boost::is_same<MutableBufferSequence, null_buffers>::value) + { + ec = boost::asio::error::eof; + } - // Free the memory associated with the handler. - ptr.reset(); + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } + private: int protocol_type_; - boost::asio::io_service::work work_; weak_cancel_token_type cancel_token_; MutableBufferSequence buffers_; Handler handler_; @@ -1369,67 +1094,20 @@ public: const MutableBufferSequence& buffers, socket_base::message_flags flags, Handler handler) { -#if defined(BOOST_ASIO_ENABLE_CANCELIO) - // Update the ID of the thread from which cancellation is safe. - if (impl.safe_cancellation_thread_id_ == 0) - impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); - else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) - impl.safe_cancellation_thread_id_ = ~DWORD(0); -#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) - // Allocate and construct an operation to wrap the handler. - typedef receive_operation<MutableBufferSequence, Handler> value_type; + typedef receive_op<MutableBufferSequence, Handler> value_type; typedef handler_alloc_traits<Handler, value_type> alloc_traits; raw_handler_ptr<alloc_traits> raw_ptr(handler); int protocol_type = impl.protocol_.type(); handler_ptr<alloc_traits> ptr(raw_ptr, protocol_type, - iocp_service_, impl.cancel_token_, buffers, handler); - - if (!is_open(impl)) - { - ptr.get()->on_immediate_completion(WSAEBADF, 0); - ptr.release(); - return; - } - - // Copy buffers into WSABUF array. - ::WSABUF bufs[max_buffers]; - typename MutableBufferSequence::const_iterator iter = buffers.begin(); - typename MutableBufferSequence::const_iterator end = buffers.end(); - DWORD i = 0; - size_t total_buffer_size = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::mutable_buffer buffer(*iter); - bufs[i].len = static_cast<u_long>(boost::asio::buffer_size(buffer)); - bufs[i].buf = boost::asio::buffer_cast<char*>(buffer); - total_buffer_size += boost::asio::buffer_size(buffer); - } + impl.cancel_token_, buffers, handler); - // A request to receive 0 bytes on a stream socket is a no-op. - if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) - { - ptr.get()->on_immediate_completion(0, 0); - ptr.release(); - return; - } + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); - // Receive some data. - DWORD bytes_transferred = 0; - DWORD recv_flags = flags; - int result = ::WSARecv(impl.socket_, bufs, i, - &bytes_transferred, &recv_flags, ptr.get(), 0); - DWORD last_error = ::WSAGetLastError(); - if (result != 0 && last_error != WSA_IO_PENDING) - { - ptr.get()->on_immediate_completion(last_error, bytes_transferred); - ptr.release(); - } - else - { - ptr.get()->on_pending(); - ptr.release(); - } + start_receive_op(impl, bufs.buffers(), bufs.count(), flags, + protocol_type == SOCK_STREAM && bufs.all_empty(), ptr.get()); + ptr.release(); } // Wait until data can be received without blocking. @@ -1437,75 +1115,36 @@ public: void async_receive(implementation_type& impl, const null_buffers& buffers, socket_base::message_flags flags, Handler handler) { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); - } - else if (impl.protocol_.type() == SOCK_STREAM) + if (impl.protocol_.type() == SOCK_STREAM) { // For stream sockets on Windows, we may issue a 0-byte overlapped // WSARecv to wait until there is data available on the socket. -#if defined(BOOST_ASIO_ENABLE_CANCELIO) - // Update the ID of the thread from which cancellation is safe. - if (impl.safe_cancellation_thread_id_ == 0) - impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); - else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) - impl.safe_cancellation_thread_id_ = ~DWORD(0); -#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) - // Allocate and construct an operation to wrap the handler. - typedef receive_operation<null_buffers, Handler> value_type; + typedef receive_op<null_buffers, Handler> value_type; typedef handler_alloc_traits<Handler, value_type> alloc_traits; raw_handler_ptr<alloc_traits> raw_ptr(handler); int protocol_type = impl.protocol_.type(); handler_ptr<alloc_traits> ptr(raw_ptr, protocol_type, - iocp_service_, impl.cancel_token_, buffers, handler); + impl.cancel_token_, buffers, handler); - // Issue a receive operation with an empty buffer. ::WSABUF buf = { 0, 0 }; - DWORD bytes_transferred = 0; - DWORD recv_flags = flags; - int result = ::WSARecv(impl.socket_, &buf, 1, - &bytes_transferred, &recv_flags, ptr.get(), 0); - DWORD last_error = ::WSAGetLastError(); - if (result != 0 && last_error != WSA_IO_PENDING) - { - ptr.get()->on_immediate_completion(last_error, bytes_transferred); - ptr.release(); - } - else - { - ptr.get()->on_pending(); - ptr.release(); - } + start_receive_op(impl, &buf, 1, flags, false, ptr.get()); + ptr.release(); } else { - // Check if the reactor was already obtained from the io_service. - reactor_type* reactor = static_cast<reactor_type*>( - interlocked_compare_exchange_pointer( - reinterpret_cast<void**>(&reactor_), 0, 0)); - if (!reactor) - { - reactor = &(boost::asio::use_service<reactor_type>( - this->get_io_service())); - interlocked_exchange_pointer( - reinterpret_cast<void**>(&reactor_), reactor); - } + // Allocate and construct an operation to wrap the handler. + typedef null_buffers_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); - if (flags & socket_base::message_out_of_band) - { - reactor->start_except_op(impl.socket_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler)); - } - else - { - reactor->start_read_op(impl.socket_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler), - false); - } + start_reactor_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + ptr.get()); + ptr.release(); } } @@ -1523,24 +1162,16 @@ public: return 0; } - // Copy buffers into WSABUF array. - ::WSABUF bufs[max_buffers]; - typename MutableBufferSequence::const_iterator iter = buffers.begin(); - typename MutableBufferSequence::const_iterator end = buffers.end(); - DWORD i = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::mutable_buffer buffer(*iter); - bufs[i].len = static_cast<u_long>(boost::asio::buffer_size(buffer)); - bufs[i].buf = boost::asio::buffer_cast<char*>(buffer); - } + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); // Receive some data. DWORD bytes_transferred = 0; DWORD recv_flags = flags; int endpoint_size = static_cast<int>(sender_endpoint.capacity()); - int result = ::WSARecvFrom(impl.socket_, bufs, i, &bytes_transferred, - &recv_flags, sender_endpoint.data(), &endpoint_size, 0, 0); + int result = ::WSARecvFrom(impl.socket_, bufs.buffers(), + bufs.count(), &bytes_transferred, &recv_flags, + sender_endpoint.data(), &endpoint_size, 0, 0); if (result != 0) { DWORD last_error = ::WSAGetLastError(); @@ -1583,22 +1214,15 @@ public: } template <typename MutableBufferSequence, typename Handler> - class receive_from_operation - : public operation + class receive_from_op : public operation { public: - receive_from_operation(int protocol_type, win_iocp_io_service& io_service, - endpoint_type& endpoint, const MutableBufferSequence& buffers, - Handler handler) - : operation(io_service, - &receive_from_operation< - MutableBufferSequence, Handler>::do_completion_impl, - &receive_from_operation< - MutableBufferSequence, Handler>::destroy_impl), + receive_from_op(int protocol_type, endpoint_type& endpoint, + const MutableBufferSequence& buffers, Handler handler) + : operation(&receive_from_op::do_complete), protocol_type_(protocol_type), endpoint_(endpoint), endpoint_size_(static_cast<int>(endpoint.capacity())), - work_(io_service.get_io_service()), buffers_(buffers), handler_(handler) { @@ -1609,83 +1233,51 @@ public: return endpoint_size_; } - private: - static void do_completion_impl(operation* op, - DWORD last_error, size_t bytes_transferred) + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code ec, std::size_t bytes_transferred) { // Take ownership of the operation object. - typedef receive_from_operation<MutableBufferSequence, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); + receive_from_op* o(static_cast<receive_from_op*>(base)); + typedef handler_alloc_traits<Handler, receive_from_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); -#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) - // Check whether buffers are still valid. - typename MutableBufferSequence::const_iterator iter - = handler_op->buffers_.begin(); - typename MutableBufferSequence::const_iterator end - = handler_op->buffers_.end(); - while (iter != end) + // Make the upcall if required. + if (owner) { - boost::asio::mutable_buffer buffer(*iter); - boost::asio::buffer_cast<char*>(buffer); - ++iter; - } +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence>::validate(o->buffers_); #endif // defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) - // Map non-portable errors to their portable counterparts. - boost::system::error_code ec(last_error, - boost::asio::error::get_system_category()); - if (ec.value() == ERROR_PORT_UNREACHABLE) - { - ec = boost::asio::error::connection_refused; - } + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = boost::asio::error::connection_refused; + } - // Check for connection closed. - if (!ec && bytes_transferred == 0 - && handler_op->protocol_type_ == SOCK_STREAM) - { - ec = boost::asio::error::eof; + // Record the size of the endpoint returned by the operation. + o->endpoint_.resize(o->endpoint_size_); + + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder2<Handler, boost::system::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); } - - // Record the size of the endpoint returned by the operation. - handler_op->endpoint_.resize(handler_op->endpoint_size_); - - // Make a copy of the handler so that the memory can be deallocated before - // the upcall is made. - Handler handler(handler_op->handler_); - - // Free the memory associated with the handler. - ptr.reset(); - - // Call the handler. - boost_asio_handler_invoke_helpers::invoke( - detail::bind_handler(handler, ec, bytes_transferred), handler); - } - - static void destroy_impl(operation* op) - { - // Take ownership of the operation object. - typedef receive_from_operation<MutableBufferSequence, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); - - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(handler_op->handler_); - (void)handler; - - // Free the memory associated with the handler. - ptr.reset(); } + private: int protocol_type_; endpoint_type& endpoint_; int endpoint_size_; - boost::asio::io_service::work work_; + weak_cancel_token_type cancel_token_; MutableBufferSequence buffers_; Handler handler_; }; @@ -1698,58 +1290,20 @@ public: const MutableBufferSequence& buffers, endpoint_type& sender_endp, socket_base::message_flags flags, Handler handler) { -#if defined(BOOST_ASIO_ENABLE_CANCELIO) - // Update the ID of the thread from which cancellation is safe. - if (impl.safe_cancellation_thread_id_ == 0) - impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); - else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) - impl.safe_cancellation_thread_id_ = ~DWORD(0); -#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) - // Allocate and construct an operation to wrap the handler. - typedef receive_from_operation<MutableBufferSequence, Handler> value_type; + typedef receive_from_op<MutableBufferSequence, Handler> value_type; typedef handler_alloc_traits<Handler, value_type> alloc_traits; raw_handler_ptr<alloc_traits> raw_ptr(handler); int protocol_type = impl.protocol_.type(); - handler_ptr<alloc_traits> ptr(raw_ptr, protocol_type, - iocp_service_, sender_endp, buffers, handler); - - if (!is_open(impl)) - { - ptr.get()->on_immediate_completion(WSAEBADF, 0); - ptr.release(); - return; - } + handler_ptr<alloc_traits> ptr(raw_ptr, + protocol_type, sender_endp, buffers, handler); - // Copy buffers into WSABUF array. - ::WSABUF bufs[max_buffers]; - typename MutableBufferSequence::const_iterator iter = buffers.begin(); - typename MutableBufferSequence::const_iterator end = buffers.end(); - DWORD i = 0; - for (; iter != end && i < max_buffers; ++iter, ++i) - { - boost::asio::mutable_buffer buffer(*iter); - bufs[i].len = static_cast<u_long>(boost::asio::buffer_size(buffer)); - bufs[i].buf = boost::asio::buffer_cast<char*>(buffer); - } + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); - // Receive some data. - DWORD bytes_transferred = 0; - DWORD recv_flags = flags; - int result = ::WSARecvFrom(impl.socket_, bufs, i, &bytes_transferred, - &recv_flags, sender_endp.data(), &ptr.get()->endpoint_size(), - ptr.get(), 0); - DWORD last_error = ::WSAGetLastError(); - if (result != 0 && last_error != WSA_IO_PENDING) - { - ptr.get()->on_immediate_completion(last_error, bytes_transferred); - ptr.release(); - } - else - { - ptr.get()->on_pending(); - ptr.release(); - } + start_receive_from_op(impl, bufs.buffers(), bufs.count(), + sender_endp, flags, &ptr.get()->endpoint_size(), ptr.get()); + ptr.release(); } // Wait until data can be received without blocking. @@ -1758,40 +1312,20 @@ public: const null_buffers&, endpoint_type& sender_endpoint, socket_base::message_flags flags, Handler handler) { - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor, 0)); - } - else - { - // Check if the reactor was already obtained from the io_service. - reactor_type* reactor = static_cast<reactor_type*>( - interlocked_compare_exchange_pointer( - reinterpret_cast<void**>(&reactor_), 0, 0)); - if (!reactor) - { - reactor = &(boost::asio::use_service<reactor_type>( - this->get_io_service())); - interlocked_exchange_pointer( - reinterpret_cast<void**>(&reactor_), reactor); - } + // Allocate and construct an operation to wrap the handler. + typedef null_buffers_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, handler); - // Reset endpoint since it can be given no sensible value at this time. - sender_endpoint = endpoint_type(); + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); - if (flags & socket_base::message_out_of_band) - { - reactor->start_except_op(impl.socket_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler)); - } - else - { - reactor->start_read_op(impl.socket_, impl.reactor_data_, - null_buffers_operation<Handler>(this->get_io_service(), handler), - false); - } - } + start_reactor_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + ptr.get()); + ptr.release(); } // Accept a new connection. @@ -1852,32 +1386,27 @@ public: } template <typename Socket, typename Handler> - class accept_operation - : public operation + class accept_op : public operation { public: - accept_operation(win_iocp_io_service& io_service, - socket_type socket, socket_type new_socket, Socket& peer, - const protocol_type& protocol, endpoint_type* peer_endpoint, - bool enable_connection_aborted, Handler handler) - : operation(io_service, - &accept_operation<Socket, Handler>::do_completion_impl, - &accept_operation<Socket, Handler>::destroy_impl), - io_service_(io_service), + accept_op(win_iocp_io_service& iocp_service, socket_type socket, + Socket& peer, const protocol_type& protocol, + endpoint_type* peer_endpoint, bool enable_connection_aborted, + Handler handler) + : operation(&accept_op::do_complete), + iocp_service_(iocp_service), socket_(socket), - new_socket_(new_socket), peer_(peer), protocol_(protocol), peer_endpoint_(peer_endpoint), - work_(io_service.get_io_service()), enable_connection_aborted_(enable_connection_aborted), handler_(handler) { } - socket_type new_socket() + socket_holder& new_socket() { - return new_socket_.get(); + return new_socket_; } void* output_buffer() @@ -1890,166 +1419,145 @@ public: return sizeof(sockaddr_storage_type) + 16; } - private: - static void do_completion_impl(operation* op, DWORD last_error, size_t) + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code ec, std::size_t /*bytes_transferred*/) { - // Take ownership of the operation object. - typedef accept_operation<Socket, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); + // Take ownership of the handler object. + accept_op* o(static_cast<accept_op*>(base)); + typedef handler_alloc_traits<Handler, accept_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); - // Map Windows error ERROR_NETNAME_DELETED to connection_aborted. - if (last_error == ERROR_NETNAME_DELETED) + // Make the upcall if required. + if (owner) { - last_error = WSAECONNABORTED; - } + // Map Windows error ERROR_NETNAME_DELETED to connection_aborted. + if (ec.value() == ERROR_NETNAME_DELETED) + { + ec = boost::asio::error::connection_aborted; + } - // Restart the accept operation if we got the connection_aborted error - // and the enable_connection_aborted socket option is not set. - if (last_error == WSAECONNABORTED - && !ptr.get()->enable_connection_aborted_) - { - // Reset OVERLAPPED structure. - ptr.get()->reset(); - - // Create a new socket for the next connection, since the AcceptEx call - // fails with WSAEINVAL if we try to reuse the same socket. - boost::system::error_code ec; - ptr.get()->new_socket_.reset(); - ptr.get()->new_socket_.reset(socket_ops::socket( - ptr.get()->protocol_.family(), ptr.get()->protocol_.type(), - ptr.get()->protocol_.protocol(), ec)); - if (ptr.get()->new_socket() != invalid_socket) + // Restart the accept operation if we got the connection_aborted error + // and the enable_connection_aborted socket option is not set. + if (ec == boost::asio::error::connection_aborted + && !o->enable_connection_aborted_) { - // Accept a connection. - DWORD bytes_read = 0; - BOOL result = ::AcceptEx(ptr.get()->socket_, ptr.get()->new_socket(), - ptr.get()->output_buffer(), 0, ptr.get()->address_length(), - ptr.get()->address_length(), &bytes_read, ptr.get()); - last_error = ::WSAGetLastError(); - - // Check if the operation completed immediately. - if (!result && last_error != WSA_IO_PENDING) + // Reset OVERLAPPED structure. + o->reset(); + + // Create a new socket for the next connection, since the AcceptEx + // call fails with WSAEINVAL if we try to reuse the same socket. + o->new_socket_.reset(); + o->new_socket_.reset(socket_ops::socket(o->protocol_.family(), + o->protocol_.type(), o->protocol_.protocol(), ec)); + if (o->new_socket_.get() != invalid_socket) { - if (last_error == ERROR_NETNAME_DELETED - || last_error == WSAECONNABORTED) + // Accept a connection. + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(o->socket_, o->new_socket_.get(), + o->output_buffer(), 0, o->address_length(), + o->address_length(), &bytes_read, o); + DWORD last_error = ::WSAGetLastError(); + ec = boost::system::error_code(last_error, + boost::asio::error::get_system_category()); + + // Check if the operation completed immediately. + if (!result && last_error != WSA_IO_PENDING) { - // Post this handler so that operation will be restarted again. - ptr.get()->on_immediate_completion(last_error, 0); - ptr.release(); - return; + if (last_error == ERROR_NETNAME_DELETED + || last_error == WSAECONNABORTED) + { + // Post this handler so that operation will be restarted again. + o->iocp_service_.work_started(); + o->iocp_service_.on_completion(o, ec); + ptr.release(); + return; + } + else + { + // Operation already complete. Continue with rest of this + // handler. + } } else { - // Operation already complete. Continue with rest of this handler. + // Asynchronous operation has been successfully restarted. + o->iocp_service_.work_started(); + o->iocp_service_.on_pending(o); + ptr.release(); + return; } } + } + + // Get the address of the peer. + endpoint_type peer_endpoint; + if (!ec) + { + LPSOCKADDR local_addr = 0; + int local_addr_length = 0; + LPSOCKADDR remote_addr = 0; + int remote_addr_length = 0; + GetAcceptExSockaddrs(o->output_buffer(), 0, o->address_length(), + o->address_length(), &local_addr, &local_addr_length, + &remote_addr, &remote_addr_length); + if (static_cast<std::size_t>(remote_addr_length) + > peer_endpoint.capacity()) + { + ec = boost::asio::error::invalid_argument; + } else { - // Asynchronous operation has been successfully restarted. - ptr.get()->on_pending(); - ptr.release(); - return; + using namespace std; // For memcpy. + memcpy(peer_endpoint.data(), remote_addr, remote_addr_length); + peer_endpoint.resize(static_cast<std::size_t>(remote_addr_length)); } } - } - // Get the address of the peer. - endpoint_type peer_endpoint; - if (last_error == 0) - { - LPSOCKADDR local_addr = 0; - int local_addr_length = 0; - LPSOCKADDR remote_addr = 0; - int remote_addr_length = 0; - GetAcceptExSockaddrs(handler_op->output_buffer(), 0, - handler_op->address_length(), handler_op->address_length(), - &local_addr, &local_addr_length, &remote_addr, &remote_addr_length); - if (static_cast<std::size_t>(remote_addr_length) - > peer_endpoint.capacity()) - { - last_error = WSAEINVAL; - } - else + // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname + // and getpeername will work on the accepted socket. + if (!ec) { - using namespace std; // For memcpy. - memcpy(peer_endpoint.data(), remote_addr, remote_addr_length); - peer_endpoint.resize(static_cast<std::size_t>(remote_addr_length)); + SOCKET update_ctx_param = o->socket_; + socket_ops::setsockopt(o->new_socket_.get(), + SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + &update_ctx_param, sizeof(SOCKET), ec); } - } - // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname - // and getpeername will work on the accepted socket. - if (last_error == 0) - { - SOCKET update_ctx_param = handler_op->socket_; - boost::system::error_code ec; - if (socket_ops::setsockopt(handler_op->new_socket_.get(), - SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, - &update_ctx_param, sizeof(SOCKET), ec) != 0) + // If the socket was successfully accepted, transfer ownership of the + // socket to the peer object. + if (!ec) { - last_error = ec.value(); + o->peer_.assign(o->protocol_, + native_type(o->new_socket_.get(), peer_endpoint), ec); + if (!ec) + o->new_socket_.release(); } - } - // If the socket was successfully accepted, transfer ownership of the - // socket to the peer object. - if (last_error == 0) - { - boost::system::error_code ec; - handler_op->peer_.assign(handler_op->protocol_, - native_type(handler_op->new_socket_.get(), peer_endpoint), ec); - if (ec) - last_error = ec.value(); - else - handler_op->new_socket_.release(); + // Pass endpoint back to caller. + if (o->peer_endpoint_) + *o->peer_endpoint_ = peer_endpoint; + + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder1<Handler, boost::system::error_code> + handler(o->handler_, ec); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); } - - // Pass endpoint back to caller. - if (handler_op->peer_endpoint_) - *handler_op->peer_endpoint_ = peer_endpoint; - - // Make a copy of the handler so that the memory can be deallocated before - // the upcall is made. - Handler handler(handler_op->handler_); - - // Free the memory associated with the handler. - ptr.reset(); - - // Call the handler. - boost::system::error_code ec(last_error, - boost::asio::error::get_system_category()); - boost_asio_handler_invoke_helpers::invoke( - detail::bind_handler(handler, ec), handler); - } - - static void destroy_impl(operation* op) - { - // Take ownership of the operation object. - typedef accept_operation<Socket, Handler> op_type; - op_type* handler_op(static_cast<op_type*>(op)); - typedef handler_alloc_traits<Handler, op_type> alloc_traits; - handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op); - - // A sub-object of the handler may be the true owner of the memory - // associated with the handler. Consequently, a local copy of the handler - // is required to ensure that any owning sub-object remains valid until - // after we have deallocated the memory here. - Handler handler(handler_op->handler_); - (void)handler; - - // Free the memory associated with the handler. - ptr.reset(); } - win_iocp_io_service& io_service_; + private: + win_iocp_io_service& iocp_service_; socket_type socket_; socket_holder new_socket_; Socket& peer_; protocol_type protocol_; endpoint_type* peer_endpoint_; - boost::asio::io_service::work work_; unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2]; bool enable_connection_aborted_; Handler handler_; @@ -2061,86 +1569,18 @@ public: void async_accept(implementation_type& impl, Socket& peer, endpoint_type* peer_endpoint, Handler handler) { - // Check whether acceptor has been initialised. - if (!is_open(impl)) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor)); - return; - } - - // Check that peer socket has not already been opened. - if (peer.is_open()) - { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::already_open)); - return; - } - -#if defined(BOOST_ASIO_ENABLE_CANCELIO) - // Update the ID of the thread from which cancellation is safe. - if (impl.safe_cancellation_thread_id_ == 0) - impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); - else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) - impl.safe_cancellation_thread_id_ = ~DWORD(0); -#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) - - // Create a new socket for the connection. - boost::system::error_code ec; - socket_holder sock(socket_ops::socket(impl.protocol_.family(), - impl.protocol_.type(), impl.protocol_.protocol(), ec)); - if (sock.get() == invalid_socket) - { - this->get_io_service().post(bind_handler(handler, ec)); - return; - } - // Allocate and construct an operation to wrap the handler. - typedef accept_operation<Socket, Handler> value_type; + typedef accept_op<Socket, Handler> value_type; typedef handler_alloc_traits<Handler, value_type> alloc_traits; raw_handler_ptr<alloc_traits> raw_ptr(handler); - socket_type new_socket = sock.get(); bool enable_connection_aborted = (impl.flags_ & implementation_type::enable_connection_aborted); - handler_ptr<alloc_traits> ptr(raw_ptr, - iocp_service_, impl.socket_, new_socket, peer, impl.protocol_, - peer_endpoint, enable_connection_aborted, handler); - sock.release(); - - // Accept a connection. - DWORD bytes_read = 0; - BOOL result = ::AcceptEx(impl.socket_, ptr.get()->new_socket(), - ptr.get()->output_buffer(), 0, ptr.get()->address_length(), - ptr.get()->address_length(), &bytes_read, ptr.get()); - DWORD last_error = ::WSAGetLastError(); - - // Check if the operation completed immediately. - if (!result && last_error != WSA_IO_PENDING) - { - if (!enable_connection_aborted - && (last_error == ERROR_NETNAME_DELETED - || last_error == WSAECONNABORTED)) - { - // Post handler so that operation will be restarted again. We do not - // perform the AcceptEx again here to avoid the possibility of starving - // other handlers. - ptr.get()->on_immediate_completion(last_error, 0); - ptr.release(); - } - else - { - boost::asio::io_service::work work(this->get_io_service()); - ptr.reset(); - boost::system::error_code ec(last_error, - boost::asio::error::get_system_category()); - iocp_service_.post(bind_handler(handler, ec)); - } - } - else - { - ptr.get()->on_pending(); - ptr.release(); - } + handler_ptr<alloc_traits> ptr(raw_ptr, iocp_service_, impl.socket_, peer, + impl.protocol_, peer_endpoint, enable_connection_aborted, handler); + + start_accept_op(impl, peer.is_open(), ptr.get()->new_socket(), + ptr.get()->output_buffer(), ptr.get()->address_length(), ptr.get()); + ptr.release(); } // Connect the socket to the specified endpoint. @@ -2159,67 +1599,76 @@ public: return ec; } - template <typename Handler> - class connect_operation + class connect_op_base : public reactor_op { public: - connect_operation(socket_type socket, bool user_set_non_blocking, - boost::asio::io_service& io_service, Handler handler) - : socket_(socket), - user_set_non_blocking_(user_set_non_blocking), - io_service_(io_service), - work_(io_service), - handler_(handler) + connect_op_base(socket_type socket, func_type complete_func) + : reactor_op(&connect_op_base::do_perform, complete_func), + socket_(socket) { } - bool perform(boost::system::error_code& ec, - std::size_t& bytes_transferred) + static bool do_perform(reactor_op* base) { - bytes_transferred = 0; - - // Check whether the operation was successful. - if (ec) - return true; + connect_op_base* o(static_cast<connect_op_base*>(base)); // Get the error code from the connect operation. int connect_error = 0; size_t connect_error_len = sizeof(connect_error); - if (socket_ops::getsockopt(socket_, SOL_SOCKET, SO_ERROR, - &connect_error, &connect_error_len, ec) == socket_error_retval) + if (socket_ops::getsockopt(o->socket_, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len, o->ec_) == socket_error_retval) return true; - // If connection failed then post the handler with the error code. + // The connection failed so the handler will be posted with an error code. if (connect_error) { - ec = boost::system::error_code(connect_error, + o->ec_ = boost::system::error_code(connect_error, boost::asio::error::get_system_category()); - return true; } - // Revert socket to blocking mode unless the user requested otherwise. - if (!user_set_non_blocking_) - { - ioctl_arg_type non_blocking = 0; - if (socket_ops::ioctl(socket_, FIONBIO, &non_blocking, ec)) - return true; - } - - // Post the result of the successful connection operation. - ec = boost::system::error_code(); return true; } - void complete(const boost::system::error_code& ec, std::size_t) + private: + socket_type socket_; + }; + + template <typename Handler> + class connect_op : public connect_op_base + { + public: + connect_op(socket_type socket, Handler handler) + : connect_op_base(socket, &connect_op::do_complete), + handler_(handler) { - io_service_.post(bind_handler(handler_, ec)); + } + + static void do_complete(io_service_impl* owner, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + connect_op* o(static_cast<connect_op*>(base)); + typedef handler_alloc_traits<Handler, connect_op> alloc_traits; + handler_ptr<alloc_traits> ptr(o->handler_, o); + + // Make the upcall if required. + if (owner) + { + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an + // upcall, a sub-object of the handler may be the true owner of the + // memory associated with the handler. Consequently, a local copy of + // the handler is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + detail::binder1<Handler, boost::system::error_code> + handler(o->handler_, o->ec_); + ptr.reset(); + boost::asio::detail::fenced_block b; + boost_asio_handler_invoke_helpers::invoke(handler, handler); + } } private: - socket_type socket_; - bool user_set_non_blocking_; - boost::asio::io_service& io_service_; - boost::asio::io_service::work work_; Handler handler_; }; @@ -2228,86 +1677,216 @@ public: void async_connect(implementation_type& impl, const endpoint_type& peer_endpoint, Handler handler) { + // Allocate and construct an operation to wrap the handler. + typedef connect_op<Handler> value_type; + typedef handler_alloc_traits<Handler, value_type> alloc_traits; + raw_handler_ptr<alloc_traits> raw_ptr(handler); + handler_ptr<alloc_traits> ptr(raw_ptr, impl.socket_, handler); + + start_connect_op(impl, ptr.get(), peer_endpoint); + ptr.release(); + } + +private: + // Helper function to start an asynchronous send operation. + void start_send_op(implementation_type& impl, WSABUF* buffers, + std::size_t buffer_count, socket_base::message_flags flags, + bool noop, operation* op) + { + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (noop) + iocp_service_.on_completion(op); + else if (!is_open(impl)) + iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + int result = ::WSASend(impl.socket_, buffers, + buffer_count, &bytes_transferred, flags, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } + } + + // Helper function to start an asynchronous send_to operation. + void start_send_to_op(implementation_type& impl, WSABUF* buffers, + std::size_t buffer_count, const endpoint_type& destination, + socket_base::message_flags flags, operation* op) + { + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + if (!is_open(impl)) + iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); + else { - this->get_io_service().post(bind_handler(handler, - boost::asio::error::bad_descriptor)); - return; + DWORD bytes_transferred = 0; + int result = ::WSASendTo(impl.socket_, buffers, buffer_count, + &bytes_transferred, flags, destination.data(), + static_cast<int>(destination.size()), op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); } + } -#if defined(BOOST_ASIO_ENABLE_CANCELIO) - // Update the ID of the thread from which cancellation is safe. - if (impl.safe_cancellation_thread_id_ == 0) - impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); - else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) - impl.safe_cancellation_thread_id_ = ~DWORD(0); -#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) + // Helper function to start an asynchronous receive operation. + void start_receive_op(implementation_type& impl, WSABUF* buffers, + std::size_t buffer_count, socket_base::message_flags flags, + bool noop, operation* op) + { + update_cancellation_thread_id(impl); + iocp_service_.work_started(); - // Check if the reactor was already obtained from the io_service. - reactor_type* reactor = static_cast<reactor_type*>( - interlocked_compare_exchange_pointer( - reinterpret_cast<void**>(&reactor_), 0, 0)); - if (!reactor) + if (noop) + iocp_service_.on_completion(op); + else if (!is_open(impl)) + iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); + else { - reactor = &(boost::asio::use_service<reactor_type>( - this->get_io_service())); - interlocked_exchange_pointer( - reinterpret_cast<void**>(&reactor_), reactor); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(impl.socket_, buffers, buffer_count, + &bytes_transferred, &recv_flags, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_NETNAME_DELETED) + last_error = WSAECONNRESET; + else if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); } + } - // Mark the socket as non-blocking so that the connection will take place - // asynchronously. - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + // Helper function to start an asynchronous receive_from operation. + void start_receive_from_op(implementation_type& impl, WSABUF* buffers, + std::size_t buffer_count, endpoint_type& sender_endpoint, + socket_base::message_flags flags, int* endpoint_size, operation* op) + { + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); + else { - this->get_io_service().post(bind_handler(handler, ec)); - return; + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecvFrom(impl.socket_, buffers, + buffer_count, &bytes_transferred, &recv_flags, + sender_endpoint.data(), endpoint_size, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); } + } - // Start the connect operation. - if (socket_ops::connect(impl.socket_, peer_endpoint.data(), - peer_endpoint.size(), ec) == 0) + // Helper function to start an asynchronous receive_from operation. + void start_accept_op(implementation_type& impl, + bool peer_is_open, socket_holder& new_socket, + void* output_buffer, DWORD address_length, operation* op) + { + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); + else if (peer_is_open) + iocp_service_.on_completion(op, boost::asio::error::already_open); + else { - // Revert socket to blocking mode unless the user requested otherwise. - if (!(impl.flags_ & implementation_type::user_set_non_blocking)) + boost::system::error_code ec; + new_socket.reset(socket_ops::socket(impl.protocol_.family(), + impl.protocol_.type(), impl.protocol_.protocol(), ec)); + if (new_socket.get() == invalid_socket) + iocp_service_.on_completion(op, ec); + else { - non_blocking = 0; - socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec); + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(impl.socket_, new_socket.get(), output_buffer, + 0, address_length, address_length, &bytes_read, op); + DWORD last_error = ::WSAGetLastError(); + if (!result && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error); + else + iocp_service_.on_pending(op); } - - // The connect operation has finished successfully so we need to post the - // handler immediately. - this->get_io_service().post(bind_handler(handler, ec)); } - else if (ec == boost::asio::error::in_progress - || ec == boost::asio::error::would_block) + } + + // Start an asynchronous read or write operation using the the reactor. + void start_reactor_op(implementation_type& impl, int op_type, reactor_op* op) + { + reactor& r = get_reactor(); + update_cancellation_thread_id(impl); + + if (is_open(impl)) { - // The connection is happening in the background, and we need to wait - // until the socket becomes writeable. - boost::shared_ptr<bool> completed(new bool(false)); - reactor->start_connect_op(impl.socket_, impl.reactor_data_, - connect_operation<Handler>( - impl.socket_, - (impl.flags_ & implementation_type::user_set_non_blocking) != 0, - this->get_io_service(), handler)); + r.start_op(op_type, impl.socket_, impl.reactor_data_, op, false); + return; } else + op->ec_ = boost::asio::error::bad_descriptor; + + iocp_service_.post_immediate_completion(op); + } + + // Start the asynchronous connect operation using the reactor. + void start_connect_op(implementation_type& impl, + reactor_op* op, const endpoint_type& peer_endpoint) + { + reactor& r = get_reactor(); + update_cancellation_thread_id(impl); + + if (is_open(impl)) { - // Revert socket to blocking mode unless the user requested otherwise. - if (!(impl.flags_ & implementation_type::user_set_non_blocking)) + ioctl_arg_type non_blocking = 1; + if (!socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, op->ec_)) { - non_blocking = 0; - boost::system::error_code ignored_ec; - socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec); - } + if (socket_ops::connect(impl.socket_, peer_endpoint.data(), + peer_endpoint.size(), op->ec_) != 0) + { + if (!op->ec_ + && !(impl.flags_ & implementation_type::user_set_non_blocking)) + { + non_blocking = 0; + socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, op->ec_); + } - // The connect operation has failed, so post the handler immediately. - this->get_io_service().post(bind_handler(handler, ec)); + if (op->ec_ == boost::asio::error::in_progress + || op->ec_ == boost::asio::error::would_block) + { + op->ec_ = boost::system::error_code(); + r.start_op(reactor::connect_op, impl.socket_, + impl.reactor_data_, op, true); + return; + } + } + } } + else + op->ec_ = boost::asio::error::bad_descriptor; + + iocp_service_.post_immediate_completion(op); } -private: // Helper function to close a socket when the associated object is being // destroyed. void close_for_destruction(implementation_type& impl) @@ -2317,11 +1896,11 @@ private: // Check if the reactor was created, in which case we need to close the // socket on the reactor as well to cancel any operations that might be // running there. - reactor_type* reactor = static_cast<reactor_type*>( + reactor* r = static_cast<reactor*>( interlocked_compare_exchange_pointer( reinterpret_cast<void**>(&reactor_), 0, 0)); - if (reactor) - reactor->close_descriptor(impl.socket_, impl.reactor_data_); + if (r) + r->close_descriptor(impl.socket_, impl.reactor_data_); // The socket destructor must not block. If the user has changed the // linger option to block in the foreground, we will change it back to the @@ -2347,6 +1926,35 @@ private: } } + // Update the ID of the thread from which cancellation is safe. + void update_cancellation_thread_id(implementation_type& impl) + { +#if defined(BOOST_ASIO_ENABLE_CANCELIO) + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); +#else // defined(BOOST_ASIO_ENABLE_CANCELIO) + (void)impl; +#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) + } + + // Helper function to get the reactor. If no reactor has been created yet, a + // new one is obtained from the io_service and a pointer to it is cached in + // this service. + reactor& get_reactor() + { + reactor* r = static_cast<reactor*>( + interlocked_compare_exchange_pointer( + reinterpret_cast<void**>(&reactor_), 0, 0)); + if (!r) + { + r = &(use_service<reactor>(io_service_)); + interlocked_exchange_pointer(reinterpret_cast<void**>(&reactor_), r); + } + return *r; + } + // Helper function to emulate InterlockedCompareExchangePointer functionality // for: // - very old Platform SDKs; and @@ -2375,13 +1983,16 @@ private: #endif } + // The io_service used to obtain the reactor, if required. + boost::asio::io_service& io_service_; + // The IOCP service used for running asynchronous operations and dispatching // handlers. win_iocp_io_service& iocp_service_; // The reactor used for performing connect operations. This object is created // only if needed. - reactor_type* reactor_; + reactor* reactor_; // Mutex to protect access to the linked list of implementations. boost::asio::detail::mutex mutex_; diff --git a/3rdParty/Boost/src/boost/asio/detail/win_mutex.hpp b/3rdParty/Boost/src/boost/asio/detail/win_mutex.hpp index 107d707..176a5fd 100644 --- a/3rdParty/Boost/src/boost/asio/detail/win_mutex.hpp +++ b/3rdParty/Boost/src/boost/asio/detail/win_mutex.hpp @@ -66,15 +66,7 @@ public: // Lock the mutex. void lock() { - int error = do_lock(); - if (error != 0) - { - boost::system::system_error e( - boost::system::error_code(error, - boost::asio::error::get_system_category()), - "mutex"); - boost::throw_exception(e); - } + ::EnterCriticalSection(&crit_section_); } // Unlock the mutex. @@ -92,12 +84,20 @@ private: #if defined(__MINGW32__) // Not sure if MinGW supports structured exception handling, so for now // we'll just call the Windows API and hope. +# if defined(UNDER_CE) ::InitializeCriticalSection(&crit_section_); +# else + ::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000); +# endif return 0; #else __try { +# if defined(UNDER_CE) ::InitializeCriticalSection(&crit_section_); +# else + ::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000); +# endif } __except(GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) @@ -109,34 +109,6 @@ private: #endif } - // Locking must be performed in a separate function to lock() since the - // compiler does not support the use of structured exceptions and C++ - // exceptions in the same function. - int do_lock() - { -#if defined(__MINGW32__) - // Not sure if MinGW supports structured exception handling, so for now - // we'll just call the Windows API and hope. - ::EnterCriticalSection(&crit_section_); - return 0; -#else - __try - { - ::EnterCriticalSection(&crit_section_); - } - __except(GetExceptionCode() == STATUS_INVALID_HANDLE - || GetExceptionCode() == STATUS_NO_MEMORY - ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) - { - if (GetExceptionCode() == STATUS_NO_MEMORY) - return ERROR_OUTOFMEMORY; - return ERROR_INVALID_HANDLE; - } - - return 0; -#endif - } - ::CRITICAL_SECTION crit_section_; }; |