diff options
Diffstat (limited to 'ext/asio/detail')
104 files changed, 20811 insertions, 0 deletions
diff --git a/ext/asio/detail/base_from_completion_cond.hpp b/ext/asio/detail/base_from_completion_cond.hpp new file mode 100644 index 0000000000..90a11f9900 --- /dev/null +++ b/ext/asio/detail/base_from_completion_cond.hpp @@ -0,0 +1,65 @@ +// +// 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 ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP +#define ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/completion_condition.hpp" + +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 asio::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 asio::error_code& ec, + std::size_t total_transferred) + { + return transfer_all_t()(ec, total_transferred); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BASE_FROM_COMPLETION_COND_HPP diff --git a/ext/asio/detail/bind_handler.hpp b/ext/asio/detail/bind_handler.hpp new file mode 100644 index 0000000000..5d9eb004ae --- /dev/null +++ b/ext/asio/detail/bind_handler.hpp @@ -0,0 +1,349 @@ +// +// bind_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 ASIO_DETAIL_BIND_HANDLER_HPP +#define ASIO_DETAIL_BIND_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +namespace asio { +namespace detail { + +template <typename Handler, typename Arg1> +class binder1 +{ +public: + binder1(const Handler& handler, const Arg1& arg1) + : handler_(handler), + arg1_(arg1) + { + } + + void operator()() + { + handler_(arg1_); + } + + void operator()() const + { + handler_(arg1_); + } + +//private: + Handler handler_; + Arg1 arg1_; +}; + +template <typename Handler, typename Arg1> +inline void* asio_handler_allocate(std::size_t size, + binder1<Handler, Arg1>* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template <typename Handler, typename Arg1> +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder1<Handler, Arg1>* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template <typename Function, typename Handler, typename Arg1> +inline void asio_handler_invoke(const Function& function, + binder1<Handler, Arg1>* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template <typename Handler, typename Arg1> +inline binder1<Handler, Arg1> bind_handler(const Handler& handler, + const Arg1& arg1) +{ + return binder1<Handler, Arg1>(handler, arg1); +} + +template <typename Handler, typename Arg1, typename Arg2> +class binder2 +{ +public: + binder2(const Handler& handler, const Arg1& arg1, const Arg2& arg2) + : handler_(handler), + arg1_(arg1), + arg2_(arg2) + { + } + + void operator()() + { + handler_(arg1_, arg2_); + } + + void operator()() const + { + handler_(arg1_, arg2_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; +}; + +template <typename Handler, typename Arg1, typename Arg2> +inline void* asio_handler_allocate(std::size_t size, + binder2<Handler, Arg1, Arg2>* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template <typename Handler, typename Arg1, typename Arg2> +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder2<Handler, Arg1, Arg2>* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template <typename Function, typename Handler, typename Arg1, typename Arg2> +inline void asio_handler_invoke(const Function& function, + binder2<Handler, Arg1, Arg2>* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template <typename Handler, typename Arg1, typename Arg2> +inline binder2<Handler, Arg1, Arg2> bind_handler(const Handler& handler, + const Arg1& arg1, const Arg2& arg2) +{ + return binder2<Handler, Arg1, Arg2>(handler, arg1, arg2); +} + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3> +class binder3 +{ +public: + binder3(const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3) + : handler_(handler), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3) + { + } + + void operator()() + { + handler_(arg1_, arg2_, arg3_); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; +}; + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3> +inline void* asio_handler_allocate(std::size_t size, + binder3<Handler, Arg1, Arg2, Arg3>* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3> +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder3<Handler, Arg1, Arg2, Arg3>* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template <typename Function, typename Handler, typename Arg1, typename Arg2, + typename Arg3> +inline void asio_handler_invoke(const Function& function, + binder3<Handler, Arg1, Arg2, Arg3>* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3> +inline binder3<Handler, Arg1, Arg2, Arg3> bind_handler(const Handler& handler, + const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) +{ + return binder3<Handler, Arg1, Arg2, Arg3>(handler, arg1, arg2, arg3); +} + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3, + typename Arg4> +class binder4 +{ +public: + binder4(const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4) + : handler_(handler), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4) + { + } + + void operator()() + { + handler_(arg1_, arg2_, arg3_, arg4_); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_, arg4_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; + Arg4 arg4_; +}; + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3, + typename Arg4> +inline void* asio_handler_allocate(std::size_t size, + binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3, + typename Arg4> +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template <typename Function, typename Handler, typename Arg1, typename Arg2, + typename Arg3, typename Arg4> +inline void asio_handler_invoke(const Function& function, + binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3, + typename Arg4> +inline binder4<Handler, Arg1, Arg2, Arg3, Arg4> bind_handler( + const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4) +{ + return binder4<Handler, Arg1, Arg2, Arg3, Arg4>(handler, arg1, arg2, arg3, + arg4); +} + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> +class binder5 +{ +public: + binder5(const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) + : handler_(handler), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4), + arg5_(arg5) + { + } + + void operator()() + { + handler_(arg1_, arg2_, arg3_, arg4_, arg5_); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_, arg4_, arg5_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; + Arg4 arg4_; + Arg5 arg5_; +}; + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> +inline void* asio_handler_allocate(std::size_t size, + binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template <typename Function, typename Handler, typename Arg1, typename Arg2, + typename Arg3, typename Arg4, typename Arg5> +inline void asio_handler_invoke(const Function& function, + binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); +} + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> +inline binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5> bind_handler( + const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) +{ + return binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>(handler, arg1, arg2, + arg3, arg4, arg5); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BIND_HANDLER_HPP diff --git a/ext/asio/detail/buffer_resize_guard.hpp b/ext/asio/detail/buffer_resize_guard.hpp new file mode 100644 index 0000000000..368de323fe --- /dev/null +++ b/ext/asio/detail/buffer_resize_guard.hpp @@ -0,0 +1,70 @@ +// +// buffer_resize_guard.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 ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP +#define ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include <boost/limits.hpp> +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +// Helper class to manage buffer resizing in an exception safe way. +template <typename Buffer> +class buffer_resize_guard +{ +public: + // Constructor. + buffer_resize_guard(Buffer& buffer) + : buffer_(buffer), + old_size_(buffer.size()) + { + } + + // Destructor rolls back the buffer resize unless commit was called. + ~buffer_resize_guard() + { + if (old_size_ + != std::numeric_limits<size_t>::max BOOST_PREVENT_MACRO_SUBSTITUTION()) + { + buffer_.resize(old_size_); + } + } + + // Commit the resize transaction. + void commit() + { + old_size_ + = std::numeric_limits<size_t>::max BOOST_PREVENT_MACRO_SUBSTITUTION(); + } + +private: + // The buffer being managed. + Buffer& buffer_; + + // The size of the buffer at the time the guard was constructed. + size_t old_size_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP diff --git a/ext/asio/detail/buffer_sequence_adapter.hpp b/ext/asio/detail/buffer_sequence_adapter.hpp new file mode 100644 index 0000000000..6269d6ae28 --- /dev/null +++ b/ext/asio/detail/buffer_sequence_adapter.hpp @@ -0,0 +1,252 @@ +// +// 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 ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP +#define ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/buffer.hpp" + +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 asio::mutable_buffer& buffer) + { + buf.buf = asio::buffer_cast<char*>(buffer); + buf.len = asio::buffer_size(buffer); + } + + static void init_native_buffer(WSABUF& buf, + const asio::const_buffer& buffer) + { + buf.buf = const_cast<char*>(asio::buffer_cast<const char*>(buffer)); + buf.len = 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 asio::mutable_buffer& buffer) + { + init_iov_base(iov.iov_base, asio::buffer_cast<void*>(buffer)); + iov.iov_len = asio::buffer_size(buffer); + } + + static void init_native_buffer(iovec& iov, + const asio::const_buffer& buffer) + { + init_iov_base(iov.iov_base, const_cast<void*>( + asio::buffer_cast<const void*>(buffer))); + iov.iov_len = 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_ += 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 (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); + 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 (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, asio::mutable_buffers_1> + : buffer_sequence_adapter_base +{ +public: + explicit buffer_sequence_adapter( + const asio::mutable_buffers_1& buffers) + { + init_native_buffer(buffer_, buffers); + total_buffer_size_ = 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 asio::mutable_buffers_1& buffers) + { + return asio::buffer_size(buffers) == 0; + } + + static void validate(const asio::mutable_buffers_1& buffers) + { + asio::buffer_cast<const void*>(buffers); + } + + static Buffer first(const 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, asio::const_buffers_1> + : buffer_sequence_adapter_base +{ +public: + explicit buffer_sequence_adapter( + const asio::const_buffers_1& buffers) + { + init_native_buffer(buffer_, buffers); + total_buffer_size_ = 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 asio::const_buffers_1& buffers) + { + return asio::buffer_size(buffers) == 0; + } + + static void validate(const asio::const_buffers_1& buffers) + { + asio::buffer_cast<const void*>(buffers); + } + + static Buffer first(const asio::const_buffers_1& buffers) + { + return Buffer(buffers); + } + +private: + native_buffer_type buffer_; + std::size_t total_buffer_size_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BUFFER_SEQUENCE_ADAPTER_HPP diff --git a/ext/asio/detail/buffered_stream_storage.hpp b/ext/asio/detail/buffered_stream_storage.hpp new file mode 100644 index 0000000000..51f6556fcb --- /dev/null +++ b/ext/asio/detail/buffered_stream_storage.hpp @@ -0,0 +1,127 @@ +// +// buffered_stream_storage.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 ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP +#define ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include <cassert> +#include <cstddef> +#include <cstring> +#include <vector> +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +class buffered_stream_storage +{ +public: + // The type of the bytes stored in the buffer. + typedef unsigned char byte_type; + + // The type used for offsets into the buffer. + typedef std::size_t size_type; + + // Constructor. + explicit buffered_stream_storage(std::size_t capacity) + : begin_offset_(0), + end_offset_(0), + buffer_(capacity) + { + } + + /// Clear the buffer. + void clear() + { + begin_offset_ = 0; + end_offset_ = 0; + } + + // Return a pointer to the beginning of the unread data. + byte_type* data() + { + return &buffer_[0] + begin_offset_; + } + + // Return a pointer to the beginning of the unread data. + const byte_type* data() const + { + return &buffer_[0] + begin_offset_; + } + + // Is there no unread data in the buffer. + bool empty() const + { + return begin_offset_ == end_offset_; + } + + // Return the amount of unread data the is in the buffer. + size_type size() const + { + return end_offset_ - begin_offset_; + } + + // Resize the buffer to the specified length. + void resize(size_type length) + { + assert(length <= capacity()); + if (begin_offset_ + length <= capacity()) + { + end_offset_ = begin_offset_ + length; + } + else + { + using namespace std; // For memmove. + memmove(&buffer_[0], &buffer_[0] + begin_offset_, size()); + end_offset_ = length; + begin_offset_ = 0; + } + } + + // Return the maximum size for data in the buffer. + size_type capacity() const + { + return buffer_.size(); + } + + // Consume multiple bytes from the beginning of the buffer. + void consume(size_type count) + { + assert(begin_offset_ + count <= end_offset_); + begin_offset_ += count; + if (empty()) + clear(); + } + +private: + // The offset to the beginning of the unread data. + size_type begin_offset_; + + // The offset to the end of the unread data. + size_type end_offset_; + + // The data in the buffer. + std::vector<byte_type> buffer_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP diff --git a/ext/asio/detail/call_stack.hpp b/ext/asio/detail/call_stack.hpp new file mode 100644 index 0000000000..0e9ddafaa3 --- /dev/null +++ b/ext/asio/detail/call_stack.hpp @@ -0,0 +1,90 @@ +// +// call_stack.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 ASIO_DETAIL_CALL_STACK_HPP +#define ASIO_DETAIL_CALL_STACK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/tss_ptr.hpp" + +namespace asio { +namespace detail { + +// Helper class to determine whether or not the current thread is inside an +// invocation of io_service::run() for a specified io_service object. +template <typename Owner> +class call_stack +{ +public: + // Context class automatically pushes an owner on to the stack. + class context + : private noncopyable + { + public: + // Push the owner on to the stack. + explicit context(Owner* d) + : owner_(d), + next_(call_stack<Owner>::top_) + { + call_stack<Owner>::top_ = this; + } + + // Pop the owner from the stack. + ~context() + { + call_stack<Owner>::top_ = next_; + } + + private: + friend class call_stack<Owner>; + + // The owner associated with the context. + Owner* owner_; + + // The next element in the stack. + context* next_; + }; + + friend class context; + + // Determine whether the specified owner is on the stack. + static bool contains(Owner* d) + { + context* elem = top_; + while (elem) + { + if (elem->owner_ == d) + return true; + elem = elem->next_; + } + return false; + } + +private: + // The top of the stack of calls for the current thread. + static tss_ptr<context> top_; +}; + +template <typename Owner> +tss_ptr<typename call_stack<Owner>::context> +call_stack<Owner>::top_; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CALL_STACK_HPP diff --git a/ext/asio/detail/completion_handler.hpp b/ext/asio/detail/completion_handler.hpp new file mode 100644 index 0000000000..16167dfbe2 --- /dev/null +++ b/ext/asio/detail/completion_handler.hpp @@ -0,0 +1,71 @@ +// +// 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 ASIO_DETAIL_COMPLETION_HANDLER_HPP +#define ASIO_DETAIL_COMPLETION_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/operation.hpp" + +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, + asio::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(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_COMPLETION_HANDLER_HPP diff --git a/ext/asio/detail/consuming_buffers.hpp b/ext/asio/detail/consuming_buffers.hpp new file mode 100644 index 0000000000..80b6a8e803 --- /dev/null +++ b/ext/asio/detail/consuming_buffers.hpp @@ -0,0 +1,280 @@ +// +// consuming_buffers.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 ASIO_DETAIL_CONSUMING_BUFFERS_HPP +#define ASIO_DETAIL_CONSUMING_BUFFERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <algorithm> +#include <cstddef> +#include <boost/config.hpp> +#include <boost/iterator.hpp> +#include <boost/limits.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" + +namespace asio { +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<std::forward_iterator_tag, const Buffer> +{ +public: + // Default constructor creates an end iterator. + consuming_buffers_iterator() + : at_end_(true) + { + } + + // Construct with a buffer for the first entry and an iterator + // range for the remaining entries. + consuming_buffers_iterator(bool at_end, const Buffer& first, + Buffer_Iterator begin_remainder, Buffer_Iterator end_remainder, + std::size_t max_size) + : at_end_(max_size > 0 ? at_end : true), + first_(buffer(first, max_size)), + begin_remainder_(begin_remainder), + end_remainder_(end_remainder), + offset_(0), + max_size_(max_size) + { + } + + // 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_) + { + if (begin_remainder_ == end_remainder_ + || offset_ + buffer_size(first_) >= max_size_) + { + at_end_ = true; + } + else + { + offset_ += buffer_size(first_); + first_ = buffer(*begin_remainder_++, max_size_ - offset_); + } + } + } + + bool equal(const consuming_buffers_iterator& other) const + { + if (at_end_ && other.at_end_) + return true; + return !at_end_ && !other.at_end_ + && buffer_cast<const void*>(first_) + == buffer_cast<const void*>(other.first_) + && buffer_size(first_) == buffer_size(other.first_) + && begin_remainder_ == other.begin_remainder_ + && end_remainder_ == other.end_remainder_; + } + + const Buffer& dereference() const + { + return first_; + } + + bool at_end_; + Buffer first_; + Buffer_Iterator begin_remainder_; + Buffer_Iterator end_remainder_; + std::size_t offset_; + std::size_t max_size_; +}; + +// A proxy for a sub-range in a list of buffers. +template <typename Buffer, typename Buffers> +class consuming_buffers +{ +public: + // The type for each element in the list of buffers. + typedef Buffer value_type; + + // A forward-only iterator type that may be used to read elements. + typedef consuming_buffers_iterator<Buffer, typename Buffers::const_iterator> + const_iterator; + + // Construct to represent the entire list of buffers. + consuming_buffers(const Buffers& buffers) + : buffers_(buffers), + at_end_(buffers_.begin() == buffers_.end()), + first_(*buffers_.begin()), + begin_remainder_(buffers_.begin()), + max_size_((std::numeric_limits<std::size_t>::max)()) + { + if (!at_end_) + ++begin_remainder_; + } + + // Copy constructor. + consuming_buffers(const consuming_buffers& other) + : buffers_(other.buffers_), + at_end_(other.at_end_), + first_(other.first_), + begin_remainder_(buffers_.begin()), + max_size_(other.max_size_) + { + typename Buffers::const_iterator first = other.buffers_.begin(); + typename Buffers::const_iterator second = other.begin_remainder_; + std::advance(begin_remainder_, std::distance(first, second)); + } + + // Assignment operator. + consuming_buffers& operator=(const consuming_buffers& other) + { + buffers_ = other.buffers_; + at_end_ = other.at_end_; + first_ = other.first_; + begin_remainder_ = buffers_.begin(); + typename Buffers::const_iterator first = other.buffers_.begin(); + typename Buffers::const_iterator second = other.begin_remainder_; + std::advance(begin_remainder_, std::distance(first, second)); + max_size_ = other.max_size_; + return *this; + } + + // Get a forward-only iterator to the first element. + const_iterator begin() const + { + return const_iterator(at_end_, first_, + begin_remainder_, buffers_.end(), max_size_); + } + + // Get a forward-only iterator for one past the last element. + const_iterator end() const + { + return const_iterator(); + } + + // Set the maximum size for a single transfer. + void prepare(std::size_t max_size) + { + max_size_ = max_size; + } + + // Consume the specified number of bytes from the buffers. + void consume(std::size_t size) + { + // Remove buffers from the start until the specified size is reached. + while (size > 0 && !at_end_) + { + if (buffer_size(first_) <= size) + { + size -= buffer_size(first_); + if (begin_remainder_ == buffers_.end()) + at_end_ = true; + else + first_ = *begin_remainder_++; + } + else + { + first_ = first_ + size; + size = 0; + } + } + + // Remove any more empty buffers at the start. + while (!at_end_ && buffer_size(first_) == 0) + { + if (begin_remainder_ == buffers_.end()) + at_end_ = true; + else + first_ = *begin_remainder_++; + } + } + +private: + Buffers buffers_; + bool at_end_; + Buffer first_; + typename Buffers::const_iterator begin_remainder_; + std::size_t max_size_; +}; + +// Specialisation for null_buffers to ensure that the null_buffers type is +// always passed through to the underlying read or write operation. +template <typename Buffer> +class consuming_buffers<Buffer, asio::null_buffers> + : public asio::null_buffers +{ +public: + consuming_buffers(const asio::null_buffers&) + { + // No-op. + } + + void prepare(std::size_t) + { + // No-op. + } + + void consume(std::size_t) + { + // No-op. + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CONSUMING_BUFFERS_HPP diff --git a/ext/asio/detail/deadline_timer_service.hpp b/ext/asio/detail/deadline_timer_service.hpp new file mode 100644 index 0000000000..6daf7acb35 --- /dev/null +++ b/ext/asio/detail/deadline_timer_service.hpp @@ -0,0 +1,222 @@ +// +// deadline_timer_service.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 ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP +#define ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <cstddef> +#include <boost/config.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_op.hpp" +#include "asio/detail/timer_queue.hpp" +#include "asio/detail/timer_scheduler.hpp" + +namespace asio { +namespace detail { + +template <typename Time_Traits> +class deadline_timer_service +{ +public: + // The time type. + typedef typename Time_Traits::time_type time_type; + + // The duration type. + typedef typename Time_Traits::duration_type duration_type; + + // The implementation type of the timer. This type is dependent on the + // underlying implementation of the timer service. + struct implementation_type + : private asio::detail::noncopyable + { + time_type expiry; + bool might_have_pending_waits; + }; + + // Constructor. + deadline_timer_service(asio::io_service& io_service) + : scheduler_(asio::use_service<timer_scheduler>(io_service)) + { + scheduler_.init_task(); + scheduler_.add_timer_queue(timer_queue_); + } + + // Destructor. + ~deadline_timer_service() + { + scheduler_.remove_timer_queue(timer_queue_); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + // Construct a new timer implementation. + void construct(implementation_type& impl) + { + impl.expiry = time_type(); + impl.might_have_pending_waits = false; + } + + // Destroy a timer implementation. + void destroy(implementation_type& impl) + { + asio::error_code ec; + cancel(impl, ec); + } + + // Cancel any asynchronous wait operations associated with the timer. + std::size_t cancel(implementation_type& impl, asio::error_code& ec) + { + if (!impl.might_have_pending_waits) + { + ec = asio::error_code(); + return 0; + } + std::size_t count = scheduler_.cancel_timer(timer_queue_, &impl); + impl.might_have_pending_waits = false; + ec = asio::error_code(); + return count; + } + + // Get the expiry time for the timer as an absolute time. + time_type expires_at(const implementation_type& impl) const + { + return impl.expiry; + } + + // Set the expiry time for the timer as an absolute time. + std::size_t expires_at(implementation_type& impl, + const time_type& expiry_time, asio::error_code& ec) + { + std::size_t count = cancel(impl, ec); + impl.expiry = expiry_time; + ec = asio::error_code(); + return count; + } + + // Get the expiry time for the timer relative to now. + duration_type expires_from_now(const implementation_type& impl) const + { + return Time_Traits::subtract(expires_at(impl), Time_Traits::now()); + } + + // Set the expiry time for the timer relative to now. + std::size_t expires_from_now(implementation_type& impl, + const duration_type& expiry_time, asio::error_code& ec) + { + return expires_at(impl, + Time_Traits::add(Time_Traits::now(), expiry_time), ec); + } + + // Perform a blocking wait on the timer. + void wait(implementation_type& impl, asio::error_code& ec) + { + time_type now = Time_Traits::now(); + while (Time_Traits::less_than(now, impl.expiry)) + { + boost::posix_time::time_duration timeout = + Time_Traits::to_posix_duration(Time_Traits::subtract(impl.expiry, now)); + ::timeval tv; + tv.tv_sec = timeout.total_seconds(); + tv.tv_usec = timeout.total_microseconds() % 1000000; + asio::error_code ec; + socket_ops::select(0, 0, 0, 0, &tv, ec); + now = Time_Traits::now(); + } + ec = asio::error_code(); + } + + template <typename Handler> + class wait_handler : public timer_op + { + public: + wait_handler(Handler handler) + : timer_op(&wait_handler::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // 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, asio::error_code> + handler(h->handler_, h->ec_); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + 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, ptr.get(), &impl); + ptr.release(); + } + +private: + // The queue of timers. + timer_queue<Time_Traits> timer_queue_; + + // The object that schedules and executes timers. Usually a reactor. + timer_scheduler& scheduler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP diff --git a/ext/asio/detail/descriptor_ops.hpp b/ext/asio/detail/descriptor_ops.hpp new file mode 100644 index 0000000000..ea3731e7ac --- /dev/null +++ b/ext/asio/detail/descriptor_ops.hpp @@ -0,0 +1,176 @@ +// +// descriptor_ops.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 ASIO_DETAIL_DESCRIPTOR_OPS_HPP +#define ASIO_DETAIL_DESCRIPTOR_OPS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include <cerrno> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/socket_types.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +namespace asio { +namespace detail { +namespace descriptor_ops { + +inline void clear_error(asio::error_code& ec) +{ + errno = 0; + ec = asio::error_code(); +} + +template <typename ReturnType> +inline ReturnType error_wrapper(ReturnType return_value, + asio::error_code& ec) +{ + ec = asio::error_code(errno, + asio::error::get_system_category()); + return return_value; +} + +inline int open(const char* path, int flags, asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(::open(path, flags), ec); + if (result >= 0) + clear_error(ec); + return result; +} + +inline int close(int d, asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(::close(d), ec); + if (result == 0) + clear_error(ec); + return result; +} + +inline void init_buf_iov_base(void*& base, void* addr) +{ + base = addr; +} + +template <typename T> +inline void init_buf_iov_base(T& base, void* addr) +{ + base = static_cast<T>(addr); +} + +typedef iovec buf; + +inline void init_buf(buf& b, void* data, size_t size) +{ + init_buf_iov_base(b.iov_base, data); + b.iov_len = size; +} + +inline void init_buf(buf& b, const void* data, size_t size) +{ + init_buf_iov_base(b.iov_base, const_cast<void*>(data)); + b.iov_len = size; +} + +inline int scatter_read(int d, buf* bufs, size_t count, + asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(::readv(d, bufs, static_cast<int>(count)), ec); + if (result >= 0) + clear_error(ec); + return result; +} + +inline int gather_write(int d, const buf* bufs, size_t count, + asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(::writev(d, bufs, static_cast<int>(count)), ec); + if (result >= 0) + clear_error(ec); + return result; +} + +inline int ioctl(int d, long cmd, ioctl_arg_type* arg, + asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(::ioctl(d, cmd, arg), ec); + if (result >= 0) + clear_error(ec); + return result; +} + +inline int fcntl(int d, long cmd, asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(::fcntl(d, cmd), ec); + if (result != -1) + clear_error(ec); + return result; +} + +inline int fcntl(int d, long cmd, long arg, asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(::fcntl(d, cmd, arg), ec); + if (result != -1) + clear_error(ec); + return result; +} + +inline int poll_read(int d, asio::error_code& ec) +{ + clear_error(ec); + pollfd fds; + fds.fd = d; + fds.events = POLLIN; + fds.revents = 0; + clear_error(ec); + int result = error_wrapper(::poll(&fds, 1, -1), ec); + if (result >= 0) + clear_error(ec); + return result; +} + +inline int poll_write(int d, asio::error_code& ec) +{ + clear_error(ec); + pollfd fds; + fds.fd = d; + fds.events = POLLOUT; + fds.revents = 0; + clear_error(ec); + int result = error_wrapper(::poll(&fds, 1, -1), ec); + if (result >= 0) + clear_error(ec); + return result; +} + +} // namespace descriptor_ops +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_DESCRIPTOR_OPS_HPP diff --git a/ext/asio/detail/dev_poll_reactor.hpp b/ext/asio/detail/dev_poll_reactor.hpp new file mode 100644 index 0000000000..1367b7190f --- /dev/null +++ b/ext/asio/detail/dev_poll_reactor.hpp @@ -0,0 +1,453 @@ +// +// dev_poll_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 ASIO_DETAIL_DEV_POLL_REACTOR_HPP +#define ASIO_DETAIL_DEV_POLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/dev_poll_reactor_fwd.hpp" + +#if defined(ASIO_HAS_DEV_POLL) + +#include "asio/detail/push_options.hpp" +#include <cstddef> +#include <vector> +#include <boost/config.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/throw_exception.hpp> +#include <sys/devpoll.h> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_op.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_fwd.hpp" +#include "asio/detail/timer_queue_set.hpp" + +namespace asio { +namespace detail { + +class dev_poll_reactor + : public 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 + { + }; + + // Constructor. + dev_poll_reactor(asio::io_service& io_service) + : 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()), + interrupter_(), + shutdown_(false) + { + // Add the interrupter's descriptor to /dev/poll. + ::pollfd ev = { 0 }; + ev.fd = interrupter_.read_descriptor(); + ev.events = POLLIN | POLLERR; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); + } + + // Destructor. + ~dev_poll_reactor() + { + shutdown_service(); + ::close(dev_poll_fd_); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + op_queue<operation> ops; + + for (int i = 0; i < max_ops; ++i) + op_queue_[i].get_all_operations(ops); + + timer_queues_.get_all_timers(ops); + } + + // Initialise the task. + void 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&) + { + return 0; + } + + // 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) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (allow_speculative) + { + if (op_type != read_op || !op_queue_[except_op].has_operation(descriptor)) + { + if (!op_queue_[op_type].has_operation(descriptor)) + { + if (op->perform()) + { + lock.unlock(); + io_service_.post_immediate_completion(op); + return; + } + } + } + } + + 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 = POLLERR | POLLHUP; + if (op_type == read_op + || op_queue_[read_op].has_operation(descriptor)) + ev.events |= POLLIN; + if (op_type == write_op + || op_queue_[write_op].has_operation(descriptor)) + ev.events |= POLLOUT; + if (op_type == except_op + || op_queue_[except_op].has_operation(descriptor)) + ev.events |= POLLPRI; + interrupter_.interrupt(); + } + } + + // 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&) + { + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); + } + + // 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&) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from /dev/poll. + ::pollfd& ev = add_pending_event_change(descriptor); + ev.events = POLLREMOVE; + interrupter_.interrupt(); + + // Cancel any outstanding operations associated with the descriptor. + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); + } + + // Add a new timer queue to the reactor. + template <typename Time_Traits> + void add_timer_queue(timer_queue<Time_Traits>& timer_queue) + { + asio::detail::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) + { + asio::detail::mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&timer_queue); + } + + // 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, timer_op* op, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + { + bool earliest = timer_queue.enqueue_timer(time, op, token); + io_service_.work_started(); + if (earliest) + interrupter_.interrupt(); + } + } + + // 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) + { + asio::detail::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; + } + + // Run /dev/poll once until interrupted or events are ready to be dispatched. + void run(bool block, op_queue<operation>& ops) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + 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(); + if (events_size > 0) + { + errno = 0; + int result = ::write(dev_poll_fd_, + &pending_event_changes_[0], events_size); + if (result != static_cast<int>(events_size)) + { + asio::error_code ec = asio::error_code( + errno, asio::error::get_system_category()); + for (std::size_t i = 0; i < pending_event_changes_.size(); ++i) + { + int descriptor = pending_event_changes_[i].fd; + for (int j = 0; j < max_ops; ++j) + op_queue_[j].cancel_operations(descriptor, ops, ec); + } + } + pending_event_changes_.clear(); + pending_event_change_index_.clear(); + } + + int timeout = block ? get_timeout() : 0; + lock.unlock(); + + // Block on the /dev/poll descriptor. + ::pollfd events[128] = { { 0 } }; + ::dvpoll dp = { 0 }; + dp.dp_fds = events; + dp.dp_nfds = 128; + dp.dp_timeout = timeout; + int num_events = ::ioctl(dev_poll_fd_, DP_POLL, &dp); + + lock.lock(); + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + int descriptor = events[i].fd; + if (descriptor == interrupter_.read_descriptor()) + { + interrupter_.reset(); + } + else + { + bool more_reads = false; + bool more_writes = false; + bool more_except = false; + + // 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 = + op_queue_[except_op].perform_operations(descriptor, ops); + else + more_except = op_queue_[except_op].has_operation(descriptor); + + if (events[i].events & (POLLIN | POLLERR | POLLHUP)) + more_reads = op_queue_[read_op].perform_operations(descriptor, ops); + else + more_reads = op_queue_[read_op].has_operation(descriptor); + + if (events[i].events & (POLLOUT | POLLERR | POLLHUP)) + more_writes = op_queue_[write_op].perform_operations(descriptor, ops); + else + more_writes = op_queue_[write_op].has_operation(descriptor); + + if ((events[i].events & (POLLERR | POLLHUP)) != 0 + && !more_except && !more_reads && !more_writes) + { + // If we have an event and no operations associated with the + // descriptor then we need to delete the descriptor from /dev/poll. + // The poll operation can produce POLLHUP or POLLERR events when there + // is no operation pending, so if we do not remove the descriptor we + // can end up in a tight polling loop. + ::pollfd ev = { 0 }; + ev.fd = descriptor; + ev.events = POLLREMOVE; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); + } + else + { + ::pollfd ev = { 0 }; + ev.fd = descriptor; + ev.events = POLLERR | POLLHUP; + if (more_reads) + ev.events |= POLLIN; + if (more_writes) + ev.events |= POLLOUT; + if (more_except) + ev.events |= POLLPRI; + ev.revents = 0; + int result = ::write(dev_poll_fd_, &ev, sizeof(ev)); + if (result != sizeof(ev)) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + for (int j = 0; j < max_ops; ++j) + op_queue_[j].cancel_operations(descriptor, ops, ec); + } + } + } + } + timer_queues_.get_ready_timers(ops); + } + + // Interrupt the select loop. + void interrupt() + { + interrupter_.interrupt(); + } + +private: + // Create the /dev/poll file descriptor. Throws an exception if the descriptor + // cannot be created. + static int do_dev_poll_create() + { + int fd = ::open("/dev/poll", O_RDWR); + if (fd == -1) + { + boost::throw_exception( + asio::system_error( + asio::error_code(errno, + asio::error::get_system_category()), + "/dev/poll")); + } + return fd; + } + + // 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() + { + // 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. + 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, + const asio::error_code& ec) + { + 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(); + } + + // Add a pending event entry for the given descriptor. + ::pollfd& add_pending_event_change(int descriptor) + { + hash_map<int, std::size_t>::iterator iter + = pending_event_change_index_.find(descriptor); + if (iter == pending_event_change_index_.end()) + { + std::size_t index = pending_event_changes_.size(); + pending_event_changes_.reserve(pending_event_changes_.size() + 1); + pending_event_change_index_.insert(std::make_pair(descriptor, index)); + pending_event_changes_.push_back(::pollfd()); + pending_event_changes_[index].fd = descriptor; + pending_event_changes_[index].revents = 0; + return pending_event_changes_[index]; + } + else + { + return pending_event_changes_[iter->second]; + } + } + + // The io_service implementation used to post completions. + io_service_impl& io_service_; + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The /dev/poll file descriptor. + int dev_poll_fd_; + + // Vector of /dev/poll events waiting to be written to the descriptor. + std::vector< ::pollfd> pending_event_changes_; + + // Hash map to associate a descriptor with a pending event change index. + hash_map<int, std::size_t> pending_event_change_index_; + + // The interrupter is used to break a blocking DP_POLL operation. + select_interrupter interrupter_; + + // The queues of read, write and except operations. + reactor_op_queue<socket_type> op_queue_[max_ops]; + + // The timer queues. + timer_queue_set timer_queues_; + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_DEV_POLL) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_DEV_POLL_REACTOR_HPP diff --git a/ext/asio/detail/dev_poll_reactor_fwd.hpp b/ext/asio/detail/dev_poll_reactor_fwd.hpp new file mode 100644 index 0000000000..f7f1aebab4 --- /dev/null +++ b/ext/asio/detail/dev_poll_reactor_fwd.hpp @@ -0,0 +1,39 @@ +// +// dev_poll_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 ASIO_DETAIL_DEV_POLL_REACTOR_FWD_HPP +#define ASIO_DETAIL_DEV_POLL_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#if !defined(ASIO_DISABLE_DEV_POLL) +#if defined(__sun) // This service is only supported on Solaris. + +// Define this to indicate that /dev/poll is supported on the target platform. +#define ASIO_HAS_DEV_POLL 1 + +namespace asio { +namespace detail { + +class dev_poll_reactor; + +} // namespace detail +} // namespace asio + +#endif // defined(__sun) +#endif // !defined(ASIO_DISABLE_DEV_POLL) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_DEV_POLL_REACTOR_FWD_HPP diff --git a/ext/asio/detail/epoll_reactor.hpp b/ext/asio/detail/epoll_reactor.hpp new file mode 100644 index 0000000000..20ef576198 --- /dev/null +++ b/ext/asio/detail/epoll_reactor.hpp @@ -0,0 +1,507 @@ +// +// epoll_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 ASIO_DETAIL_EPOLL_REACTOR_HPP +#define ASIO_DETAIL_EPOLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/epoll_reactor_fwd.hpp" + +#if defined(ASIO_HAS_EPOLL) + +#include "asio/detail/push_options.hpp" +#include <cstddef> +#include <sys/epoll.h> +#include <boost/config.hpp> +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_op.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_fwd.hpp" +#include "asio/detail/timer_queue_set.hpp" + +#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) +# define ASIO_HAS_TIMERFD 1 +#endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) + +#if defined(ASIO_HAS_TIMERFD) +# include "asio/detail/push_options.hpp" +# include <sys/timerfd.h> +# include "asio/detail/pop_options.hpp" +#endif // defined(ASIO_HAS_TIMERFD) + +namespace asio { +namespace detail { + +class epoll_reactor + : public asio::detail::service_base<epoll_reactor> +{ +public: + enum { read_op = 0, write_op = 1, + connect_op = 1, except_op = 2, max_ops = 3 }; + + // Per-descriptor queues. + struct descriptor_state + { + 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(asio::io_service& io_service) + : asio::detail::service_base<epoll_reactor>(io_service), + io_service_(use_service<io_service_impl>(io_service)), + mutex_(), + epoll_fd_(do_epoll_create()), +#if defined(ASIO_HAS_TIMERFD) + timer_fd_(timerfd_create(CLOCK_MONOTONIC, 0)), +#else // defined(ASIO_HAS_TIMERFD) + timer_fd_(-1), +#endif // defined(ASIO_HAS_TIMERFD) + interrupter_(), + shutdown_(false) + { + // Add the interrupter's descriptor to epoll. + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLET; + ev.data.ptr = &interrupter_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); + interrupter_.interrupt(); + + // Add the timer descriptor to epoll. + if (timer_fd_ != -1) + { + ev.events = EPOLLIN | EPOLLERR; + ev.data.ptr = &timer_fd_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &ev); + } + } + + // Destructor. + ~epoll_reactor() + { + close(epoll_fd_); + if (timer_fd_ != -1) + close(timer_fd_); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + op_queue<operation> ops; + + descriptor_map::iterator iter = registered_descriptors_.begin(); + descriptor_map::iterator end = registered_descriptors_.end(); + while (iter != end) + { + for (int i = 0; i < max_ops; ++i) + ops.push(iter->second.op_queue_[i]); + iter->second.shutdown_ = true; + ++iter; + } + + timer_queues_.get_all_timers(ops); + } + + // Initialise the task. + void 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 descriptor, + per_descriptor_data& descriptor_data) + { + 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; + + epoll_event ev = { 0, { 0 } }; + 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 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) + { + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + if (descriptor_data->shutdown_) + return; + + if (descriptor_data->op_queue_[op_type].empty()) + { + if (allow_speculative + && (op_type != read_op + || descriptor_data->op_queue_[except_op].empty())) + { + if (op->perform()) + { + descriptor_lock.unlock(); + io_service_.post_immediate_completion(op); + return; + } + } + else + { + 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(); + } + + // 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 UNUSED_PARAM, per_descriptor_data& descriptor_data) + { + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + op_queue<operation> ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_lock.unlock(); + + io_service_.post_deferred_completions(ops); + } + + // 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) + { + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + + // 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; + + op_queue<operation> ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_lock.unlock(); + + registered_descriptors_.erase(descriptor); + + descriptors_lock.unlock(); + + 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) + { + 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) + { + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&timer_queue); + } + + // 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, timer_op* op, void* token) + { + mutex::scoped_lock lock(mutex_); + if (!shutdown_) + { + bool earliest = timer_queue.enqueue_timer(time, op, token); + io_service_.work_started(); + if (earliest) + { +#if defined(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(ASIO_HAS_TIMERFD) + interrupter_.interrupt(); + } + } + } + + // 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) + { + 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; + } + + // Run epoll once until interrupted or events are ready to be dispatched. + void run(bool block, op_queue<operation>& ops) + { + // Calculate a timeout only if timerfd is not used. + int timeout; + if (timer_fd_ != -1) + timeout = block ? -1 : 0; + else + { + mutex::scoped_lock lock(mutex_); + timeout = block ? get_timeout() : 0; + } + + // Block on the epoll descriptor. + epoll_event events[128]; + int num_events = epoll_wait(epoll_fd_, events, 128, timeout); + +#if defined(ASIO_HAS_TIMERFD) + bool check_timers = (timer_fd_ == -1); +#else // defined(ASIO_HAS_TIMERFD) + bool check_timers = true; +#endif // defined(ASIO_HAS_TIMERFD) + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + void* ptr = events[i].data.ptr; + if (ptr == &interrupter_) + { + // 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(ASIO_HAS_TIMERFD) + if (timer_fd_ == -1) + check_timers = true; +#else // defined(ASIO_HAS_TIMERFD) + check_timers = true; +#endif // defined(ASIO_HAS_TIMERFD) + } +#if defined(ASIO_HAS_TIMERFD) + else if (ptr == &timer_fd_) + { + check_timers = true; + } +#endif // defined(ASIO_HAS_TIMERFD) + else + { + descriptor_state* descriptor_data = static_cast<descriptor_state*>(ptr); + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI }; + for (int j = max_ops - 1; j >= 0; --j) + { + if (events[i].events & (flag[j] | EPOLLERR | EPOLLHUP)) + { + while (reactor_op* op = descriptor_data->op_queue_[j].front()) + { + if (op->perform()) + { + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + else + break; + } + } + } + } + } + + if (check_timers) + { + mutex::scoped_lock common_lock(mutex_); + timer_queues_.get_ready_timers(ops); + +#if defined(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(ASIO_HAS_TIMERFD) + } + } + + // Interrupt the select loop. + void 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 }; + + // Create the epoll file descriptor. Throws an exception if the descriptor + // cannot be created. + static int do_epoll_create() + { + int fd = epoll_create(epoll_size); + if (fd == -1) + { + boost::throw_exception( + asio::system_error( + asio::error_code(errno, + asio::error::get_system_category()), + "epoll")); + } + return fd; + } + + // 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() + { + // 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. + return timer_queues_.wait_duration_msec(5 * 60 * 1000); + } + +#if defined(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) + { + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + + 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(ASIO_HAS_TIMERFD) + + // The io_service implementation used to post completions. + io_service_impl& io_service_; + + // Mutex to protect access to internal data. + mutex mutex_; + + // The epoll file descriptor. + int epoll_fd_; + + // The timer file descriptor. + int timer_fd_; + + // The interrupter is used to break a blocking epoll_wait call. + select_interrupter interrupter_; + + // The timer queues. + timer_queue_set timer_queues_; + + // Whether the service has been shut down. + bool shutdown_; + + // 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 +} // namespace asio + +#endif // defined(ASIO_HAS_EPOLL) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EPOLL_REACTOR_HPP diff --git a/ext/asio/detail/epoll_reactor_fwd.hpp b/ext/asio/detail/epoll_reactor_fwd.hpp new file mode 100644 index 0000000000..266bccdab0 --- /dev/null +++ b/ext/asio/detail/epoll_reactor_fwd.hpp @@ -0,0 +1,46 @@ +// +// epoll_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 ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP +#define ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#if !defined(ASIO_DISABLE_EPOLL) +#if defined(__linux__) // This service is only supported on Linux. + +#include "asio/detail/push_options.hpp" +#include <linux/version.h> +#include "asio/detail/pop_options.hpp" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) // Only kernels >= 2.5.45. + +// Define this to indicate that epoll is supported on the target platform. +#define ASIO_HAS_EPOLL 1 + +namespace asio { +namespace detail { + +class epoll_reactor; + +} // namespace detail +} // namespace asio + +#endif // LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) +#endif // defined(__linux__) +#endif // !defined(ASIO_DISABLE_EPOLL) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP diff --git a/ext/asio/detail/event.hpp b/ext/asio/detail/event.hpp new file mode 100644 index 0000000000..65aa4cba7f --- /dev/null +++ b/ext/asio/detail/event.hpp @@ -0,0 +1,50 @@ +// +// event.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 ASIO_DETAIL_EVENT_HPP +#define ASIO_DETAIL_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +# include "asio/detail/null_event.hpp" +#elif defined(BOOST_WINDOWS) +# include "asio/detail/win_event.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_event.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +typedef null_event event; +#elif defined(BOOST_WINDOWS) +typedef win_event event; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_event event; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EVENT_HPP diff --git a/ext/asio/detail/eventfd_select_interrupter.hpp b/ext/asio/detail/eventfd_select_interrupter.hpp new file mode 100644 index 0000000000..63b7ac4ff5 --- /dev/null +++ b/ext/asio/detail/eventfd_select_interrupter.hpp @@ -0,0 +1,166 @@ +// +// eventfd_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Roelof Naude (roelof.naude at gmail 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 ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(__linux__) +# if !defined(ASIO_DISABLE_EVENTFD) +# include <linux/version.h> +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) +# define ASIO_HAS_EVENTFD +# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) +# endif // !defined(ASIO_DISABLE_EVENTFD) +#endif // defined(__linux__) + +#if defined(ASIO_HAS_EVENTFD) + +#include "asio/detail/push_options.hpp" +#include <fcntl.h> +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 +# include <asm/unistd.h> +#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 +# include <sys/eventfd.h> +#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +class eventfd_select_interrupter +{ +public: + // Constructor. + eventfd_select_interrupter() + { +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 + write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0); +#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 + write_descriptor_ = read_descriptor_ = ::eventfd(0, 0); +#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 + if (read_descriptor_ != -1) + { + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + } + else + { + int pipe_fds[2]; + if (pipe(pipe_fds) == 0) + { + read_descriptor_ = pipe_fds[0]; + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + write_descriptor_ = pipe_fds[1]; + ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); + } + else + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::system_error e(ec, "eventfd_select_interrupter"); + boost::throw_exception(e); + } + } + } + + // Destructor. + ~eventfd_select_interrupter() + { + if (write_descriptor_ != -1 && write_descriptor_ != read_descriptor_) + ::close(write_descriptor_); + if (read_descriptor_ != -1) + ::close(read_descriptor_); + } + + // Interrupt the select call. + void interrupt() + { + uint64_t counter(1UL); + int result = ::write(write_descriptor_, &counter, sizeof(uint64_t)); + (void)result; + } + + // Reset the select interrupt. Returns true if the call was interrupted. + bool reset() + { + if (write_descriptor_ == read_descriptor_) + { + for (;;) + { + // Only perform one read. The kernel maintains an atomic counter. + uint64_t counter(0); + errno = 0; + int bytes_read = ::read(read_descriptor_, &counter, sizeof(uint64_t)); + if (bytes_read < 0 && errno == EINTR) + continue; + bool was_interrupted = (bytes_read > 0); + return was_interrupted; + } + } + else + { + for (;;) + { + // Clear all data from the pipe. + char data[1024]; + int bytes_read = ::read(read_descriptor_, data, sizeof(data)); + if (bytes_read < 0 && errno == EINTR) + continue; + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = ::read(read_descriptor_, data, sizeof(data)); + return was_interrupted; + } + } + } + + // Get the read descriptor to be passed to select. + int read_descriptor() const + { + return read_descriptor_; + } + +private: + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // 64bit value will be written on the other end of the connection and this + // descriptor will become readable. + int read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // 64bit non-zero value may be written to this to wake up the select which is + // waiting for the other end to become readable. This descriptor will only + // differ from the read descriptor when a pipe is used. + int write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_EVENTFD) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP diff --git a/ext/asio/detail/fd_set_adapter.hpp b/ext/asio/detail/fd_set_adapter.hpp new file mode 100644 index 0000000000..a575491bfb --- /dev/null +++ b/ext/asio/detail/fd_set_adapter.hpp @@ -0,0 +1,41 @@ +// +// fd_set_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 ASIO_DETAIL_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/posix_fd_set_adapter.hpp" +#include "asio/detail/win_fd_set_adapter.hpp" + +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef win_fd_set_adapter fd_set_adapter; +#else +typedef posix_fd_set_adapter fd_set_adapter; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_FD_SET_ADAPTER_HPP diff --git a/ext/asio/detail/fenced_block.hpp b/ext/asio/detail/fenced_block.hpp new file mode 100644 index 0000000000..60198bc65b --- /dev/null +++ b/ext/asio/detail/fenced_block.hpp @@ -0,0 +1,70 @@ +// +// 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 ASIO_DETAIL_FENCED_BLOCK_HPP +#define ASIO_DETAIL_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +# include "asio/detail/null_fenced_block.hpp" +#elif defined(__MACH__) && defined(__APPLE__) +# include "asio/detail/macos_fenced_block.hpp" +#elif defined(__sun) +# include "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 "asio/detail/gcc_fenced_block.hpp" +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +# include "asio/detail/gcc_x86_fenced_block.hpp" +#elif defined(BOOST_WINDOWS) && !defined(UNDER_CE) +# include "asio/detail/win_fenced_block.hpp" +#else +# include "asio/detail/null_fenced_block.hpp" +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) || defined(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 + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_FENCED_BLOCK_HPP diff --git a/ext/asio/detail/gcc_fenced_block.hpp b/ext/asio/detail/gcc_fenced_block.hpp new file mode 100644 index 0000000000..23303fbf2e --- /dev/null +++ b/ext/asio/detail/gcc_fenced_block.hpp @@ -0,0 +1,63 @@ +// +// 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 ASIO_DETAIL_GCC_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "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 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 + +#endif // defined(__GNUC__) + // && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) + // && !defined(__INTEL_COMPILER) && !defined(__ICL) + // && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_GCC_FENCED_BLOCK_HPP diff --git a/ext/asio/detail/gcc_x86_fenced_block.hpp b/ext/asio/detail/gcc_x86_fenced_block.hpp new file mode 100644 index 0000000000..48119f4c39 --- /dev/null +++ b/ext/asio/detail/gcc_x86_fenced_block.hpp @@ -0,0 +1,61 @@ +// +// 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 ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +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 + +#endif // defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP diff --git a/ext/asio/detail/handler_alloc_helpers.hpp b/ext/asio/detail/handler_alloc_helpers.hpp new file mode 100644 index 0000000000..0ac42a7b12 --- /dev/null +++ b/ext/asio/detail/handler_alloc_helpers.hpp @@ -0,0 +1,259 @@ +// +// handler_alloc_helpers.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 ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP +#define ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/detail/workaround.hpp> +#include <boost/utility/addressof.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/handler_alloc_hook.hpp" +#include "asio/detail/noncopyable.hpp" + +// Calls to asio_handler_allocate and asio_handler_deallocate must be made from +// a namespace that does not contain any overloads of these functions. The +// asio_handler_alloc_helpers namespace is defined here for that purpose. +namespace asio_handler_alloc_helpers { + +template <typename Handler> +inline void* allocate(std::size_t s, Handler& h) +{ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) \ + || BOOST_WORKAROUND(__GNUC__, < 3) + return ::operator new(s); +#else + using namespace asio; + return asio_handler_allocate(s, boost::addressof(h)); +#endif +} + +template <typename Handler> +inline void deallocate(void* p, std::size_t s, Handler& h) +{ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) \ + || BOOST_WORKAROUND(__GNUC__, < 3) + ::operator delete(p); +#else + using namespace asio; + asio_handler_deallocate(p, s, boost::addressof(h)); +#endif +} + +} // namespace asio_handler_alloc_helpers + +namespace asio { +namespace detail { + +// Traits for handler allocation. +template <typename Handler, typename Object> +struct handler_alloc_traits +{ + typedef Handler handler_type; + typedef Object value_type; + typedef Object* pointer_type; + BOOST_STATIC_CONSTANT(std::size_t, value_size = sizeof(Object)); +}; + +template <typename Alloc_Traits> +class handler_ptr; + +// Helper class to provide RAII on uninitialised handler memory. +template <typename Alloc_Traits> +class raw_handler_ptr + : private noncopyable +{ +public: + typedef typename Alloc_Traits::handler_type handler_type; + typedef typename Alloc_Traits::value_type value_type; + typedef typename Alloc_Traits::pointer_type pointer_type; + BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size); + + // Constructor allocates the memory. + raw_handler_ptr(handler_type& handler) + : handler_(handler), + pointer_(static_cast<pointer_type>( + asio_handler_alloc_helpers::allocate(value_size, handler_))) + { + } + + // Destructor automatically deallocates memory, unless it has been stolen by + // a handler_ptr object. + ~raw_handler_ptr() + { + if (pointer_) + asio_handler_alloc_helpers::deallocate( + pointer_, value_size, handler_); + } + +private: + friend class handler_ptr<Alloc_Traits>; + handler_type& handler_; + pointer_type pointer_; +}; + +// Helper class to provide RAII on uninitialised handler memory. +template <typename Alloc_Traits> +class handler_ptr + : private noncopyable +{ +public: + typedef typename Alloc_Traits::handler_type handler_type; + typedef typename Alloc_Traits::value_type value_type; + typedef typename Alloc_Traits::pointer_type pointer_type; + BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size); + typedef raw_handler_ptr<Alloc_Traits> raw_ptr_type; + + // Take ownership of existing memory. + handler_ptr(handler_type& handler, pointer_type pointer) + : handler_(handler), + pointer_(pointer) + { + } + + // Construct object in raw memory and take ownership if construction succeeds. + handler_ptr(raw_ptr_type& raw_ptr) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template <typename Arg1> + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template <typename Arg1, typename Arg2> + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template <typename Arg1, typename Arg2, typename Arg3> + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template <typename Arg1, typename Arg2, typename Arg3, typename Arg4> + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, + typename Arg5> + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, + Arg5& a5) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, + typename Arg5, typename Arg6> + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, + Arg5& a5, Arg6& a6) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, + typename Arg5, typename Arg6, typename Arg7> + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, + Arg5& a5, Arg6& a6, Arg7& a7) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6, a7)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, + typename Arg5, typename Arg6, typename Arg7, typename Arg8> + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, + Arg5& a5, Arg6& a6, Arg7& a7, Arg8& a8) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type( + a1, a2, a3, a4, a5, a6, a7, a8)) + { + raw_ptr.pointer_ = 0; + } + + // Destructor automatically deallocates memory, unless it has been released. + ~handler_ptr() + { + reset(); + } + + // Get the memory. + pointer_type get() const + { + return pointer_; + } + + // Release ownership of the memory. + pointer_type release() + { + pointer_type tmp = pointer_; + pointer_ = 0; + return tmp; + } + + // Explicitly destroy and deallocate the memory. + void reset() + { + if (pointer_) + { + pointer_->value_type::~value_type(); + asio_handler_alloc_helpers::deallocate( + pointer_, value_size, handler_); + pointer_ = 0; + } + } + +private: + handler_type& handler_; + pointer_type pointer_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP diff --git a/ext/asio/detail/handler_invoke_helpers.hpp b/ext/asio/detail/handler_invoke_helpers.hpp new file mode 100644 index 0000000000..8332567c9f --- /dev/null +++ b/ext/asio/detail/handler_invoke_helpers.hpp @@ -0,0 +1,49 @@ +// +// handler_invoke_helpers.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 ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP +#define ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/detail/workaround.hpp> +#include <boost/utility/addressof.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/handler_invoke_hook.hpp" + +// Calls to asio_handler_invoke must be made from a namespace that does not +// contain overloads of this function. The asio_handler_invoke_helpers +// namespace is defined here for that purpose. +namespace asio_handler_invoke_helpers { + +template <typename Function, typename Context> +inline void invoke(const Function& function, Context& context) +{ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) \ + || BOOST_WORKAROUND(__GNUC__, < 3) + Function tmp(function); + tmp(); +#else + using namespace asio; + asio_handler_invoke(function, boost::addressof(context)); +#endif +} + +} // namespace asio_handler_invoke_helpers + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP diff --git a/ext/asio/detail/hash_map.hpp b/ext/asio/detail/hash_map.hpp new file mode 100644 index 0000000000..b2a1517865 --- /dev/null +++ b/ext/asio/detail/hash_map.hpp @@ -0,0 +1,324 @@ +// +// hash_map.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 ASIO_DETAIL_HASH_MAP_HPP +#define ASIO_DETAIL_HASH_MAP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <cassert> +#include <list> +#include <utility> +#include <boost/functional/hash.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +template <typename T> +inline std::size_t calculate_hash_value(const T& t) +{ + return boost::hash_value(t); +} + +#if defined(_WIN64) +inline std::size_t calculate_hash_value(SOCKET s) +{ + return static_cast<std::size_t>(s); +} +#endif // defined(_WIN64) + +// Note: assumes K and V are POD types. +template <typename K, typename V> +class hash_map + : private noncopyable +{ +public: + // The type of a value in the map. + typedef std::pair<K, V> value_type; + + // The type of a non-const iterator over the hash map. + typedef typename std::list<value_type>::iterator iterator; + + // The type of a const iterator over the hash map. + typedef typename std::list<value_type>::const_iterator const_iterator; + + // Constructor. + hash_map() + : size_(0), + buckets_(0), + num_buckets_(0) + { + } + + // Destructor. + ~hash_map() + { + delete[] buckets_; + } + + // Get an iterator for the beginning of the map. + iterator begin() + { + return values_.begin(); + } + + // Get an iterator for the beginning of the map. + const_iterator begin() const + { + return values_.begin(); + } + + // Get an iterator for the end of the map. + iterator end() + { + return values_.end(); + } + + // Get an iterator for the end of the map. + const_iterator end() const + { + return values_.end(); + } + + // Check whether the map is empty. + bool empty() const + { + return values_.empty(); + } + + // Find an entry in the map. + iterator find(const K& k) + { + if (num_buckets_) + { + 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(); + } + + // Find an entry in the map. + const_iterator find(const K& k) const + { + if (num_buckets_) + { + size_t bucket = calculate_hash_value(k) % num_buckets_; + const_iterator it = buckets_[bucket].first; + if (it == values_.end()) + return it; + const_iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == k) + return it; + ++it; + } + } + return values_.end(); + } + + // Insert a new entry into the map. + std::pair<iterator, bool> insert(const value_type& v) + { + if (size_ + 1 >= num_buckets_) + rehash(hash_size(size_ + 1)); + size_t bucket = calculate_hash_value(v.first) % num_buckets_; + iterator it = buckets_[bucket].first; + if (it == values_.end()) + { + buckets_[bucket].first = buckets_[bucket].last = + values_insert(values_.end(), v); + ++size_; + return std::pair<iterator, bool>(buckets_[bucket].last, true); + } + iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == v.first) + return std::pair<iterator, bool>(it, false); + ++it; + } + buckets_[bucket].last = values_insert(end, v); + ++size_; + return std::pair<iterator, bool>(buckets_[bucket].last, true); + } + + // Erase an entry from the map. + void erase(iterator it) + { + assert(it != values_.end()); + + 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) + buckets_[bucket].first = buckets_[bucket].last = values_.end(); + else if (is_first) + ++buckets_[bucket].first; + else if (is_last) + --buckets_[bucket].last; + + values_erase(it); + --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() + { + // Clear the values. + values_.clear(); + size_ = 0; + + // Initialise all buckets to empty. + iterator end = values_.end(); + for (size_t i = 0; i < num_buckets_; ++i) + buckets_[i].first = buckets_[i].last = end; + } + +private: + // Calculate the hash size for the specified number of elements. + static std::size_t hash_size(std::size_t num_elems) + { + static std::size_t sizes[] = + { +#if defined(ASIO_HASH_MAP_BUCKETS) + ASIO_HASH_MAP_BUCKETS +#else // ASIO_HASH_MAP_BUCKETS + 3, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, + 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, + 12582917, 25165843 +#endif // ASIO_HASH_MAP_BUCKETS + }; + const std::size_t nth_size = sizeof(sizes) / sizeof(std::size_t) - 1; + for (std::size_t i = 0; i < nth_size; ++i) + if (num_elems < sizes[i]) + return sizes[i]; + return sizes[nth_size]; + } + + // Re-initialise the hash from the values already contained in the list. + void rehash(std::size_t num_buckets) + { + if (num_buckets == num_buckets_) + return; + num_buckets_ = num_buckets; + + iterator end = values_.end(); + + // Update number of buckets and initialise all buckets to empty. + 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) % num_buckets_; + if (buckets_[bucket].last == end) + { + buckets_[bucket].first = buckets_[bucket].last = iter++; + } + else if (++buckets_[bucket].last == iter) + { + ++iter; + } + else + { + values_.splice(buckets_[bucket].last, values_, iter++); + --buckets_[bucket].last; + } + } + } + + // Insert an element into the values list by splicing from the spares list, + // if a spare is available, and otherwise by inserting a new element. + iterator values_insert(iterator it, const value_type& v) + { + if (spares_.empty()) + { + return values_.insert(it, v); + } + else + { + spares_.front() = v; + values_.splice(it, spares_, spares_.begin()); + return --it; + } + } + + // Erase an element from the values list by splicing it to the spares list. + void values_erase(iterator it) + { + *it = value_type(); + spares_.splice(spares_.begin(), values_, it); + } + + // The number of elements in the hash. + std::size_t size_; + + // The list of all values in the hash map. + std::list<value_type> values_; + + // The list of spare nodes waiting to be recycled. Assumes that POD types only + // are stored in the hash map. + std::list<value_type> spares_; + + // The type for a bucket in the hash table. + struct bucket_type + { + iterator first; + iterator last; + }; + + // The buckets in the hash. + bucket_type* buckets_; + + // The number of buckets in the hash. + std::size_t num_buckets_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HASH_MAP_HPP diff --git a/ext/asio/detail/io_control.hpp b/ext/asio/detail/io_control.hpp new file mode 100644 index 0000000000..df7171cbe2 --- /dev/null +++ b/ext/asio/detail/io_control.hpp @@ -0,0 +1,137 @@ +// +// io_control.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 ASIO_DETAIL_IO_CONTROL_HPP +#define ASIO_DETAIL_IO_CONTROL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <cstddef> +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { +namespace io_control { + +// IO control command for non-blocking I/O. +class non_blocking_io +{ +public: + // Default constructor. + non_blocking_io() + : value_(0) + { + } + + // Construct with a specific command value. + non_blocking_io(bool value) + : value_(value ? 1 : 0) + { + } + + // Get the name of the IO control command. + int name() const + { + return FIONBIO; + } + + // Set the value of the I/O control command. + void set(bool value) + { + value_ = value ? 1 : 0; + } + + // Get the current value of the I/O control command. + bool get() const + { + return value_ != 0; + } + + // Get the address of the command data. + detail::ioctl_arg_type* data() + { + return &value_; + } + + // Get the address of the command data. + const detail::ioctl_arg_type* data() const + { + return &value_; + } + +private: + detail::ioctl_arg_type value_; +}; + +// I/O control command for getting number of bytes available. +class bytes_readable +{ +public: + // Default constructor. + bytes_readable() + : value_(0) + { + } + + // Construct with a specific command value. + bytes_readable(std::size_t value) + : value_(static_cast<detail::ioctl_arg_type>(value)) + { + } + + // Get the name of the IO control command. + int name() const + { + return FIONREAD; + } + + // Set the value of the I/O control command. + void set(std::size_t value) + { + value_ = static_cast<detail::ioctl_arg_type>(value); + } + + // Get the current value of the I/O control command. + std::size_t get() const + { + return static_cast<std::size_t>(value_); + } + + // Get the address of the command data. + detail::ioctl_arg_type* data() + { + return &value_; + } + + // Get the address of the command data. + const detail::ioctl_arg_type* data() const + { + return &value_; + } + +private: + detail::ioctl_arg_type value_; +}; + +} // namespace io_control +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IO_CONTROL_HPP diff --git a/ext/asio/detail/kqueue_reactor.hpp b/ext/asio/detail/kqueue_reactor.hpp new file mode 100644 index 0000000000..1e118b3118 --- /dev/null +++ b/ext/asio/detail/kqueue_reactor.hpp @@ -0,0 +1,485 @@ +// +// kqueue_reactor.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze 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 ASIO_DETAIL_KQUEUE_REACTOR_HPP +#define ASIO_DETAIL_KQUEUE_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/kqueue_reactor_fwd.hpp" + +#if defined(ASIO_HAS_KQUEUE) + +#include "asio/detail/push_options.hpp" +#include <cstddef> +#include <sys/types.h> +#include <sys/event.h> +#include <sys/time.h> +#include <boost/config.hpp> +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_op.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_fwd.hpp" +#include "asio/detail/timer_queue_set.hpp" + +// Older versions of Mac OS X may not define EV_OOBAND. +#if !defined(EV_OOBAND) +# define EV_OOBAND EV_FLAG1 +#endif // !defined(EV_OOBAND) + +namespace asio { +namespace detail { + +class kqueue_reactor + : public asio::detail::service_base<kqueue_reactor> +{ +public: + enum op_types { read_op = 0, write_op = 1, + connect_op = 1, except_op = 2, max_ops = 3 }; + + // Per-descriptor queues. + struct descriptor_state + { + 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(asio::io_service& io_service) + : asio::detail::service_base<kqueue_reactor>(io_service), + io_service_(use_service<io_service_impl>(io_service)), + mutex_(), + kqueue_fd_(do_kqueue_create()), + interrupter_(), + shutdown_(false) + { + // 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() + { + close(kqueue_fd_); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + op_queue<operation> ops; + + descriptor_map::iterator iter = registered_descriptors_.begin(); + descriptor_map::iterator end = registered_descriptors_.end(); + while (iter != end) + { + for (int i = 0; i < max_ops; ++i) + ops.push(iter->second.op_queue_[i]); + iter->second.shutdown_ = true; + ++iter; + } + + timer_queues_.get_all_timers(ops); + } + + // Initialise the task. + void 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 descriptor, + per_descriptor_data& descriptor_data) + { + 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 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) + { + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + if (descriptor_data->shutdown_) + return; + + bool first = descriptor_data->op_queue_[op_type].empty(); + if (first) + { + if (allow_speculative) + { + 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; + } + } + } + } + + descriptor_data->op_queue_[op_type].push(op); + io_service_.work_started(); + + if (first) + { + struct kevent event; + 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) + { + op->ec_ = asio::error_code(errno, + asio::error::get_system_category()); + descriptor_data->op_queue_[op_type].pop(); + io_service_.post_deferred_completion(op); + } + } + } + + // 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) + { + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + op_queue<operation> ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_lock.unlock(); + + io_service_.post_deferred_completions(ops); + } + + // 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) + { + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + + // 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; + + op_queue<operation> ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_lock.unlock(); + + registered_descriptors_.erase(descriptor); + + descriptors_lock.unlock(); + + 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) + { + 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) + { + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&timer_queue); + } + + // 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, timer_op* op, void* token) + { + mutex::scoped_lock lock(mutex_); + if (!shutdown_) + { + bool earliest = timer_queue.enqueue_timer(time, op, token); + io_service_.work_started(); + if (earliest) + interrupt(); + } + } + + // 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) + { + 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; + } + + // Run the kqueue loop. + void run(bool block, op_queue<operation>& ops) + { + 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; + + lock.unlock(); + + // Block on the kqueue descriptor. + struct kevent events[128]; + 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; + void* ptr = events[i].udata; + if (ptr == &interrupter_) + { + // 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 + { + 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) + { + 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_ = asio::error_code(events[i].data, + 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; + } + } + } + } + + // Renew registration for event notifications. + struct kevent event; + switch (events[i].filter) + { + 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; + } + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + asio::error_code error(errno, + asio::error::get_system_category()); + 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); + } + } + } + } + } + + lock.lock(); + timer_queues_.get_ready_timers(ops); + } + + // Interrupt the kqueue loop. + void 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() + { + int fd = kqueue(); + if (fd == -1) + { + boost::throw_exception( + asio::system_error( + asio::error_code(errno, + asio::error::get_system_category()), + "kqueue")); + } + return fd; + } + + // Get the timeout value for the kevent call. + timespec* get_timeout(timespec& ts) + { + // 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. + long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + return &ts; + } + + // The io_service implementation used to post completions. + io_service_impl& io_service_; + + // Mutex to protect access to internal data. + mutex mutex_; + + // The kqueue file descriptor. + int kqueue_fd_; + + // The interrupter is used to break a blocking kevent call. + select_interrupter interrupter_; + + // The timer queues. + timer_queue_set timer_queues_; + + // Whether the service has been shut down. + bool shutdown_; + + // 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 +} // namespace asio + +#endif // defined(ASIO_HAS_KQUEUE) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_KQUEUE_REACTOR_HPP diff --git a/ext/asio/detail/kqueue_reactor_fwd.hpp b/ext/asio/detail/kqueue_reactor_fwd.hpp new file mode 100644 index 0000000000..abbc0c7d7b --- /dev/null +++ b/ext/asio/detail/kqueue_reactor_fwd.hpp @@ -0,0 +1,44 @@ +// +// kqueue_reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze 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 ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP +#define ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#if !defined(ASIO_DISABLE_KQUEUE) + +#if (defined(__MACH__) && defined(__APPLE__)) \ + || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + +// Define this to indicate that kqueue is supported on the target platform. +#define ASIO_HAS_KQUEUE 1 + +namespace asio { +namespace detail { + +class kqueue_reactor; + +} // namespace detail +} // namespace asio + +#endif // (defined(__MACH__) && defined(__APPLE__)) + // || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + +#endif // !defined(ASIO_DISABLE_KQUEUE) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP diff --git a/ext/asio/detail/local_free_on_block_exit.hpp b/ext/asio/detail/local_free_on_block_exit.hpp new file mode 100644 index 0000000000..554943c817 --- /dev/null +++ b/ext/asio/detail/local_free_on_block_exit.hpp @@ -0,0 +1,59 @@ +// +// local_free_on_block_exit.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 ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP +#define ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +class local_free_on_block_exit + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + explicit local_free_on_block_exit(void* p) + : p_(p) + { + } + + // Destructor restores the previous signal mask. + ~local_free_on_block_exit() + { + ::LocalFree(p_); + } + +private: + void* p_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP diff --git a/ext/asio/detail/macos_fenced_block.hpp b/ext/asio/detail/macos_fenced_block.hpp new file mode 100644 index 0000000000..3c303d62bc --- /dev/null +++ b/ext/asio/detail/macos_fenced_block.hpp @@ -0,0 +1,57 @@ +// +// 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 ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP +#define ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(__MACH__) && defined(__APPLE__) + +#include "asio/detail/push_options.hpp" +#include <libkern/OSAtomic.h> +#include "asio/detail/pop_options.hpp" + +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 + +#endif // defined(__MACH__) && defined(__APPLE__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP diff --git a/ext/asio/detail/mutex.hpp b/ext/asio/detail/mutex.hpp new file mode 100644 index 0000000000..024ec7f43f --- /dev/null +++ b/ext/asio/detail/mutex.hpp @@ -0,0 +1,50 @@ +// +// mutex.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 ASIO_DETAIL_MUTEX_HPP +#define ASIO_DETAIL_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +# include "asio/detail/null_mutex.hpp" +#elif defined(BOOST_WINDOWS) +# include "asio/detail/win_mutex.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_mutex.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +typedef null_mutex mutex; +#elif defined(BOOST_WINDOWS) +typedef win_mutex mutex; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_mutex mutex; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_MUTEX_HPP diff --git a/ext/asio/detail/noncopyable.hpp b/ext/asio/detail/noncopyable.hpp new file mode 100644 index 0000000000..8b73ff0407 --- /dev/null +++ b/ext/asio/detail/noncopyable.hpp @@ -0,0 +1,55 @@ +// +// noncopyable.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 ASIO_DETAIL_NONCOPYABLE_HPP +#define ASIO_DETAIL_NONCOPYABLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include <boost/noncopyable.hpp> +#include <boost/detail/workaround.hpp> +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +// Redefine the noncopyable class for Borland C++ since that compiler does not +// apply the empty base optimisation unless the base class contains a dummy +// char data member. +class noncopyable +{ +protected: + noncopyable() {} + ~noncopyable() {} +private: + noncopyable(const noncopyable&); + const noncopyable& operator=(const noncopyable&); + char dummy_; +}; +#else +using boost::noncopyable; +#endif + +} // namespace detail + +using asio::detail::noncopyable; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NONCOPYABLE_HPP diff --git a/ext/asio/detail/null_buffers_op.hpp b/ext/asio/detail/null_buffers_op.hpp new file mode 100644 index 0000000000..b44de2f077 --- /dev/null +++ b/ext/asio/detail/null_buffers_op.hpp @@ -0,0 +1,77 @@ +// +// 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 ASIO_DETAIL_NULL_BUFFERS_OP_HPP +#define ASIO_DETAIL_NULL_BUFFERS_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/reactor_op.hpp" + +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, + asio::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, asio::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_BUFFERS_OP_HPP diff --git a/ext/asio/detail/null_event.hpp b/ext/asio/detail/null_event.hpp new file mode 100644 index 0000000000..bcea31b804 --- /dev/null +++ b/ext/asio/detail/null_event.hpp @@ -0,0 +1,77 @@ +// +// null_event.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 ASIO_DETAIL_NULL_EVENT_HPP +#define ASIO_DETAIL_NULL_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class null_event + : private noncopyable +{ +public: + // Constructor. + null_event() + { + } + + // Destructor. + ~null_event() + { + } + + // Signal the event. + template <typename Lock> + void signal(Lock&) + { + } + + // Signal the event and unlock the mutex. + template <typename Lock> + void signal_and_unlock(Lock&) + { + } + + // Reset the event. + template <typename Lock> + void clear(Lock&) + { + } + + // Wait for the event to become signalled. + template <typename Lock> + void wait(Lock&) + { + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_EVENT_HPP diff --git a/ext/asio/detail/null_fenced_block.hpp b/ext/asio/detail/null_fenced_block.hpp new file mode 100644 index 0000000000..dd9a095a72 --- /dev/null +++ b/ext/asio/detail/null_fenced_block.hpp @@ -0,0 +1,43 @@ +// +// 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 ASIO_DETAIL_NULL_FENCED_BLOCK_HPP +#define ASIO_DETAIL_NULL_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class null_fenced_block + : private noncopyable +{ +public: + // Constructor. + null_fenced_block() + { + } + + // Destructor. + ~null_fenced_block() + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_FENCED_BLOCK_HPP diff --git a/ext/asio/detail/null_mutex.hpp b/ext/asio/detail/null_mutex.hpp new file mode 100644 index 0000000000..6661ef8324 --- /dev/null +++ b/ext/asio/detail/null_mutex.hpp @@ -0,0 +1,66 @@ +// +// null_mutex.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 ASIO_DETAIL_NULL_MUTEX_HPP +#define ASIO_DETAIL_NULL_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +namespace asio { +namespace detail { + +class null_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock<null_mutex> scoped_lock; + + // Constructor. + null_mutex() + { + } + + // Destructor. + ~null_mutex() + { + } + + // Lock the mutex. + void lock() + { + } + + // Unlock the mutex. + void unlock() + { + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_MUTEX_HPP diff --git a/ext/asio/detail/null_signal_blocker.hpp b/ext/asio/detail/null_signal_blocker.hpp new file mode 100644 index 0000000000..a5db315acb --- /dev/null +++ b/ext/asio/detail/null_signal_blocker.hpp @@ -0,0 +1,63 @@ +// +// null_signal_blocker.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 ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class null_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + null_signal_blocker() + { + } + + // Destructor restores the previous signal mask. + ~null_signal_blocker() + { + } + + // Block all signals for the calling thread. + void block() + { + } + + // Restore the previous signal mask. + void unblock() + { + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP diff --git a/ext/asio/detail/null_thread.hpp b/ext/asio/detail/null_thread.hpp new file mode 100644 index 0000000000..d96883f336 --- /dev/null +++ b/ext/asio/detail/null_thread.hpp @@ -0,0 +1,68 @@ +// +// null_thread.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 ASIO_DETAIL_NULL_THREAD_HPP +#define ASIO_DETAIL_NULL_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/push_options.hpp" +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class null_thread + : private noncopyable +{ +public: + // Constructor. + template <typename Function> + null_thread(Function f) + { + asio::system_error e( + asio::error::operation_not_supported, "thread"); + boost::throw_exception(e); + } + + // Destructor. + ~null_thread() + { + } + + // Wait for the thread to exit. + void join() + { + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_THREAD_HPP diff --git a/ext/asio/detail/null_tss_ptr.hpp b/ext/asio/detail/null_tss_ptr.hpp new file mode 100644 index 0000000000..112b4761c6 --- /dev/null +++ b/ext/asio/detail/null_tss_ptr.hpp @@ -0,0 +1,70 @@ +// +// null_tss_ptr.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 ASIO_DETAIL_NULL_TSS_PTR_HPP +#define ASIO_DETAIL_NULL_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +template <typename T> +class null_tss_ptr + : private noncopyable +{ +public: + // Constructor. + null_tss_ptr() + : value_(0) + { + } + + // Destructor. + ~null_tss_ptr() + { + } + + // Get the value. + operator T*() const + { + return value_; + } + + // Set the value. + void operator=(T* value) + { + value_ = value; + } + +private: + T* value_; +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_TSS_PTR_HPP diff --git a/ext/asio/detail/old_win_sdk_compat.hpp b/ext/asio/detail/old_win_sdk_compat.hpp new file mode 100644 index 0000000000..70e5916d6a --- /dev/null +++ b/ext/asio/detail/old_win_sdk_compat.hpp @@ -0,0 +1,340 @@ +// +// old_win_sdk_compat.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 ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP +#define ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +// Guess whether we are building against on old Platform SDK. +#if !defined(IN6ADDR_ANY_INIT) +#define ASIO_HAS_OLD_WIN_SDK 1 +#endif // !defined(IN6ADDR_ANY_INIT) + +#if defined(ASIO_HAS_OLD_WIN_SDK) + +// Emulation of types that are missing from old Platform SDKs. +// +// N.B. this emulation is also used if building for a Windows 2000 target with +// a recent (i.e. Vista or later) SDK, as the SDK does not provide IPv6 support +// in that case. + +namespace asio { +namespace detail { + +enum +{ + sockaddr_storage_maxsize = 128, // Maximum size. + sockaddr_storage_alignsize = (sizeof(__int64)), // Desired alignment. + sockaddr_storage_pad1size = (sockaddr_storage_alignsize - sizeof(short)), + sockaddr_storage_pad2size = (sockaddr_storage_maxsize - + (sizeof(short) + sockaddr_storage_pad1size + sockaddr_storage_alignsize)) +}; + +struct sockaddr_storage_emulation +{ + short ss_family; + char __ss_pad1[sockaddr_storage_pad1size]; + __int64 __ss_align; + char __ss_pad2[sockaddr_storage_pad2size]; +}; + +struct in6_addr_emulation +{ + union + { + u_char Byte[16]; + u_short Word[8]; + } u; +}; + +#if !defined(s6_addr) +# define _S6_un u +# define _S6_u8 Byte +# define s6_addr _S6_un._S6_u8 +#endif // !defined(s6_addr) + +struct sockaddr_in6_emulation +{ + short sin6_family; + u_short sin6_port; + u_long sin6_flowinfo; + in6_addr_emulation sin6_addr; + u_long sin6_scope_id; +}; + +struct ipv6_mreq_emulation +{ + in6_addr_emulation ipv6mr_multiaddr; + unsigned int ipv6mr_interface; +}; + +#if !defined(IN6ADDR_ANY_INIT) +# define IN6ADDR_ANY_INIT { 0 } +#endif + +#if !defined(IN6ADDR_LOOPBACK_INIT) +# define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } +#endif + +struct addrinfo_emulation +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char* ai_canonname; + sockaddr* ai_addr; + addrinfo_emulation* ai_next; +}; + +#if !defined(AI_PASSIVE) +# define AI_PASSIVE 0x1 +#endif + +#if !defined(AI_CANONNAME) +# define AI_CANONNAME 0x2 +#endif + +#if !defined(AI_NUMERICHOST) +# define AI_NUMERICHOST 0x4 +#endif + +#if !defined(EAI_AGAIN) +# define EAI_AGAIN WSATRY_AGAIN +#endif + +#if !defined(EAI_BADFLAGS) +# define EAI_BADFLAGS WSAEINVAL +#endif + +#if !defined(EAI_FAIL) +# define EAI_FAIL WSANO_RECOVERY +#endif + +#if !defined(EAI_FAMILY) +# define EAI_FAMILY WSAEAFNOSUPPORT +#endif + +#if !defined(EAI_MEMORY) +# define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY +#endif + +#if !defined(EAI_NODATA) +# define EAI_NODATA WSANO_DATA +#endif + +#if !defined(EAI_NONAME) +# define EAI_NONAME WSAHOST_NOT_FOUND +#endif + +#if !defined(EAI_SERVICE) +# define EAI_SERVICE WSATYPE_NOT_FOUND +#endif + +#if !defined(EAI_SOCKTYPE) +# define EAI_SOCKTYPE WSAESOCKTNOSUPPORT +#endif + +#if !defined(NI_NOFQDN) +# define NI_NOFQDN 0x01 +#endif + +#if !defined(NI_NUMERICHOST) +# define NI_NUMERICHOST 0x02 +#endif + +#if !defined(NI_NAMEREQD) +# define NI_NAMEREQD 0x04 +#endif + +#if !defined(NI_NUMERICSERV) +# define NI_NUMERICSERV 0x08 +#endif + +#if !defined(NI_DGRAM) +# define NI_DGRAM 0x10 +#endif + +#if !defined(IPPROTO_IPV6) +# define IPPROTO_IPV6 41 +#endif + +#if !defined(IPV6_UNICAST_HOPS) +# define IPV6_UNICAST_HOPS 4 +#endif + +#if !defined(IPV6_MULTICAST_IF) +# define IPV6_MULTICAST_IF 9 +#endif + +#if !defined(IPV6_MULTICAST_HOPS) +# define IPV6_MULTICAST_HOPS 10 +#endif + +#if !defined(IPV6_MULTICAST_LOOP) +# define IPV6_MULTICAST_LOOP 11 +#endif + +#if !defined(IPV6_JOIN_GROUP) +# define IPV6_JOIN_GROUP 12 +#endif + +#if !defined(IPV6_LEAVE_GROUP) +# define IPV6_LEAVE_GROUP 13 +#endif + +inline int IN6_IS_ADDR_UNSPECIFIED(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0) + && (a->s6_addr[1] == 0) + && (a->s6_addr[2] == 0) + && (a->s6_addr[3] == 0) + && (a->s6_addr[4] == 0) + && (a->s6_addr[5] == 0) + && (a->s6_addr[6] == 0) + && (a->s6_addr[7] == 0) + && (a->s6_addr[8] == 0) + && (a->s6_addr[9] == 0) + && (a->s6_addr[10] == 0) + && (a->s6_addr[11] == 0) + && (a->s6_addr[12] == 0) + && (a->s6_addr[13] == 0) + && (a->s6_addr[14] == 0) + && (a->s6_addr[15] == 0)); +} + +inline int IN6_IS_ADDR_LOOPBACK(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0) + && (a->s6_addr[1] == 0) + && (a->s6_addr[2] == 0) + && (a->s6_addr[3] == 0) + && (a->s6_addr[4] == 0) + && (a->s6_addr[5] == 0) + && (a->s6_addr[6] == 0) + && (a->s6_addr[7] == 0) + && (a->s6_addr[8] == 0) + && (a->s6_addr[9] == 0) + && (a->s6_addr[10] == 0) + && (a->s6_addr[11] == 0) + && (a->s6_addr[12] == 0) + && (a->s6_addr[13] == 0) + && (a->s6_addr[14] == 0) + && (a->s6_addr[15] == 1)); +} + +inline int IN6_IS_ADDR_MULTICAST(const in6_addr_emulation* a) +{ + return (a->s6_addr[0] == 0xff); +} + +inline int IN6_IS_ADDR_LINKLOCAL(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0x80)); +} + +inline int IN6_IS_ADDR_SITELOCAL(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0xc0)); +} + +inline int IN6_IS_ADDR_V4MAPPED(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0) + && (a->s6_addr[1] == 0) + && (a->s6_addr[2] == 0) + && (a->s6_addr[3] == 0) + && (a->s6_addr[4] == 0) + && (a->s6_addr[5] == 0) + && (a->s6_addr[6] == 0) + && (a->s6_addr[7] == 0) + && (a->s6_addr[8] == 0) + && (a->s6_addr[9] == 0) + && (a->s6_addr[10] == 0xff) + && (a->s6_addr[11] == 0xff)); +} + +inline int IN6_IS_ADDR_V4COMPAT(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0) + && (a->s6_addr[1] == 0) + && (a->s6_addr[2] == 0) + && (a->s6_addr[3] == 0) + && (a->s6_addr[4] == 0) + && (a->s6_addr[5] == 0) + && (a->s6_addr[6] == 0) + && (a->s6_addr[7] == 0) + && (a->s6_addr[8] == 0) + && (a->s6_addr[9] == 0) + && (a->s6_addr[10] == 0xff) + && (a->s6_addr[11] == 0xff) + && !((a->s6_addr[12] == 0) + && (a->s6_addr[13] == 0) + && (a->s6_addr[14] == 0) + && ((a->s6_addr[15] == 0) || (a->s6_addr[15] == 1)))); +} + +inline int IN6_IS_ADDR_MC_NODELOCAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 1); +} + +inline int IN6_IS_ADDR_MC_LINKLOCAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 2); +} + +inline int IN6_IS_ADDR_MC_SITELOCAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 5); +} + +inline int IN6_IS_ADDR_MC_ORGLOCAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 8); +} + +inline int IN6_IS_ADDR_MC_GLOBAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 0xe); +} + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_OLD_WIN_SDK) + +// Even newer Platform SDKs that support IPv6 may not define IPV6_V6ONLY. +#if !defined(IPV6_V6ONLY) +# define IPV6_V6ONLY 27 +#endif + +// Some SDKs (e.g. Windows CE) don't define IPPROTO_ICMPV6. +#if !defined(IPPROTO_ICMPV6) +# define IPPROTO_ICMPV6 58 +#endif + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP diff --git a/ext/asio/detail/op_queue.hpp b/ext/asio/detail/op_queue.hpp new file mode 100644 index 0000000000..ccf8b9a5c2 --- /dev/null +++ b/ext/asio/detail/op_queue.hpp @@ -0,0 +1,156 @@ +// +// 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 ASIO_DETAIL_OP_QUEUE_HPP +#define ASIO_DETAIL_OP_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" + +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 + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_OP_QUEUE_HPP diff --git a/ext/asio/detail/operation.hpp b/ext/asio/detail/operation.hpp new file mode 100644 index 0000000000..6aba361218 --- /dev/null +++ b/ext/asio/detail/operation.hpp @@ -0,0 +1,43 @@ +// +// 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 ASIO_DETAIL_OPERATION_HPP +#define ASIO_DETAIL_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_operation.hpp" +#else +# include "asio/detail/reactor_fwd.hpp" +# include "asio/detail/task_io_service_operation.hpp" +#endif + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_IOCP) +typedef win_iocp_operation operation; +#else +typedef task_io_service_operation<reactor> operation; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_OPERATION_HPP diff --git a/ext/asio/detail/pipe_select_interrupter.hpp b/ext/asio/detail/pipe_select_interrupter.hpp new file mode 100644 index 0000000000..74695994dc --- /dev/null +++ b/ext/asio/detail/pipe_select_interrupter.hpp @@ -0,0 +1,120 @@ +// +// pipe_select_interrupter.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 ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" +#include <fcntl.h> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +class pipe_select_interrupter +{ +public: + // Constructor. + pipe_select_interrupter() + { + int pipe_fds[2]; + if (pipe(pipe_fds) == 0) + { + read_descriptor_ = pipe_fds[0]; + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + write_descriptor_ = pipe_fds[1]; + ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); + } + else + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::system_error e(ec, "pipe_select_interrupter"); + boost::throw_exception(e); + } + } + + // Destructor. + ~pipe_select_interrupter() + { + if (read_descriptor_ != -1) + ::close(read_descriptor_); + if (write_descriptor_ != -1) + ::close(write_descriptor_); + } + + // Interrupt the select call. + void interrupt() + { + char byte = 0; + int result = ::write(write_descriptor_, &byte, 1); + (void)result; + } + + // Reset the select interrupt. Returns true if the call was interrupted. + bool reset() + { + for (;;) + { + char data[1024]; + int bytes_read = ::read(read_descriptor_, data, sizeof(data)); + if (bytes_read < 0 && errno == EINTR) + continue; + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = ::read(read_descriptor_, data, sizeof(data)); + return was_interrupted; + } + } + + // Get the read descriptor to be passed to select. + int read_descriptor() const + { + return read_descriptor_; + } + +private: + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // byte will be written on the other end of the connection and this + // descriptor will become readable. + int read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // byte may be written to this to wake up the select which is waiting for the + // other end to become readable. + int write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP diff --git a/ext/asio/detail/pop_options.hpp b/ext/asio/detail/pop_options.hpp new file mode 100644 index 0000000000..a26b2039c0 --- /dev/null +++ b/ext/asio/detail/pop_options.hpp @@ -0,0 +1,88 @@ +// +// pop_options.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) +// + +// No header guard + +#if defined(__COMO__) + +// Comeau C++ + +#elif defined(__DMC__) + +// Digital Mars C++ + +#elif defined(__INTEL_COMPILER) || defined(__ICL) \ + || defined(__ICC) || defined(__ECC) + +// Intel C++ + +#elif defined(__GNUC__) + +// GNU C++ + +# if defined(__MINGW32__) || defined(__CYGWIN__) +# pragma pack (pop) +# endif + +#elif defined(__KCC) + +// Kai C++ + +#elif defined(__sgi) + +// SGI MIPSpro C++ + +#elif defined(__DECCXX) + +// Compaq Tru64 Unix cxx + +#elif defined(__ghs) + +// Greenhills C++ + +#elif defined(__BORLANDC__) + +// Borland C++ + +# pragma option pop +# pragma nopushoptwarn +# pragma nopackwarning + +#elif defined(__MWERKS__) + +// Metrowerks CodeWarrior + +#elif defined(__SUNPRO_CC) + +// Sun Workshop Compiler C++ + +#elif defined(__HP_aCC) + +// HP aCC + +#elif defined(__MRC__) || defined(__SC__) + +// MPW MrCpp or SCpp + +#elif defined(__IBMCPP__) + +// IBM Visual Age + +#elif defined(_MSC_VER) + +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for example) +// also #define _MSC_VER + +# pragma warning (pop) +# pragma pack (pop) + +#endif diff --git a/ext/asio/detail/posix_event.hpp b/ext/asio/detail/posix_event.hpp new file mode 100644 index 0000000000..49c15aa5a0 --- /dev/null +++ b/ext/asio/detail/posix_event.hpp @@ -0,0 +1,114 @@ +// +// posix_event.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 ASIO_DETAIL_POSIX_EVENT_HPP +#define ASIO_DETAIL_POSIX_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include <boost/assert.hpp> +#include <boost/throw_exception.hpp> +#include <pthread.h> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class posix_event + : private noncopyable +{ +public: + // Constructor. + posix_event() + : signalled_(false) + { + int error = ::pthread_cond_init(&cond_, 0); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, + asio::error::get_system_category()), + "event"); + boost::throw_exception(e); + } + } + + // Destructor. + ~posix_event() + { + ::pthread_cond_destroy(&cond_); + } + + // Signal the event. + template <typename Lock> + void signal(Lock& lock) + { + BOOST_ASSERT(lock.locked()); + (void)lock; + signalled_ = true; + ::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) + { + BOOST_ASSERT(lock.locked()); + (void)lock; + signalled_ = false; + } + + // Wait for the event to become signalled. + template <typename Lock> + void wait(Lock& lock) + { + BOOST_ASSERT(lock.locked()); + while (!signalled_) + ::pthread_cond_wait(&cond_, &lock.mutex().mutex_); // Ignore EINVAL. + } + +private: + ::pthread_cond_t cond_; + bool signalled_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_EVENT_HPP diff --git a/ext/asio/detail/posix_fd_set_adapter.hpp b/ext/asio/detail/posix_fd_set_adapter.hpp new file mode 100644 index 0000000000..17ef269cee --- /dev/null +++ b/ext/asio/detail/posix_fd_set_adapter.hpp @@ -0,0 +1,81 @@ +// +// posix_fd_set_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 ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <cstring> +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class posix_fd_set_adapter +{ +public: + posix_fd_set_adapter() + : max_descriptor_(invalid_socket) + { + using namespace std; // Needed for memset on Solaris. + FD_ZERO(&fd_set_); + } + + bool set(socket_type descriptor) + { + if (descriptor < (socket_type)FD_SETSIZE) + { + if (max_descriptor_ == invalid_socket || descriptor > max_descriptor_) + max_descriptor_ = descriptor; + FD_SET(descriptor, &fd_set_); + return true; + } + return false; + } + + bool is_set(socket_type descriptor) const + { + return FD_ISSET(descriptor, &fd_set_) != 0; + } + + operator fd_set*() + { + return &fd_set_; + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + +private: + mutable fd_set fd_set_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP diff --git a/ext/asio/detail/posix_mutex.hpp b/ext/asio/detail/posix_mutex.hpp new file mode 100644 index 0000000000..230b83a356 --- /dev/null +++ b/ext/asio/detail/posix_mutex.hpp @@ -0,0 +1,91 @@ +// +// posix_mutex.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 ASIO_DETAIL_POSIX_MUTEX_HPP +#define ASIO_DETAIL_POSIX_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include <boost/throw_exception.hpp> +#include <pthread.h> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +namespace asio { +namespace detail { + +class posix_event; + +class posix_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock<posix_mutex> scoped_lock; + + // Constructor. + posix_mutex() + { + int error = ::pthread_mutex_init(&mutex_, 0); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, + asio::error::get_system_category()), + "mutex"); + boost::throw_exception(e); + } + } + + // Destructor. + ~posix_mutex() + { + ::pthread_mutex_destroy(&mutex_); // Ignore EBUSY. + } + + // Lock the mutex. + void lock() + { + (void)::pthread_mutex_lock(&mutex_); // Ignore EINVAL. + } + + // Unlock the mutex. + void unlock() + { + (void)::pthread_mutex_unlock(&mutex_); // Ignore EINVAL. + } + +private: + friend class posix_event; + ::pthread_mutex_t mutex_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_MUTEX_HPP diff --git a/ext/asio/detail/posix_signal_blocker.hpp b/ext/asio/detail/posix_signal_blocker.hpp new file mode 100644 index 0000000000..135ca41b7e --- /dev/null +++ b/ext/asio/detail/posix_signal_blocker.hpp @@ -0,0 +1,90 @@ +// +// posix_signal_blocker.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 ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include <csignal> +#include <pthread.h> +#include <signal.h> +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class posix_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + posix_signal_blocker() + : blocked_(false) + { + sigset_t new_mask; + sigfillset(&new_mask); + blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); + } + + // Destructor restores the previous signal mask. + ~posix_signal_blocker() + { + if (blocked_) + pthread_sigmask(SIG_SETMASK, &old_mask_, 0); + } + + // Block all signals for the calling thread. + void block() + { + if (!blocked_) + { + sigset_t new_mask; + sigfillset(&new_mask); + blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); + } + } + + // Restore the previous signal mask. + void unblock() + { + if (blocked_) + blocked_ = (pthread_sigmask(SIG_SETMASK, &old_mask_, 0) != 0); + } + +private: + // Have signals been blocked. + bool blocked_; + + // The previous signal mask. + sigset_t old_mask_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP diff --git a/ext/asio/detail/posix_thread.hpp b/ext/asio/detail/posix_thread.hpp new file mode 100644 index 0000000000..e0fea75129 --- /dev/null +++ b/ext/asio/detail/posix_thread.hpp @@ -0,0 +1,129 @@ +// +// posix_thread.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 ASIO_DETAIL_POSIX_THREAD_HPP +#define ASIO_DETAIL_POSIX_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include <memory> +#include <boost/throw_exception.hpp> +#include <pthread.h> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +extern "C" void* asio_detail_posix_thread_function(void* arg); + +class posix_thread + : private noncopyable +{ +public: + // Constructor. + template <typename Function> + posix_thread(Function f) + : joined_(false) + { + std::auto_ptr<func_base> arg(new func<Function>(f)); + int error = ::pthread_create(&thread_, 0, + asio_detail_posix_thread_function, arg.get()); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, + asio::error::get_system_category()), + "thread"); + boost::throw_exception(e); + } + arg.release(); + } + + // Destructor. + ~posix_thread() + { + if (!joined_) + ::pthread_detach(thread_); + } + + // Wait for the thread to exit. + void join() + { + if (!joined_) + { + ::pthread_join(thread_, 0); + joined_ = true; + } + } + +private: + friend void* asio_detail_posix_thread_function(void* arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + template <typename Function> + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::pthread_t thread_; + bool joined_; +}; + +inline void* asio_detail_posix_thread_function(void* arg) +{ + std::auto_ptr<posix_thread::func_base> f( + static_cast<posix_thread::func_base*>(arg)); + f->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_THREAD_HPP diff --git a/ext/asio/detail/posix_tss_ptr.hpp b/ext/asio/detail/posix_tss_ptr.hpp new file mode 100644 index 0000000000..3b4ba07b44 --- /dev/null +++ b/ext/asio/detail/posix_tss_ptr.hpp @@ -0,0 +1,88 @@ +// +// posix_tss_ptr.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 ASIO_DETAIL_POSIX_TSS_PTR_HPP +#define ASIO_DETAIL_POSIX_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include <boost/throw_exception.hpp> +#include <pthread.h> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +template <typename T> +class posix_tss_ptr + : private noncopyable +{ +public: + // Constructor. + posix_tss_ptr() + { + int error = ::pthread_key_create(&tss_key_, 0); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, + asio::error::get_system_category()), + "tss"); + boost::throw_exception(e); + } + } + + // Destructor. + ~posix_tss_ptr() + { + ::pthread_key_delete(tss_key_); + } + + // Get the value. + operator T*() const + { + return static_cast<T*>(::pthread_getspecific(tss_key_)); + } + + // Set the value. + void operator=(T* value) + { + ::pthread_setspecific(tss_key_, value); + } + +private: + // Thread-specific storage to allow unlocked access to determine whether a + // thread is a member of the pool. + pthread_key_t tss_key_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_TSS_PTR_HPP diff --git a/ext/asio/detail/push_options.hpp b/ext/asio/detail/push_options.hpp new file mode 100644 index 0000000000..cb0e90242e --- /dev/null +++ b/ext/asio/detail/push_options.hpp @@ -0,0 +1,114 @@ +// +// push_options.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) +// + +// No header guard + +#if defined(__COMO__) + +// Comeau C++ + +#elif defined(__DMC__) + +// Digital Mars C++ + +#elif defined(__INTEL_COMPILER) || defined(__ICL) \ + || defined(__ICC) || defined(__ECC) + +// Intel C++ + +#elif defined(__GNUC__) + +// GNU C++ + +# if defined(__MINGW32__) || defined(__CYGWIN__) +# pragma pack (push, 8) +# endif + +#elif defined(__KCC) + +// Kai C++ + +#elif defined(__sgi) + +// SGI MIPSpro C++ + +#elif defined(__DECCXX) + +// Compaq Tru64 Unix cxx + +#elif defined(__ghs) + +// Greenhills C++ + +#elif defined(__BORLANDC__) + +// Borland C++ + +# pragma option push -a8 -b -Ve- -Vx- -w-inl -vi- +# pragma nopushoptwarn +# pragma nopackwarning +# if !defined(__MT__) +# error Multithreaded RTL must be selected. +# endif // !defined(__MT__) + +#elif defined(__MWERKS__) + +// Metrowerks CodeWarrior + +#elif defined(__SUNPRO_CC) + +// Sun Workshop Compiler C++ + +#elif defined(__HP_aCC) + +// HP aCC + +#elif defined(__MRC__) || defined(__SC__) + +// MPW MrCpp or SCpp + +#elif defined(__IBMCPP__) + +// IBM Visual Age + +#elif defined(_MSC_VER) + +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for example) +// also #define _MSC_VER + +# pragma warning (disable:4103) +# pragma warning (push) +# pragma warning (disable:4127) +# pragma warning (disable:4244) +# pragma warning (disable:4355) +# pragma warning (disable:4512) +# pragma warning (disable:4675) +# if defined(_M_IX86) && defined(_Wp64) +// The /Wp64 option is broken. If you want to check 64 bit portability, use a +// 64 bit compiler! +# pragma warning (disable:4311) +# pragma warning (disable:4312) +# endif // defined(_M_IX86) && defined(_Wp64) +# pragma pack (push, 8) +// Note that if the /Og optimisation flag is enabled with MSVC6, the compiler +// has a tendency to incorrectly optimise away some calls to member template +// functions, even though those functions contain code that should not be +// optimised away! Therefore we will always disable this optimisation option +// for the MSVC6 compiler. +# if (_MSC_VER < 1300) +# pragma optimize ("g", off) +# endif +# if !defined(_MT) +# error Multithreaded RTL must be selected. +# endif // !defined(_MT) + +#endif diff --git a/ext/asio/detail/reactive_descriptor_service.hpp b/ext/asio/detail/reactive_descriptor_service.hpp new file mode 100644 index 0000000000..7ad368d7de --- /dev/null +++ b/ext/asio/detail/reactive_descriptor_service.hpp @@ -0,0 +1,668 @@ +// +// reactive_descriptor_service.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 ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP +#define ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/null_buffers_op.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/detail/reactor_op.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +namespace asio { +namespace detail { + +class reactive_descriptor_service +{ +public: + // The native type of a descriptor. + typedef int native_type; + + // The implementation type of the descriptor. + class implementation_type + : private asio::detail::noncopyable + { + public: + // Default constructor. + implementation_type() + : descriptor_(-1), + flags_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class reactive_descriptor_service; + + // The native descriptor representation. + int descriptor_; + + enum + { + // 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. + 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_descriptor_service(asio::io_service& io_service) + : io_service_impl_(asio::use_service<io_service_impl>(io_service)), + reactor_(asio::use_service<reactor>(io_service)) + { + reactor_.init_task(); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + // Construct a new descriptor implementation. + void construct(implementation_type& impl) + { + impl.descriptor_ = -1; + impl.flags_ = 0; + } + + // Destroy a descriptor implementation. + void destroy(implementation_type& impl) + { + if (impl.descriptor_ != -1) + { + reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_); + + if (impl.flags_ & implementation_type::internal_non_blocking) + { + ioctl_arg_type non_blocking = 0; + asio::error_code ignored_ec; + descriptor_ops::ioctl(impl.descriptor_, + FIONBIO, &non_blocking, ignored_ec); + impl.flags_ &= ~implementation_type::internal_non_blocking; + } + + asio::error_code ignored_ec; + descriptor_ops::close(impl.descriptor_, ignored_ec); + + impl.descriptor_ = -1; + } + } + + // Assign a native descriptor to a descriptor implementation. + asio::error_code assign(implementation_type& impl, + const native_type& native_descriptor, asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (int err = reactor_.register_descriptor( + native_descriptor, impl.reactor_data_)) + { + ec = asio::error_code(err, + asio::error::get_system_category()); + return ec; + } + + impl.descriptor_ = native_descriptor; + impl.flags_ = 0; + ec = asio::error_code(); + return ec; + } + + // Determine whether the descriptor is open. + bool is_open(const implementation_type& impl) const + { + return impl.descriptor_ != -1; + } + + // Destroy a descriptor implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + if (is_open(impl)) + { + reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_); + + if (impl.flags_ & implementation_type::internal_non_blocking) + { + ioctl_arg_type non_blocking = 0; + asio::error_code ignored_ec; + descriptor_ops::ioctl(impl.descriptor_, + FIONBIO, &non_blocking, ignored_ec); + impl.flags_ &= ~implementation_type::internal_non_blocking; + } + + if (descriptor_ops::close(impl.descriptor_, ec) == -1) + return ec; + + impl.descriptor_ = -1; + } + + ec = asio::error_code(); + return ec; + } + + // Get the native descriptor representation. + native_type native(const implementation_type& impl) const + { + return impl.descriptor_; + } + + // Cancel all operations associated with the descriptor. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + reactor_.cancel_ops(impl.descriptor_, impl.reactor_data_); + ec = asio::error_code(); + return ec; + } + + // Perform an IO control command on the descriptor. + template <typename IO_Control_Command> + asio::error_code io_control(implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + 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 (*static_cast<ioctl_arg_type*>(command.data())) + { + impl.flags_ |= implementation_type::user_set_non_blocking; + } + else + { + // 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; + } + + // Write some data to the descriptor. + template <typename ConstBufferSequence> + size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + buffer_sequence_adapter<asio::const_buffer, + ConstBufferSequence> bufs(buffers); + + // A request to read_some 0 bytes on a stream is a no-op. + if (bufs.all_empty()) + { + ec = asio::error_code(); + return 0; + } + + // Send the data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes_sent = descriptor_ops::gather_write( + impl.descriptor_, bufs.buffers(), bufs.count(), ec); + + // Check if operation succeeded. + if (bytes_sent >= 0) + return bytes_sent; + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for descriptor to become ready. + if (descriptor_ops::poll_write(impl.descriptor_, ec) < 0) + return 0; + } + } + + // Wait until data can be written without blocking. + size_t write_some(implementation_type& impl, + const null_buffers&, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Wait for descriptor to become ready. + descriptor_ops::poll_write(impl.descriptor_, ec); + + return 0; + } + + template <typename ConstBufferSequence> + class write_op_base : public reactor_op + { + public: + write_op_base(int descriptor, + const ConstBufferSequence& buffers, func_type complete_func) + : reactor_op(&write_op_base::do_perform, complete_func), + descriptor_(descriptor), + buffers_(buffers) + { + } + + static bool do_perform(reactor_op* base) + { + write_op_base* o(static_cast<write_op_base*>(base)); + + buffer_sequence_adapter<asio::const_buffer, + ConstBufferSequence> bufs(o->buffers_); + + for (;;) + { + // Write the data. + asio::error_code ec; + int bytes = descriptor_ops::gather_write( + o->descriptor_, bufs.buffers(), bufs.count(), ec); + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + o->ec_ = ec; + o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); + return true; + } + } + + private: + int descriptor_; + ConstBufferSequence buffers_; + }; + + template <typename ConstBufferSequence, typename Handler> + class write_op : public write_op_base<ConstBufferSequence> + { + public: + write_op(int descriptor, + const ConstBufferSequence& buffers, Handler handler) + : write_op_base<ConstBufferSequence>( + descriptor, buffers, &write_op::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // 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); + + // 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, asio::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + 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<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) + { + // 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. + template <typename MutableBufferSequence> + size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + buffer_sequence_adapter<asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); + + // A request to read_some 0 bytes on a stream is a no-op. + if (bufs.all_empty()) + { + ec = asio::error_code(); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes_read = descriptor_ops::scatter_read( + impl.descriptor_, bufs.buffers(), bufs.count(), ec); + + // Check if operation succeeded. + if (bytes_read > 0) + return bytes_read; + + // Check for EOF. + if (bytes_read == 0) + { + ec = asio::error::eof; + return 0; + } + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for descriptor to become ready. + if (descriptor_ops::poll_read(impl.descriptor_, ec) < 0) + return 0; + } + } + + // Wait until data can be read without blocking. + size_t read_some(implementation_type& impl, + const null_buffers&, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Wait for descriptor to become ready. + descriptor_ops::poll_read(impl.descriptor_, ec); + + return 0; + } + + template <typename MutableBufferSequence> + class read_op_base : public reactor_op + { + public: + read_op_base(int descriptor, + const MutableBufferSequence& buffers, func_type complete_func) + : reactor_op(&read_op_base::do_perform, complete_func), + descriptor_(descriptor), + buffers_(buffers) + { + } + + static bool do_perform(reactor_op* base) + { + read_op_base* o(static_cast<read_op_base*>(base)); + + buffer_sequence_adapter<asio::mutable_buffer, + MutableBufferSequence> bufs(o->buffers_); + + for (;;) + { + // Read some data. + asio::error_code ec; + int bytes = descriptor_ops::scatter_read( + o->descriptor_, bufs.buffers(), bufs.count(), ec); + if (bytes == 0) + ec = asio::error::eof; + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + o->ec_ = ec; + o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); + return true; + } + } + + private: + int descriptor_; + MutableBufferSequence buffers_; + }; + + template <typename MutableBufferSequence, typename Handler> + class read_op : public read_op_base<MutableBufferSequence> + { + public: + read_op(int descriptor, + const MutableBufferSequence& buffers, Handler handler) + : read_op_base<MutableBufferSequence>( + descriptor, buffers, &read_op::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // 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); + + // 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, asio::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + Handler 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<asio::mutable_buffer, + MutableBufferSequence>::all_empty(buffers)); + ptr.release(); + } + + // Wait until data can be read without blocking. + template <typename Handler> + void async_read_some(implementation_type& impl, + const null_buffers&, Handler handler) + { + // 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) + { + 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_ = asio::error::bad_descriptor; + } + + io_service_impl_.post_immediate_completion(op); + } + + // 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, + asio::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_; +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP diff --git a/ext/asio/detail/reactive_serial_port_service.hpp b/ext/asio/detail/reactive_serial_port_service.hpp new file mode 100644 index 0000000000..186460fef6 --- /dev/null +++ b/ext/asio/detail/reactive_serial_port_service.hpp @@ -0,0 +1,261 @@ +// +// reactive_serial_port_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.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 ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP +#define ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <cstring> +#include <string> +#include "asio/detail/pop_options.hpp" + +#include "asio/serial_port_base.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) \ + && !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/reactive_descriptor_service.hpp" + +namespace asio { +namespace detail { + +// Extend reactive_descriptor_service to provide serial port support. +class reactive_serial_port_service +{ +public: + // The native type of a serial port. + typedef reactive_descriptor_service::native_type native_type; + + // The implementation type of the serial port. + typedef reactive_descriptor_service::implementation_type implementation_type; + + reactive_serial_port_service(asio::io_service& 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. + void construct(implementation_type& impl) + { + descriptor_service_.construct(impl); + } + + // Destroy a handle implementation. + void destroy(implementation_type& impl) + { + descriptor_service_.destroy(impl); + } + + // Open the serial port using the specified device name. + asio::error_code open(implementation_type& impl, + const std::string& device, asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + int fd = descriptor_ops::open(device.c_str(), + O_RDWR | O_NONBLOCK | O_NOCTTY, ec); + if (fd < 0) + return ec; + + int s = descriptor_ops::fcntl(fd, F_GETFL, ec); + if (s >= 0) + s = descriptor_ops::fcntl(fd, F_SETFL, s | O_NONBLOCK, ec); + if (s < 0) + { + asio::error_code ignored_ec; + descriptor_ops::close(fd, ignored_ec); + return ec; + } + + // Set up default serial port options. + termios ios; + descriptor_ops::clear_error(ec); + s = descriptor_ops::error_wrapper(::tcgetattr(fd, &ios), ec); + if (s >= 0) + { +#if defined(_BSD_SOURCE) + ::cfmakeraw(&ios); +#else + ios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK + | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + ios.c_oflag &= ~OPOST; + ios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + ios.c_cflag &= ~(CSIZE | PARENB); + ios.c_cflag |= CS8; +#endif + ios.c_iflag |= IGNPAR; + ios.c_cflag |= CREAD | CLOCAL; + descriptor_ops::clear_error(ec); + s = descriptor_ops::error_wrapper(::tcsetattr(fd, TCSANOW, &ios), ec); + } + if (s < 0) + { + asio::error_code ignored_ec; + descriptor_ops::close(fd, ignored_ec); + return ec; + } + + // We're done. Take ownership of the serial port descriptor. + if (descriptor_service_.assign(impl, fd, ec)) + { + asio::error_code ignored_ec; + descriptor_ops::close(fd, ignored_ec); + } + + return ec; + } + + // Assign a native handle to a handle implementation. + asio::error_code assign(implementation_type& impl, + const native_type& native_descriptor, asio::error_code& ec) + { + return descriptor_service_.assign(impl, native_descriptor, ec); + } + + // Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return descriptor_service_.is_open(impl); + } + + // Destroy a handle implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return descriptor_service_.close(impl, ec); + } + + // Get the native handle representation. + native_type native(implementation_type& impl) + { + return descriptor_service_.native(impl); + } + + // Cancel all operations associated with the handle. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return descriptor_service_.cancel(impl, ec); + } + + // Set an option on the serial port. + template <typename SettableSerialPortOption> + asio::error_code set_option(implementation_type& impl, + const SettableSerialPortOption& option, asio::error_code& ec) + { + termios ios; + descriptor_ops::clear_error(ec); + descriptor_ops::error_wrapper(::tcgetattr( + descriptor_service_.native(impl), &ios), ec); + if (ec) + return ec; + + if (option.store(ios, ec)) + return ec; + + descriptor_ops::clear_error(ec); + descriptor_ops::error_wrapper(::tcsetattr( + descriptor_service_.native(impl), TCSANOW, &ios), ec); + return ec; + } + + // Get an option from the serial port. + template <typename GettableSerialPortOption> + asio::error_code get_option(const implementation_type& impl, + GettableSerialPortOption& option, asio::error_code& ec) const + { + termios ios; + descriptor_ops::clear_error(ec); + descriptor_ops::error_wrapper(::tcgetattr( + descriptor_service_.native(impl), &ios), ec); + if (ec) + return ec; + + return option.load(ios, ec); + } + + // Send a break sequence to the serial port. + asio::error_code send_break(implementation_type& impl, + asio::error_code& ec) + { + descriptor_ops::clear_error(ec); + descriptor_ops::error_wrapper(::tcsendbreak( + descriptor_service_.native(impl), 0), ec); + return ec; + } + + // Write the given data. Returns the number of bytes sent. + template <typename ConstBufferSequence> + size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return descriptor_service_.write_some(impl, buffers, ec); + } + + // Start an asynchronous write. The data being written 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) + { + descriptor_service_.async_write_some(impl, buffers, handler); + } + + // Read some data. Returns the number of bytes received. + template <typename MutableBufferSequence> + size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return descriptor_service_.read_some(impl, buffers, ec); + } + + // Start an asynchronous read. The buffer for the data being received 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) + { + descriptor_service_.async_read_some(impl, buffers, handler); + } + +private: + // The implementation used for initiating asynchronous operations. + reactive_descriptor_service descriptor_service_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_SERIAL_PORT) + // && !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP diff --git a/ext/asio/detail/reactive_socket_service.hpp b/ext/asio/detail/reactive_socket_service.hpp new file mode 100644 index 0000000000..20bd512ef6 --- /dev/null +++ b/ext/asio/detail/reactive_socket_service.hpp @@ -0,0 +1,1744 @@ +// +// reactive_socket_service.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 ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/null_buffers_op.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +template <typename Protocol> +class reactive_socket_service +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + typedef socket_type native_type; + + // The implementation type of the socket. + class implementation_type + : private asio::detail::noncopyable + { + public: + // Default constructor. + implementation_type() + : socket_(invalid_socket), + flags_(0), + protocol_(endpoint_type().protocol()) + { + } + + private: + // Only this service will have access to the internal values. + friend class reactive_socket_service<Protocol>; + + // The native socket representation. + socket_type socket_; + + enum + { + // The user wants a non-blocking socket. + user_set_non_blocking = 1, + + // The implementation wants a non-blocking socket (in order to be able to + // perform asynchronous read and write operations). + internal_non_blocking = 2, + + // Helper "flag" used to determine whether the socket is non-blocking. + non_blocking = user_set_non_blocking | internal_non_blocking, + + // 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. + user_set_linger = 8 + }; + + // Flags indicating the current state of the socket. + unsigned char flags_; + + // The protocol associated with the socket. + protocol_type protocol_; + + // Per-descriptor data used by the reactor. + reactor::per_descriptor_data reactor_data_; + }; + + // Constructor. + reactive_socket_service(asio::io_service& io_service) + : io_service_impl_(use_service<io_service_impl>(io_service)), + reactor_(use_service<reactor>(io_service)) + { + reactor_.init_task(); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + // Construct a new socket implementation. + void construct(implementation_type& impl) + { + impl.socket_ = invalid_socket; + impl.flags_ = 0; + } + + // Destroy a socket implementation. + void destroy(implementation_type& impl) + { + if (impl.socket_ != invalid_socket) + { + reactor_.close_descriptor(impl.socket_, impl.reactor_data_); + + if (impl.flags_ & implementation_type::non_blocking) + { + ioctl_arg_type non_blocking = 0; + asio::error_code ignored_ec; + socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec); + impl.flags_ &= ~implementation_type::non_blocking; + } + + if (impl.flags_ & implementation_type::user_set_linger) + { + ::linger opt; + opt.l_onoff = 0; + opt.l_linger = 0; + asio::error_code ignored_ec; + socket_ops::setsockopt(impl.socket_, + SOL_SOCKET, SO_LINGER, &opt, sizeof(opt), ignored_ec); + } + + asio::error_code ignored_ec; + socket_ops::close(impl.socket_, ignored_ec); + + impl.socket_ = invalid_socket; + } + } + + // Open a new socket implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + socket_holder sock(socket_ops::socket(protocol.family(), + protocol.type(), protocol.protocol(), ec)); + if (sock.get() == invalid_socket) + return ec; + + if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_)) + { + ec = asio::error_code(err, + asio::error::get_system_category()); + return ec; + } + + impl.socket_ = sock.release(); + impl.flags_ = 0; + impl.protocol_ = protocol; + ec = asio::error_code(); + return ec; + } + + // Assign a native socket to a socket implementation. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_type& native_socket, + asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (int err = reactor_.register_descriptor( + native_socket, impl.reactor_data_)) + { + ec = asio::error_code(err, + asio::error::get_system_category()); + return ec; + } + + impl.socket_ = native_socket; + impl.flags_ = 0; + impl.protocol_ = protocol; + ec = asio::error_code(); + return ec; + } + + // Determine whether the socket is open. + bool is_open(const implementation_type& impl) const + { + return impl.socket_ != invalid_socket; + } + + // Destroy a socket implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + if (is_open(impl)) + { + reactor_.close_descriptor(impl.socket_, impl.reactor_data_); + + if (impl.flags_ & implementation_type::non_blocking) + { + ioctl_arg_type non_blocking = 0; + asio::error_code ignored_ec; + socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec); + impl.flags_ &= ~implementation_type::non_blocking; + } + + if (socket_ops::close(impl.socket_, ec) == socket_error_retval) + return ec; + + impl.socket_ = invalid_socket; + } + + ec = asio::error_code(); + return ec; + } + + // Get the native socket representation. + native_type native(implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + reactor_.cancel_ops(impl.socket_, impl.reactor_data_); + ec = asio::error_code(); + return ec; + } + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return false; + } + +#if defined(SIOCATMARK) + asio::detail::ioctl_arg_type value = 0; + socket_ops::ioctl(impl.socket_, SIOCATMARK, &value, ec); +# if defined(ENOTTY) + if (ec.value() == ENOTTY) + ec = asio::error::not_socket; +# endif // defined(ENOTTY) +#else // defined(SIOCATMARK) + int value = sockatmark(impl.socket_); + if (value == -1) + ec = asio::error_code(errno, + asio::error::get_system_category()); + else + ec = asio::error_code(); +#endif // defined(SIOCATMARK) + return ec ? false : value != 0; + } + + // Determine the number of bytes available for reading. + std::size_t available(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + asio::detail::ioctl_arg_type value = 0; + socket_ops::ioctl(impl.socket_, FIONREAD, &value, ec); +#if defined(ENOTTY) + if (ec.value() == ENOTTY) + ec = asio::error::not_socket; +#endif // defined(ENOTTY) + return ec ? static_cast<std::size_t>(0) : static_cast<std::size_t>(value); + } + + // Bind the socket to the specified local endpoint. + asio::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); + return ec; + } + + // Place the socket into the state where it will listen for new connections. + asio::error_code listen(implementation_type& impl, int backlog, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::listen(impl.socket_, backlog, ec); + return ec; + } + + // Set a socket option. + template <typename Option> + asio::error_code set_option(implementation_type& impl, + const Option& option, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + if (option.level(impl.protocol_) == custom_socket_option_level + && option.name(impl.protocol_) == enable_connection_aborted_option) + { + if (option.size(impl.protocol_) != sizeof(int)) + { + ec = asio::error::invalid_argument; + } + else + { + if (*reinterpret_cast<const int*>(option.data(impl.protocol_))) + impl.flags_ |= implementation_type::enable_connection_aborted; + else + impl.flags_ &= ~implementation_type::enable_connection_aborted; + ec = asio::error_code(); + } + return ec; + } + else + { + if (option.level(impl.protocol_) == SOL_SOCKET + && option.name(impl.protocol_) == SO_LINGER) + { + impl.flags_ |= implementation_type::user_set_linger; + } + + socket_ops::setsockopt(impl.socket_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_), ec); + +#if defined(__MACH__) && defined(__APPLE__) \ +|| defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + // To implement portable behaviour for SO_REUSEADDR with UDP sockets we + // need to also set SO_REUSEPORT on BSD-based platforms. + if (!ec && impl.protocol_.type() == SOCK_DGRAM + && option.level(impl.protocol_) == SOL_SOCKET + && option.name(impl.protocol_) == SO_REUSEADDR) + { + asio::error_code ignored_ec; + socket_ops::setsockopt(impl.socket_, SOL_SOCKET, SO_REUSEPORT, + option.data(impl.protocol_), option.size(impl.protocol_), + ignored_ec); + } +#endif + + return ec; + } + } + + // Set a socket option. + template <typename Option> + asio::error_code get_option(const implementation_type& impl, + Option& option, asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + if (option.level(impl.protocol_) == custom_socket_option_level + && option.name(impl.protocol_) == enable_connection_aborted_option) + { + if (option.size(impl.protocol_) != sizeof(int)) + { + ec = asio::error::invalid_argument; + } + else + { + int* target = reinterpret_cast<int*>(option.data(impl.protocol_)); + if (impl.flags_ & implementation_type::enable_connection_aborted) + *target = 1; + else + *target = 0; + option.resize(impl.protocol_, sizeof(int)); + ec = asio::error_code(); + } + return ec; + } + else + { + size_t size = option.size(impl.protocol_); + socket_ops::getsockopt(impl.socket_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); + return ec; + } + } + + // Perform an IO control command on the socket. + template <typename IO_Control_Command> + asio::error_code io_control(implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + 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)) + { + if (*static_cast<ioctl_arg_type*>(command.data())) + { + impl.flags_ |= implementation_type::user_set_non_blocking; + } + else + { + // 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; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return endpoint_type(); + } + + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return endpoint_type(); + } + + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + /// Disable sends or receives on the socket. + asio::error_code shutdown(implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::shutdown(impl.socket_, what, ec); + return ec; + } + + // Send the given data to the peer. + template <typename ConstBufferSequence> + size_t send(implementation_type& impl, const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + buffer_sequence_adapter<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 && bufs.all_empty()) + { + ec = asio::error_code(); + return 0; + } + + // Send the data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes_sent = socket_ops::send(impl.socket_, + bufs.buffers(), bufs.count(), flags, ec); + + // Check if operation succeeded. + if (bytes_sent >= 0) + return bytes_sent; + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_write(impl.socket_, ec) < 0) + return 0; + } + } + + // Wait until data can be sent without blocking. + size_t send(implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, ec); + + return 0; + } + + template <typename ConstBufferSequence> + class send_op_base : public reactor_op + { + public: + 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), + buffers_(buffers), + flags_(flags) + { + } + + static bool do_perform(reactor_op* base) + { + send_op_base* o(static_cast<send_op_base*>(base)); + + buffer_sequence_adapter<asio::const_buffer, + ConstBufferSequence> bufs(o->buffers_); + + for (;;) + { + // Send the data. + asio::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 == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + o->ec_ = ec; + o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); + return true; + } + } + + private: + socket_type socket_; + ConstBufferSequence buffers_; + socket_base::message_flags flags_; + }; + + template <typename ConstBufferSequence, typename Handler> + class send_op : public send_op_base<ConstBufferSequence> + { + public: + 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) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // 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); + + // 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, asio::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, 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(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<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) + { + // 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 + // sent. + template <typename ConstBufferSequence> + size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + buffer_sequence_adapter<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.buffers(), + bufs.count(), flags, destination.data(), destination.size(), ec); + + // Check if operation succeeded. + if (bytes_sent >= 0) + return bytes_sent; + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_write(impl.socket_, ec) < 0) + return 0; + } + } + + // Wait until data can be sent without blocking. + size_t send_to(implementation_type& impl, const null_buffers&, + socket_base::message_flags, const endpoint_type&, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, ec); + + return 0; + } + + template <typename ConstBufferSequence> + class send_to_op_base : public reactor_op + { + public: + 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), + buffers_(buffers), + destination_(endpoint), + flags_(flags) + { + } + + static bool do_perform(reactor_op* base) + { + send_to_op_base* o(static_cast<send_to_op_base*>(base)); + + buffer_sequence_adapter<asio::const_buffer, + ConstBufferSequence> bufs(o->buffers_); + + for (;;) + { + // Send the data. + asio::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 == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + o->ec_ = ec; + o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); + return true; + } + } + + private: + socket_type socket_; + ConstBufferSequence buffers_; + endpoint_type destination_; + socket_base::message_flags flags_; + }; + + template <typename ConstBufferSequence, typename Handler> + class send_to_op : public send_to_op_base<ConstBufferSequence> + { + 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) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // 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) + { + // 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, asio::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, 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. + template <typename Handler> + void async_send_to(implementation_type& impl, const null_buffers&, + socket_base::message_flags, const endpoint_type&, Handler handler) + { + // 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. + template <typename MutableBufferSequence> + size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + buffer_sequence_adapter<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 && bufs.all_empty()) + { + ec = asio::error_code(); + return 0; + } + + // Receive some data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes_recvd = socket_ops::recv(impl.socket_, + bufs.buffers(), bufs.count(), flags, ec); + + // Check if operation succeeded. + if (bytes_recvd > 0) + return bytes_recvd; + + // Check for EOF. + if (bytes_recvd == 0 && impl.protocol_.type() == SOCK_STREAM) + { + ec = asio::error::eof; + return 0; + } + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(impl.socket_, ec) < 0) + return 0; + } + } + + // Wait until data can be received without blocking. + size_t receive(implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, ec); + + return 0; + } + + template <typename MutableBufferSequence> + class receive_op_base : public reactor_op + { + public: + receive_op_base(socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(&receive_op_base::do_perform, complete_func), + socket_(socket), + protocol_type_(protocol_type), + buffers_(buffers), + flags_(flags) + { + } + + static bool do_perform(reactor_op* base) + { + receive_op_base* o(static_cast<receive_op_base*>(base)); + + buffer_sequence_adapter<asio::mutable_buffer, + MutableBufferSequence> bufs(o->buffers_); + + for (;;) + { + // Receive some data. + asio::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 = asio::error::eof; + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + o->ec_ = ec; + o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); + return true; + } + } + + private: + socket_type socket_; + int protocol_type_; + MutableBufferSequence buffers_; + socket_base::message_flags flags_; + }; + + template <typename MutableBufferSequence, typename Handler> + class receive_op : public receive_op_base<MutableBufferSequence> + { + 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) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::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); + + // 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, asio::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + asio::detail::fenced_block b; + 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<asio::mutable_buffer, + MutableBufferSequence>::all_empty(buffers))); + ptr.release(); + } + + // Wait until data can be received without blocking. + template <typename Handler> + void async_receive(implementation_type& impl, const null_buffers&, + socket_base::message_flags flags, Handler handler) + { + // 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 + // bytes received. + template <typename MutableBufferSequence> + size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + buffer_sequence_adapter<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.buffers(), + bufs.count(), flags, sender_endpoint.data(), &addr_len, ec); + + // Check if operation succeeded. + if (bytes_recvd > 0) + { + sender_endpoint.resize(addr_len); + return bytes_recvd; + } + + // Check for EOF. + if (bytes_recvd == 0 && impl.protocol_.type() == SOCK_STREAM) + { + ec = asio::error::eof; + return 0; + } + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(impl.socket_, ec) < 0) + return 0; + } + } + + // Wait until data can be received without blocking. + size_t receive_from(implementation_type& impl, const null_buffers&, + endpoint_type& sender_endpoint, socket_base::message_flags, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, ec); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + return 0; + } + + template <typename MutableBufferSequence> + class receive_from_op_base : public reactor_op + { + public: + receive_from_op_base(socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, endpoint_type& endpoint, + 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), + buffers_(buffers), + sender_endpoint_(endpoint), + flags_(flags) + { + } + + static bool do_perform(reactor_op* base) + { + receive_from_op_base* o(static_cast<receive_from_op_base*>(base)); + + buffer_sequence_adapter<asio::mutable_buffer, + MutableBufferSequence> bufs(o->buffers_); + + for (;;) + { + // Receive some data. + asio::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 = asio::error::eof; + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + o->sender_endpoint_.resize(addr_len); + o->ec_ = ec; + o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); + return true; + } + } + + private: + socket_type socket_; + int protocol_type_; + MutableBufferSequence buffers_; + endpoint_type& sender_endpoint_; + socket_base::message_flags flags_; + }; + + template <typename MutableBufferSequence, typename Handler> + class receive_from_op : public receive_from_op_base<MutableBufferSequence> + { + 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) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // 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) + { + // 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, asio::error_code, std::size_t> + handler(o->handler_, o->ec_, o->bytes_transferred_); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, 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. + template <typename Handler> + void async_receive_from(implementation_type& impl, + const null_buffers&, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler handler) + { + // 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(); + + 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. + template <typename Socket> + asio::error_code accept(implementation_type& impl, + Socket& peer, endpoint_type* peer_endpoint, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + // We cannot accept a socket that is already open. + if (peer.is_open()) + { + ec = asio::error::already_open; + return ec; + } + + // Accept a socket. + for (;;) + { + // Try to complete the operation without blocking. + socket_holder new_socket; + std::size_t addr_len = 0; + if (peer_endpoint) + { + addr_len = peer_endpoint->capacity(); + new_socket.reset(socket_ops::accept(impl.socket_, + peer_endpoint->data(), &addr_len, ec)); + } + else + { + new_socket.reset(socket_ops::accept(impl.socket_, 0, 0, ec)); + } + + // Check if operation succeeded. + if (new_socket.get() >= 0) + { + if (peer_endpoint) + peer_endpoint->resize(addr_len); + peer.assign(impl.protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + return ec; + } + + // Operation failed. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + { + if (impl.flags_ & implementation_type::user_set_non_blocking) + return ec; + // Fall through to retry operation. + } + else if (ec == asio::error::connection_aborted) + { + if (impl.flags_ & implementation_type::enable_connection_aborted) + return ec; + // Fall through to retry operation. + } +#if defined(EPROTO) + else if (ec.value() == EPROTO) + { + if (impl.flags_ & implementation_type::enable_connection_aborted) + return ec; + // Fall through to retry operation. + } +#endif // defined(EPROTO) + else + return ec; + + // Wait for socket to become ready. + if (socket_ops::poll_read(impl.socket_, ec) < 0) + return ec; + } + } + + template <typename Socket> + class accept_op_base : public reactor_op + { + public: + 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), + peer_(peer), + protocol_(protocol), + peer_endpoint_(peer_endpoint), + enable_connection_aborted_(enable_connection_aborted) + { + } + + static bool do_perform(reactor_op* base) + { + accept_op_base* o(static_cast<accept_op_base*>(base)); + + for (;;) + { + // Accept the waiting connection. + asio::error_code ec; + socket_holder new_socket; + std::size_t addr_len = 0; + std::size_t* addr_len_p = 0; + socket_addr_type* addr = 0; + if (o->peer_endpoint_) + { + 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 == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + if (ec == asio::error::connection_aborted + && !o->enable_connection_aborted_) + return false; +#if defined(EPROTO) + 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 (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; + } + } + + private: + socket_type socket_; + Socket& peer_; + protocol_type protocol_; + endpoint_type* peer_endpoint_; + bool enable_connection_aborted_; + }; + + template <typename Socket, typename Handler> + class accept_op : public accept_op_base<Socket> + { + 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) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // 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) + { + // 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, asio::error_code> + handler(o->handler_, o->ec_); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, 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. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + // Perform the connect operation. + socket_ops::connect(impl.socket_, + peer_endpoint.data(), peer_endpoint.size(), ec); + if (ec != asio::error::in_progress + && ec != asio::error::would_block) + { + // The connect operation finished immediately. + return ec; + } + + // Wait for socket to become ready. + if (socket_ops::poll_connect(impl.socket_, ec) < 0) + return ec; + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len, ec) == socket_error_retval) + return ec; + + // Return the result of the connect operation. + ec = asio::error_code(connect_error, + asio::error::get_system_category()); + return ec; + } + + class connect_op_base : public reactor_op + { + public: + connect_op_base(socket_type socket, func_type complete_func) + : reactor_op(&connect_op_base::do_perform, complete_func), + socket_(socket) + { + } + + static bool do_perform(reactor_op* base) + { + 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(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) + { + o->ec_ = asio::error_code(connect_error, + asio::error::get_system_category()); + } + + return true; + } + + 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) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::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, asio::error_code> + handler(o->handler_, o->ec_); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + Handler handler_; + }; + + // Start an asynchronous connect. + template <typename Handler> + 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: + // 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 (is_open(impl)) + { + if (!non_blocking || is_non_blocking(impl) + || set_non_blocking(impl, op->ec_)) + { + reactor_.start_op(op_type, impl.socket_, + impl.reactor_data_, op, non_blocking); + return; + } + } + else + op->ec_ = asio::error::bad_descriptor; + } + + 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 + { + op->ec_ = asio::error::already_open; + io_service_impl_.post_immediate_completion(op); + } + } + + // Start the asynchronous connect operation. + void start_connect_op(implementation_type& impl, + reactor_op* op, const endpoint_type& peer_endpoint) + { + if (is_open(impl)) + { + 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_ == asio::error::in_progress + || op->ec_ == asio::error::would_block) + { + op->ec_ = asio::error_code(); + reactor_.start_op(reactor::connect_op, + impl.socket_, impl.reactor_data_, op, false); + return; + } + } + } + } + else + op->ec_ = asio::error::bad_descriptor; + + io_service_impl_.post_immediate_completion(op); + } + + // 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, + asio::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_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP diff --git a/ext/asio/detail/reactor.hpp b/ext/asio/detail/reactor.hpp new file mode 100644 index 0000000000..98ac360b9e --- /dev/null +++ b/ext/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 ASIO_DETAIL_REACTOR_HPP +#define ASIO_DETAIL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/reactor_fwd.hpp" + +#if defined(ASIO_HAS_EPOLL) +# include "asio/detail/epoll_reactor.hpp" +#elif defined(ASIO_HAS_KQUEUE) +# include "asio/detail/kqueue_reactor.hpp" +#elif defined(ASIO_HAS_DEV_POLL) +# include "asio/detail/dev_poll_reactor.hpp" +#else +# include "asio/detail/select_reactor.hpp" +#endif + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTOR_HPP diff --git a/ext/asio/detail/reactor_fwd.hpp b/ext/asio/detail/reactor_fwd.hpp new file mode 100644 index 0000000000..9c33be54ca --- /dev/null +++ b/ext/asio/detail/reactor_fwd.hpp @@ -0,0 +1,46 @@ +// +// 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 ASIO_DETAIL_REACTOR_FWD_HPP +#define ASIO_DETAIL_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/dev_poll_reactor_fwd.hpp" +#include "asio/detail/epoll_reactor_fwd.hpp" +#include "asio/detail/kqueue_reactor_fwd.hpp" +#include "asio/detail/select_reactor_fwd.hpp" +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_IOCP) +typedef select_reactor<true> reactor; +#elif defined(ASIO_HAS_EPOLL) +typedef epoll_reactor reactor; +#elif defined(ASIO_HAS_KQUEUE) +typedef kqueue_reactor reactor; +#elif defined(ASIO_HAS_DEV_POLL) +typedef dev_poll_reactor reactor; +#else +typedef select_reactor<false> reactor; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTOR_FWD_HPP diff --git a/ext/asio/detail/reactor_op.hpp b/ext/asio/detail/reactor_op.hpp new file mode 100644 index 0000000000..cd557fa6ae --- /dev/null +++ b/ext/asio/detail/reactor_op.hpp @@ -0,0 +1,60 @@ +// +// 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 ASIO_DETAIL_REACTOR_OP_HPP +#define ASIO_DETAIL_REACTOR_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/operation.hpp" + +namespace asio { +namespace detail { + +class reactor_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::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 + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTOR_OP_HPP diff --git a/ext/asio/detail/reactor_op_queue.hpp b/ext/asio/detail/reactor_op_queue.hpp new file mode 100644 index 0000000000..233c0aec16 --- /dev/null +++ b/ext/asio/detail/reactor_op_queue.hpp @@ -0,0 +1,199 @@ +// +// reactor_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 ASIO_DETAIL_REACTOR_OP_QUEUE_HPP +#define ASIO_DETAIL_REACTOR_OP_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" + +namespace asio { +namespace detail { + +template <typename Descriptor> +class reactor_op_queue + : private noncopyable +{ +public: + // Constructor. + reactor_op_queue() + : 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. + bool enqueue_operation(Descriptor descriptor, reactor_op* op) + { + typedef typename operations_map::iterator iterator; + typedef typename operations_map::value_type value_type; + std::pair<iterator, bool> entry = + 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 + // pending for the descriptor will be notified that they have been cancelled + // 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, op_queue<operation>& ops, + const asio::error_code& ec = + asio::error::operation_aborted) + { + typename operations_map::iterator i = operations_.find(descriptor); + if (i != operations_.end()) + { + 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; + } + + return false; + } + + // Whether there are no operations in the queue. + bool empty() const + { + return operations_.empty(); + } + + // Determine whether there are any operations associated with the descriptor. + bool has_operation(Descriptor descriptor) const + { + return operations_.find(descriptor) != operations_.end(); + } + + // 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 operations_map::iterator i = operations_.find(descriptor); + if (i != operations_.end()) + { + while (reactor_op* op = i->second.op_queue_.front()) + { + if (op->perform()) + { + i->second.op_queue_.pop(); + ops.push(op); + } + else + { + return true; + } + } + operations_.erase(i); + } + return false; + } + + // Fill a descriptor set with the descriptors corresponding to each active + // 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, op_queue<operation>& ops) + { + typename operations_map::iterator i = operations_.begin(); + while (i != operations_.end()) + { + Descriptor descriptor = i->first; + ++i; + if (!descriptors.set(descriptor)) + { + asio::error_code ec(error::fd_set_failure); + cancel_operations(descriptor, ops, ec); + } + } + } + + // 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, op_queue<operation>& ops) + { + typename operations_map::iterator i = operations_.begin(); + while (i != operations_.end()) + { + typename operations_map::iterator op_iter = i++; + if (descriptors.is_set(op_iter->first)) + { + while (reactor_op* op = op_iter->second.op_queue_.front()) + { + if (op->perform()) + { + op_iter->second.op_queue_.pop(); + ops.push(op); + } + else + { + break; + } + } + + if (op_iter->second.op_queue_.empty()) + operations_.erase(op_iter); + } + } + } + + // Get all operations owned by the queue. + void get_all_operations(op_queue<operation>& ops) + { + typename operations_map::iterator i = operations_.begin(); + while (i != operations_.end()) + { + typename operations_map::iterator op_iter = i++; + ops.push(op_iter->second.op_queue_); + operations_.erase(op_iter); + } + } + +private: + struct operations + { + operations() {} + operations(const operations&) {} + void operator=(const operations&) {} + + // The operations waiting on the desccriptor. + op_queue<reactor_op> op_queue_; + }; + + // The type for a map of operations. + typedef hash_map<Descriptor, operations> operations_map; + + // The operations that are currently executing asynchronously. + operations_map operations_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTOR_OP_QUEUE_HPP diff --git a/ext/asio/detail/resolver_service.hpp b/ext/asio/detail/resolver_service.hpp new file mode 100644 index 0000000000..562dc10476 --- /dev/null +++ b/ext/asio/detail/resolver_service.hpp @@ -0,0 +1,442 @@ +// +// resolver_service.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 ASIO_DETAIL_RESOLVER_SERVICE_HPP +#define ASIO_DETAIL_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <cstring> +#include <boost/scoped_ptr.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/thread.hpp" + +namespace asio { +namespace detail { + +template <typename Protocol> +class resolver_service + : public asio::detail::service_base<resolver_service<Protocol> > +{ +private: + // Helper class to perform exception-safe cleanup of addrinfo objects. + class auto_addrinfo + : private asio::detail::noncopyable + { + public: + explicit auto_addrinfo(asio::detail::addrinfo_type* ai) + : ai_(ai) + { + } + + ~auto_addrinfo() + { + if (ai_) + socket_ops::freeaddrinfo(ai_); + } + + operator asio::detail::addrinfo_type*() + { + return ai_; + } + + private: + asio::detail::addrinfo_type* ai_; + }; + +public: + // The implementation type of the resolver. The shared pointer is used as a + // cancellation token to indicate to the background thread that the operation + // has been cancelled. + typedef boost::shared_ptr<void> implementation_type; + struct noop_deleter { void operator()(void*) {} }; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The query type. + typedef asio::ip::basic_resolver_query<Protocol> query_type; + + // The iterator type. + typedef asio::ip::basic_resolver_iterator<Protocol> iterator_type; + + // Constructor. + resolver_service(asio::io_service& io_service) + : asio::detail::service_base< + resolver_service<Protocol> >(io_service), + mutex_(), + io_service_impl_(asio::use_service<io_service_impl>(io_service)), + work_io_service_(new asio::io_service), + work_io_service_impl_(asio::use_service< + io_service_impl>(*work_io_service_)), + work_(new asio::io_service::work(*work_io_service_)), + work_thread_(0) + { + } + + // Destructor. + ~resolver_service() + { + shutdown_service(); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + work_.reset(); + if (work_io_service_) + { + work_io_service_->stop(); + if (work_thread_) + { + work_thread_->join(); + work_thread_.reset(); + } + work_io_service_.reset(); + } + } + + // Construct a new resolver implementation. + void construct(implementation_type& impl) + { + impl.reset(static_cast<void*>(0), noop_deleter()); + } + + // Destroy a resolver implementation. + void destroy(implementation_type&) + { + } + + // Cancel pending asynchronous operations. + void cancel(implementation_type& impl) + { + impl.reset(static_cast<void*>(0), noop_deleter()); + } + + // Resolve a query to a list of entries. + iterator_type resolve(implementation_type&, const query_type& query, + asio::error_code& ec) + { + asio::detail::addrinfo_type* address_info = 0; + std::string host_name = query.host_name(); + std::string service_name = query.service_name(); + asio::detail::addrinfo_type hints = query.hints(); + + 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); + + if (ec) + return iterator_type(); + + return iterator_type::create(address_info, host_name, service_name); + } + + template <typename Handler> + class resolve_op + : public operation + { + public: + 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_impl_(io_service_impl), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // 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) + { + 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_ = asio::error::operation_aborted; + } + else + { + // Perform the blocking host resolution operation. + asio::detail::addrinfo_type* address_info = 0; + std::string host_name = o->query_.host_name(); + std::string service_name = o->query_.service_name(); + 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, asio::error_code, iterator_type> + handler(o->handler_, o->ec_, o->iter_); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + } + + private: + boost::weak_ptr<void> impl_; + query_type query_; + io_service_impl& io_service_impl_; + Handler handler_; + asio::error_code ec_; + iterator_type iter_; + }; + + // Asynchronously resolve a query to a list of entries. + template <typename Handler> + 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(); + io_service_impl_.work_started(); + work_io_service_impl_.post_immediate_completion(ptr.get()); + ptr.release(); + } + } + + // Resolve an endpoint to a list of entries. + iterator_type resolve(implementation_type&, + const endpoint_type& endpoint, asio::error_code& ec) + { + // 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; + socket_ops::getnameinfo(endpoint.data(), endpoint.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); + if (ec) + { + flags |= NI_NUMERICSERV; + socket_ops::getnameinfo(endpoint.data(), endpoint.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); + } + + if (ec) + return iterator_type(); + + return iterator_type::create(endpoint, host_name, service_name); + } + + template <typename Handler> + class resolve_endpoint_op + : public operation + { + public: + 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) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // 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); + + if (owner) + { + 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_ = 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, asio::error_code, iterator_type> + handler(o->handler_, o->ec_, o->iter_); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + } + + private: + boost::weak_ptr<void> impl_; + endpoint_type ep_; + io_service_impl& io_service_impl_; + Handler handler_; + asio::error_code ec_; + iterator_type iter_; + }; + + // Asynchronously resolve an endpoint to a list of entries. + template <typename Handler> + 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(); + io_service_impl_.work_started(); + work_io_service_impl_.post_immediate_completion(ptr.get()); + ptr.release(); + } + } + +private: + // Helper class to run the work io_service in a thread. + class work_io_service_runner + { + public: + work_io_service_runner(asio::io_service& io_service) + : io_service_(io_service) {} + void operator()() { io_service_.run(); } + private: + asio::io_service& io_service_; + }; + + // Start the work thread if it's not already running. + void start_work_thread() + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!work_thread_) + { + work_thread_.reset(new asio::detail::thread( + work_io_service_runner(*work_io_service_))); + } + } + + // Mutex to protect access to internal data. + 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<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<asio::io_service::work> work_; + + // Thread used for running the work io_service's run loop. + boost::scoped_ptr<asio::detail::thread> work_thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RESOLVER_SERVICE_HPP diff --git a/ext/asio/detail/scoped_lock.hpp b/ext/asio/detail/scoped_lock.hpp new file mode 100644 index 0000000000..e6f6ba59a0 --- /dev/null +++ b/ext/asio/detail/scoped_lock.hpp @@ -0,0 +1,91 @@ +// +// scoped_lock.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 ASIO_DETAIL_SCOPED_LOCK_HPP +#define ASIO_DETAIL_SCOPED_LOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +// Helper class to lock and unlock a mutex automatically. +template <typename Mutex> +class scoped_lock + : private noncopyable +{ +public: + // Constructor acquires the lock. + scoped_lock(Mutex& m) + : mutex_(m) + { + mutex_.lock(); + locked_ = true; + } + + // Destructor releases the lock. + ~scoped_lock() + { + if (locked_) + mutex_.unlock(); + } + + // Explicitly acquire the lock. + void lock() + { + if (!locked_) + { + mutex_.lock(); + locked_ = true; + } + } + + // Explicitly release the lock. + void unlock() + { + if (locked_) + { + mutex_.unlock(); + locked_ = false; + } + } + + // Test whether the lock is held. + bool locked() const + { + return locked_; + } + + // Get the underlying mutex. + Mutex& mutex() + { + return mutex_; + } + +private: + // The underlying mutex. + Mutex& mutex_; + + // Whether the mutex is currently locked or unlocked. + bool locked_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SCOPED_LOCK_HPP diff --git a/ext/asio/detail/select_interrupter.hpp b/ext/asio/detail/select_interrupter.hpp new file mode 100644 index 0000000000..ff5505bebc --- /dev/null +++ b/ext/asio/detail/select_interrupter.hpp @@ -0,0 +1,47 @@ +// +// select_interrupter.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 ASIO_DETAIL_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# include "asio/detail/socket_select_interrupter.hpp" +#else +# include "asio/detail/eventfd_select_interrupter.hpp" +# include "asio/detail/pipe_select_interrupter.hpp" +#endif + +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef socket_select_interrupter select_interrupter; +#elif defined(ASIO_HAS_EVENTFD) +typedef eventfd_select_interrupter select_interrupter; +#else +typedef pipe_select_interrupter select_interrupter; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SELECT_INTERRUPTER_HPP diff --git a/ext/asio/detail/select_reactor.hpp b/ext/asio/detail/select_reactor.hpp new file mode 100644 index 0000000000..798611b281 --- /dev/null +++ b/ext/asio/detail/select_reactor.hpp @@ -0,0 +1,374 @@ +// +// select_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 ASIO_DETAIL_SELECT_REACTOR_HPP +#define ASIO_DETAIL_SELECT_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" // Must come before posix_time. + +#include "asio/detail/push_options.hpp" +#include <cstddef> +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fd_set_adapter.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/select_reactor_fwd.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/signal_blocker.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/timer_op.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_fwd.hpp" +#include "asio/detail/timer_queue_set.hpp" + +namespace asio { +namespace detail { + +template <bool Own_Thread> +class select_reactor + : public 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 + { + }; + + // Constructor. + select_reactor(asio::io_service& io_service) + : asio::detail::service_base< + select_reactor<Own_Thread> >(io_service), + io_service_(use_service<io_service_impl>(io_service)), + mutex_(), + interrupter_(), + stop_thread_(false), + thread_(0), + shutdown_(false) + { + if (Own_Thread) + { + asio::detail::signal_blocker sb; + thread_ = new asio::detail::thread( + bind_handler(&select_reactor::call_run_thread, this)); + } + } + + // Destructor. + ~select_reactor() + { + shutdown_service(); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + stop_thread_ = true; + lock.unlock(); + + if (Own_Thread) + { + if (thread_) + { + interrupter_.interrupt(); + thread_->join(); + delete thread_; + thread_ = 0; + } + } + + op_queue<operation> ops; + + 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() + { + 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&) + { + return 0; + } + + // 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) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + { + bool first = op_queue_[op_type].enqueue_operation(descriptor, op); + io_service_.work_started(); + if (first) + interrupter_.interrupt(); + } + } + + // 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&) + { + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); + } + + // 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&) + { + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); + } + + // Add a new timer queue to the reactor. + template <typename Time_Traits> + void add_timer_queue(timer_queue<Time_Traits>& timer_queue) + { + asio::detail::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) + { + asio::detail::mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&timer_queue); + } + + // 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, timer_op* op, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + { + bool earliest = timer_queue.enqueue_timer(time, op, token); + io_service_.work_started(); + if (earliest) + interrupter_.interrupt(); + } + } + + // 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) + { + asio::detail::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; + } + + // Run select once until interrupted or events are ready to be dispatched. + void run(bool block, op_queue<operation>& ops) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Check if the thread is supposed to stop. + 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) + { + 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 && !have_work_to_do) + return; + + // Determine how long to block while waiting for events. + timeval tv_buf = { 0, 0 }; + timeval* tv = block ? get_timeout(tv_buf) : &tv_buf; + + lock.unlock(); + + // Block on the select call until descriptors become ready. + asio::error_code ec; + int retval = socket_ops::select(static_cast<int>(max_fd + 1), + fds[read_op], fds[write_op], fds[except_op], tv, ec); + + // Reset the interrupter. + 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. + for (int i = max_select_ops - 1; i >= 0; --i) + op_queue_[i].perform_operations_for_descriptors(fds[i], ops); + } + timer_queues_.get_ready_timers(ops); + } + + // Interrupt the select loop. + void interrupt() + { + interrupter_.interrupt(); + } + +private: + // Run the select loop in the thread. + void run_thread() + { + if (Own_Thread) + { + 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) + { + if (Own_Thread) + { + reactor->run_thread(); + } + } + + // Get the timeout value for the select call. + timeval* get_timeout(timeval& tv) + { + // 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. + 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. This function + // does not acquire the select_reactor's mutex. + void cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec) + { + 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(); + } + + // The io_service implementation used to post completions. + io_service_impl& io_service_; + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The interrupter is used to break a blocking select call. + select_interrupter interrupter_; + + // The queues of read, write and except operations. + reactor_op_queue<socket_type> op_queue_[max_ops]; + + // The timer queues. + timer_queue_set timer_queues_; + + // Does the reactor loop thread need to stop. + bool stop_thread_; + + // The thread that is running the reactor loop. + asio::detail::thread* thread_; + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SELECT_REACTOR_HPP diff --git a/ext/asio/detail/select_reactor_fwd.hpp b/ext/asio/detail/select_reactor_fwd.hpp new file mode 100644 index 0000000000..0b72e7e8aa --- /dev/null +++ b/ext/asio/detail/select_reactor_fwd.hpp @@ -0,0 +1,31 @@ +// +// select_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 ASIO_DETAIL_SELECT_REACTOR_FWD_HPP +#define ASIO_DETAIL_SELECT_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template <bool Own_Thread> +class select_reactor; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SELECT_REACTOR_FWD_HPP diff --git a/ext/asio/detail/service_base.hpp b/ext/asio/detail/service_base.hpp new file mode 100644 index 0000000000..45000c3f89 --- /dev/null +++ b/ext/asio/detail/service_base.hpp @@ -0,0 +1,49 @@ +// +// service_base.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 ASIO_DETAIL_SERVICE_BASE_HPP +#define ASIO_DETAIL_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/service_id.hpp" + +namespace asio { +namespace detail { + +// Special service base class to keep classes header-file only. +template <typename Type> +class service_base + : public asio::io_service::service +{ +public: + static asio::detail::service_id<Type> id; + + // Constructor. + service_base(asio::io_service& io_service) + : asio::io_service::service(io_service) + { + } +}; + +template <typename Type> +asio::detail::service_id<Type> service_base<Type>::id; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SERVICE_BASE_HPP diff --git a/ext/asio/detail/service_id.hpp b/ext/asio/detail/service_id.hpp new file mode 100644 index 0000000000..baf6cce22b --- /dev/null +++ b/ext/asio/detail/service_id.hpp @@ -0,0 +1,37 @@ +// +// service_id.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 ASIO_DETAIL_SERVICE_ID_HPP +#define ASIO_DETAIL_SERVICE_ID_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/io_service.hpp" + +namespace asio { +namespace detail { + +// Special derived service id type to keep classes header-file only. +template <typename Type> +class service_id + : public asio::io_service::id +{ +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SERVICE_ID_HPP diff --git a/ext/asio/detail/service_registry.hpp b/ext/asio/detail/service_registry.hpp new file mode 100644 index 0000000000..f80b4b6953 --- /dev/null +++ b/ext/asio/detail/service_registry.hpp @@ -0,0 +1,275 @@ +// +// service_registry.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 ASIO_DETAIL_SERVICE_REGISTRY_HPP +#define ASIO_DETAIL_SERVICE_REGISTRY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <typeinfo> +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/service_id.hpp" + +#if defined(BOOST_NO_TYPEID) +# if !defined(ASIO_NO_TYPEID) +# define ASIO_NO_TYPEID +# endif // !defined(ASIO_NO_TYPEID) +#endif // defined(BOOST_NO_TYPEID) + +namespace asio { +namespace detail { + +#if defined(__GNUC__) +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility push (default) +# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +#endif // defined(__GNUC__) + +template <typename T> +class typeid_wrapper {}; + +#if defined(__GNUC__) +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility pop +# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +#endif // defined(__GNUC__) + +class service_registry + : private noncopyable +{ +public: + // Constructor. + service_registry(asio::io_service& o) + : owner_(o), + first_service_(0) + { + } + + // Destructor. + ~service_registry() + { + // Shutdown all services. This must be done in a separate loop before the + // services are destroyed since the destructors of user-defined handler + // objects may try to access other service objects. + asio::io_service::service* service = first_service_; + while (service) + { + service->shutdown_service(); + service = service->next_; + } + + // Destroy all services. + while (first_service_) + { + asio::io_service::service* next_service = first_service_->next_; + destroy(first_service_); + first_service_ = next_service; + } + } + + // Get the service object corresponding to the specified service type. Will + // create a new service object automatically if no such object already + // exists. Ownership of the service object is not transferred to the caller. + template <typename Service> + Service& use_service() + { + 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) + { + 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 + { + 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(asio::io_service::service::key& key, + const asio::io_service::id& id) + { + key.type_info_ = 0; + key.id_ = &id; + } + +#if !defined(ASIO_NO_TYPEID) + // Initialise a service's key based on its id. + template <typename Service> + void init_key(asio::io_service::service::key& key, + const asio::detail::service_id<Service>& /*id*/) + { + key.type_info_ = &typeid(typeid_wrapper<Service>); + key.id_ = 0; + } +#endif // !defined(ASIO_NO_TYPEID) + + // Check if a service matches the given id. + static bool keys_match( + const asio::io_service::service::key& key1, + const 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 asio::io_service::service* + (*factory_type)(asio::io_service&); + + // Factory function for creating a service instance. + template <typename Service> + static asio::io_service::service* create( + asio::io_service& owner) + { + return new Service(owner); + } + + // Destroy a service instance. + static void destroy(asio::io_service::service* service) + { + delete service; + } + + // Helper class to manage service pointers. + struct auto_service_ptr + { + 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. + asio::io_service::service* do_use_service( + const asio::io_service::service::key& key, + factory_type factory) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // First see if there is an existing service object with the given key. + asio::io_service::service* service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return service; + service = service->next_; + } + + // Create a new service object. The service registry's mutex is not locked + // at this time to allow for nested calls into this function from the new + // service's constructor. + lock.unlock(); + 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 + // while the lock was released. + service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return service; + service = service->next_; + } + + // Service was successfully initialised, pass ownership to registry. + 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. + bool do_add_service( + const asio::io_service::service::key& key, + asio::io_service::service* new_service) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Check if there is an existing service object with the given key. + asio::io_service::service* service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return false; + service = service->next_; + } + + // Take ownership of the service object. + new_service->key_ = key; + new_service->next_ = first_service_; + first_service_ = new_service; + + return true; + } + + // Check whether a service object with the specified key already exists. + bool do_has_service(const asio::io_service::service::key& key) const + { + asio::detail::mutex::scoped_lock lock(mutex_); + + asio::io_service::service* service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return true; + service = service->next_; + } + + return false; + } + + // Mutex to protect access to internal data. + mutable asio::detail::mutex mutex_; + + // The owner of this service registry and the services it contains. + asio::io_service& owner_; + + // The first service in the list of contained services. + asio::io_service::service* first_service_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SERVICE_REGISTRY_HPP diff --git a/ext/asio/detail/service_registry_fwd.hpp b/ext/asio/detail/service_registry_fwd.hpp new file mode 100644 index 0000000000..423bb4beef --- /dev/null +++ b/ext/asio/detail/service_registry_fwd.hpp @@ -0,0 +1,30 @@ +// +// service_registry_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 ASIO_DETAIL_SERVICE_REGISTRY_FWD_HPP +#define ASIO_DETAIL_SERVICE_REGISTRY_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class service_registry; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SERVICE_REGISTRY_FWD_HPP diff --git a/ext/asio/detail/signal_blocker.hpp b/ext/asio/detail/signal_blocker.hpp new file mode 100644 index 0000000000..52f70c8578 --- /dev/null +++ b/ext/asio/detail/signal_blocker.hpp @@ -0,0 +1,50 @@ +// +// signal_blocker.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 ASIO_DETAIL_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +# include "asio/detail/null_signal_blocker.hpp" +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# include "asio/detail/win_signal_blocker.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_signal_blocker.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +typedef null_signal_blocker signal_blocker; +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef win_signal_blocker signal_blocker; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_signal_blocker signal_blocker; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SIGNAL_BLOCKER_HPP diff --git a/ext/asio/detail/signal_init.hpp b/ext/asio/detail/signal_init.hpp new file mode 100644 index 0000000000..12f17d37ac --- /dev/null +++ b/ext/asio/detail/signal_init.hpp @@ -0,0 +1,51 @@ +// +// signal_init.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 ASIO_DETAIL_SIGNAL_INIT_HPP +#define ASIO_DETAIL_SIGNAL_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" +#include <csignal> +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +template <int Signal = SIGPIPE> +class signal_init +{ +public: + // Constructor. + signal_init() + { + std::signal(Signal, SIG_IGN); + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SIGNAL_INIT_HPP diff --git a/ext/asio/detail/socket_holder.hpp b/ext/asio/detail/socket_holder.hpp new file mode 100644 index 0000000000..82a38848a5 --- /dev/null +++ b/ext/asio/detail/socket_holder.hpp @@ -0,0 +1,95 @@ +// +// socket_holder.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 ASIO_DETAIL_SOCKET_HOLDER_HPP +#define ASIO_DETAIL_SOCKET_HOLDER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_ops.hpp" + +namespace asio { +namespace detail { + +// Implement the resource acquisition is initialisation idiom for sockets. +class socket_holder + : private noncopyable +{ +public: + // Construct as an uninitialised socket. + socket_holder() + : socket_(invalid_socket) + { + } + + // Construct to take ownership of the specified socket. + explicit socket_holder(socket_type s) + : socket_(s) + { + } + + // Destructor. + ~socket_holder() + { + if (socket_ != invalid_socket) + { + asio::error_code ec; + socket_ops::close(socket_, ec); + } + } + + // Get the underlying socket. + socket_type get() const + { + return socket_; + } + + // Reset to an uninitialised socket. + void reset() + { + if (socket_ != invalid_socket) + { + asio::error_code ec; + socket_ops::close(socket_, ec); + socket_ = invalid_socket; + } + } + + // Reset to take ownership of the specified socket. + void reset(socket_type s) + { + reset(); + socket_ = s; + } + + // Release ownership of the socket. + socket_type release() + { + socket_type tmp = socket_; + socket_ = invalid_socket; + return tmp; + } + +private: + // The underlying socket. + socket_type socket_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_HOLDER_HPP diff --git a/ext/asio/detail/socket_ops.hpp b/ext/asio/detail/socket_ops.hpp new file mode 100644 index 0000000000..1a863a9175 --- /dev/null +++ b/ext/asio/detail/socket_ops.hpp @@ -0,0 +1,1912 @@ +// +// socket_ops.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 ASIO_DETAIL_SOCKET_OPS_HPP +#define ASIO_DETAIL_SOCKET_OPS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include <boost/assert.hpp> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <cerrno> +#include <boost/detail/workaround.hpp> +#include <new> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { +namespace socket_ops { + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +struct msghdr { int msg_namelen; }; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#if defined(__hpux) +// HP-UX doesn't declare these functions extern "C", so they are declared again +// here to avoid linker errors about undefined symbols. +extern "C" char* if_indextoname(unsigned int, char*); +extern "C" unsigned int if_nametoindex(const char*); +#endif // defined(__hpux) + +inline void clear_error(asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + WSASetLastError(0); +#else + errno = 0; +#endif + ec = asio::error_code(); +} + +template <typename ReturnType> +inline ReturnType error_wrapper(ReturnType return_value, + asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + ec = asio::error_code(WSAGetLastError(), + asio::error::get_system_category()); +#else + ec = asio::error_code(errno, + asio::error::get_system_category()); +#endif + return return_value; +} + +template <typename SockLenType> +inline socket_type call_accept(SockLenType msghdr::*, + socket_type s, socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0; + socket_type result = ::accept(s, addr, addrlen ? &tmp_addrlen : 0); + if (addrlen) + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +inline socket_type accept(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + clear_error(ec); + + socket_type new_s = error_wrapper(call_accept( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (new_s == invalid_socket) + return new_s; + +#if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) + int optval = 1; + int result = error_wrapper(::setsockopt(new_s, + SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec); + if (result != 0) + { + ::close(new_s); + return invalid_socket; + } +#endif + + clear_error(ec); + return new_s; +} + +template <typename SockLenType> +inline int call_bind(SockLenType msghdr::*, + socket_type s, const socket_addr_type* addr, std::size_t addrlen) +{ + return ::bind(s, addr, (SockLenType)addrlen); +} + +inline int bind(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(call_bind( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (result == 0) + clear_error(ec); + return result; +} + +inline int close(socket_type s, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::closesocket(s), ec); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::close(s), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (result == 0) + clear_error(ec); + return result; +} + +inline int shutdown(socket_type s, int what, asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(::shutdown(s, what), ec); + if (result == 0) + clear_error(ec); + return result; +} + +template <typename SockLenType> +inline int call_connect(SockLenType msghdr::*, + socket_type s, const socket_addr_type* addr, std::size_t addrlen) +{ + return ::connect(s, addr, (SockLenType)addrlen); +} + +inline int connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(call_connect( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (result == 0) + clear_error(ec); + return result; +} + +inline int socketpair(int af, int type, int protocol, + socket_type sv[2], asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + (void)(af); + (void)(type); + (void)(protocol); + (void)(sv); + ec = asio::error::operation_not_supported; + return -1; +#else + clear_error(ec); + int result = error_wrapper(::socketpair(af, type, protocol, sv), ec); + if (result == 0) + clear_error(ec); + return result; +#endif +} + +inline int listen(socket_type s, int backlog, asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(::listen(s, backlog), ec); + if (result == 0) + clear_error(ec); + return result; +} + +inline void init_buf_iov_base(void*& base, void* addr) +{ + base = addr; +} + +template <typename T> +inline void init_buf_iov_base(T& base, void* addr) +{ + base = static_cast<T>(addr); +} + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef WSABUF buf; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef iovec buf; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +inline void init_buf(buf& b, void* data, size_t size) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + b.buf = static_cast<char*>(data); + b.len = static_cast<u_long>(size); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + init_buf_iov_base(b.iov_base, data); + b.iov_len = size; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline void init_buf(buf& b, const void* data, size_t size) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + b.buf = static_cast<char*>(const_cast<void*>(data)); + b.len = static_cast<u_long>(size); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + init_buf_iov_base(b.iov_base, const_cast<void*>(data)); + b.iov_len = size; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline void init_msghdr_msg_name(void*& name, socket_addr_type* addr) +{ + name = addr; +} + +inline void init_msghdr_msg_name(void*& name, const socket_addr_type* addr) +{ + name = const_cast<socket_addr_type*>(addr); +} + +template <typename T> +inline void init_msghdr_msg_name(T& name, socket_addr_type* addr) +{ + name = reinterpret_cast<T>(addr); +} + +template <typename T> +inline void init_msghdr_msg_name(T& name, const socket_addr_type* addr) +{ + name = reinterpret_cast<T>(const_cast<socket_addr_type*>(addr)); +} + +inline int recv(socket_type s, buf* bufs, size_t count, int flags, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast<DWORD>(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = error_wrapper(::WSARecv(s, bufs, + recv_buf_count, &bytes_transferred, &recv_flags, 0, 0), ec); + if (result != 0) + return -1; + clear_error(ec); + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + msg.msg_iov = bufs; + msg.msg_iovlen = count; + int result = error_wrapper(::recvmsg(s, &msg, flags), ec); + if (result >= 0) + clear_error(ec); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int recvfrom(socket_type s, buf* bufs, size_t count, int flags, + socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast<DWORD>(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int tmp_addrlen = (int)*addrlen; + int result = error_wrapper(::WSARecvFrom(s, bufs, recv_buf_count, + &bytes_transferred, &recv_flags, addr, &tmp_addrlen, 0, 0), ec); + *addrlen = (std::size_t)tmp_addrlen; + if (result != 0) + return -1; + clear_error(ec); + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + init_msghdr_msg_name(msg.msg_name, addr); + msg.msg_namelen = *addrlen; + msg.msg_iov = bufs; + msg.msg_iovlen = count; + int result = error_wrapper(::recvmsg(s, &msg, flags), ec); + *addrlen = msg.msg_namelen; + if (result >= 0) + clear_error(ec); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int send(socket_type s, const buf* bufs, size_t count, int flags, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Send the data. + DWORD send_buf_count = static_cast<DWORD>(count); + DWORD bytes_transferred = 0; + DWORD send_flags = flags; + int result = error_wrapper(::WSASend(s, const_cast<buf*>(bufs), + send_buf_count, &bytes_transferred, send_flags, 0, 0), ec); + if (result != 0) + return -1; + clear_error(ec); + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + msg.msg_iov = const_cast<buf*>(bufs); + msg.msg_iovlen = count; +#if defined(__linux__) + flags |= MSG_NOSIGNAL; +#endif // defined(__linux__) + int result = error_wrapper(::sendmsg(s, &msg, flags), ec); + if (result >= 0) + clear_error(ec); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int sendto(socket_type s, const buf* bufs, size_t count, int flags, + const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Send the data. + DWORD send_buf_count = static_cast<DWORD>(count); + DWORD bytes_transferred = 0; + int result = error_wrapper(::WSASendTo(s, const_cast<buf*>(bufs), + send_buf_count, &bytes_transferred, flags, addr, + static_cast<int>(addrlen), 0, 0), ec); + if (result != 0) + return -1; + clear_error(ec); + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + init_msghdr_msg_name(msg.msg_name, addr); + msg.msg_namelen = addrlen; + msg.msg_iov = const_cast<buf*>(bufs); + msg.msg_iovlen = count; +#if defined(__linux__) + flags |= MSG_NOSIGNAL; +#endif // defined(__linux__) + int result = error_wrapper(::sendmsg(s, &msg, flags), ec); + if (result >= 0) + clear_error(ec); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline socket_type socket(int af, int type, int protocol, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + socket_type s = error_wrapper(::WSASocket(af, type, protocol, 0, 0, + WSA_FLAG_OVERLAPPED), ec); + if (s == invalid_socket) + return s; + + if (af == AF_INET6) + { + // Try to enable the POSIX default behaviour of having IPV6_V6ONLY set to + // false. This will only succeed on Windows Vista and later versions of + // Windows, where a dual-stack IPv4/v6 implementation is available. + DWORD optval = 0; + ::setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + reinterpret_cast<const char*>(&optval), sizeof(optval)); + } + + clear_error(ec); + + return s; +#elif defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) + socket_type s = error_wrapper(::socket(af, type, protocol), ec); + if (s == invalid_socket) + return s; + + int optval = 1; + int result = error_wrapper(::setsockopt(s, + SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec); + if (result != 0) + { + ::close(s); + return invalid_socket; + } + + return s; +#else + int s = error_wrapper(::socket(af, type, protocol), ec); + if (s >= 0) + clear_error(ec); + return s; +#endif +} + +template <typename SockLenType> +inline int call_setsockopt(SockLenType msghdr::*, + socket_type s, int level, int optname, + const void* optval, std::size_t optlen) +{ + return ::setsockopt(s, level, optname, + (const char*)optval, (SockLenType)optlen); +} + +inline int setsockopt(socket_type s, int level, int optname, + const void* optval, std::size_t optlen, asio::error_code& ec) +{ + if (level == custom_socket_option_level && optname == always_fail_option) + { + ec = asio::error::invalid_argument; + return -1; + } + +#if defined(__BORLANDC__) + // Mysteriously, using the getsockopt and setsockopt functions directly with + // Borland C++ results in incorrect values being set and read. The bug can be + // worked around by using function addresses resolved with GetProcAddress. + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + typedef int (WSAAPI *sso_t)(SOCKET, int, int, const char*, int); + if (sso_t sso = (sso_t)::GetProcAddress(winsock_module, "setsockopt")) + { + clear_error(ec); + return error_wrapper(sso(s, level, optname, + reinterpret_cast<const char*>(optval), + static_cast<int>(optlen)), ec); + } + } + ec = asio::error::fault; + return -1; +#else // defined(__BORLANDC__) + clear_error(ec); + int result = error_wrapper(call_setsockopt(&msghdr::msg_namelen, + s, level, optname, optval, optlen), ec); + if (result == 0) + clear_error(ec); + return result; +#endif // defined(__BORLANDC__) +} + +template <typename SockLenType> +inline int call_getsockopt(SockLenType msghdr::*, + socket_type s, int level, int optname, + void* optval, std::size_t* optlen) +{ + SockLenType tmp_optlen = (SockLenType)*optlen; + int result = ::getsockopt(s, level, optname, (char*)optval, &tmp_optlen); + *optlen = (std::size_t)tmp_optlen; + return result; +} + +inline int getsockopt(socket_type s, int level, int optname, void* optval, + size_t* optlen, asio::error_code& ec) +{ + if (level == custom_socket_option_level && optname == always_fail_option) + { + ec = asio::error::invalid_argument; + return -1; + } + +#if defined(__BORLANDC__) + // Mysteriously, using the getsockopt and setsockopt functions directly with + // Borland C++ results in incorrect values being set and read. The bug can be + // worked around by using function addresses resolved with GetProcAddress. + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + typedef int (WSAAPI *gso_t)(SOCKET, int, int, char*, int*); + if (gso_t gso = (gso_t)::GetProcAddress(winsock_module, "getsockopt")) + { + clear_error(ec); + int tmp_optlen = static_cast<int>(*optlen); + int result = error_wrapper(gso(s, level, optname, + reinterpret_cast<char*>(optval), &tmp_optlen), ec); + *optlen = static_cast<size_t>(tmp_optlen); + if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY + && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) + { + // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are + // only supported on Windows Vista and later. To simplify program logic + // we will fake success of getting this option and specify that the + // value is non-zero (i.e. true). This corresponds to the behavior of + // IPv6 sockets on Windows platforms pre-Vista. + *static_cast<DWORD*>(optval) = 1; + clear_error(ec); + } + return result; + } + } + ec = asio::error::fault; + return -1; +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) + clear_error(ec); + int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen, + s, level, optname, optval, optlen), ec); + if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY + && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) + { + // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are only + // supported on Windows Vista and later. To simplify program logic we will + // fake success of getting this option and specify that the value is + // non-zero (i.e. true). This corresponds to the behavior of IPv6 sockets + // on Windows platforms pre-Vista. + *static_cast<DWORD*>(optval) = 1; + clear_error(ec); + } + if (result == 0) + clear_error(ec); + return result; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + clear_error(ec); + int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen, + s, level, optname, optval, optlen), ec); +#if defined(__linux__) + if (result == 0 && level == SOL_SOCKET && *optlen == sizeof(int) + && (optname == SO_SNDBUF || optname == SO_RCVBUF)) + { + // On Linux, setting SO_SNDBUF or SO_RCVBUF to N actually causes the kernel + // to set the buffer size to N*2. Linux puts additional stuff into the + // buffers so that only about half is actually available to the application. + // The retrieved value is divided by 2 here to make it appear as though the + // correct value has been set. + *static_cast<int*>(optval) /= 2; + } +#endif // defined(__linux__) + if (result == 0) + clear_error(ec); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +template <typename SockLenType> +inline int call_getpeername(SockLenType msghdr::*, + socket_type s, socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = (SockLenType)*addrlen; + int result = ::getpeername(s, addr, &tmp_addrlen); + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +inline int getpeername(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(call_getpeername( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (result == 0) + clear_error(ec); + return result; +} + +template <typename SockLenType> +inline int call_getsockname(SockLenType msghdr::*, + socket_type s, socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = (SockLenType)*addrlen; + int result = ::getsockname(s, addr, &tmp_addrlen); + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +inline int getsockname(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(call_getsockname( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (result == 0) + clear_error(ec); + return result; +} + +inline int ioctl(socket_type s, long cmd, ioctl_arg_type* arg, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::ioctlsocket(s, cmd, arg), ec); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::ioctl(s, cmd, arg), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (result >= 0) + clear_error(ec); + return result; +} + +inline int select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, timeval* timeout, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (!readfds && !writefds && !exceptfds && timeout) + { + DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; + if (milliseconds == 0) + milliseconds = 1; // Force context switch. + ::Sleep(milliseconds); + ec = asio::error_code(); + return 0; + } + + // The select() call allows timeout values measured in microseconds, but the + // system clock (as wrapped by boost::posix_time::microsec_clock) typically + // has a resolution of 10 milliseconds. This can lead to a spinning select + // reactor, meaning increased CPU usage, when waiting for the earliest + // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight + // spin we'll use a minimum timeout of 1 millisecond. + if (timeout && timeout->tv_sec == 0 + && timeout->tv_usec > 0 && timeout->tv_usec < 1000) + timeout->tv_usec = 1000; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#if defined(__hpux) && defined(__HP_aCC) + timespec ts; + ts.tv_sec = timeout ? timeout->tv_sec : 0; + ts.tv_nsec = timeout ? timeout->tv_usec * 1000 : 0; + return error_wrapper(::pselect(nfds, readfds, + writefds, exceptfds, timeout ? &ts : 0, 0), ec); +#else + int result = error_wrapper(::select(nfds, readfds, + writefds, exceptfds, timeout), ec); + if (result >= 0) + clear_error(ec); + return result; +#endif +} + +inline int poll_read(socket_type s, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + FD_SET fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + clear_error(ec); + int result = error_wrapper(::select(s, &fds, 0, 0, 0), ec); + if (result >= 0) + clear_error(ec); + return result; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + pollfd fds; + fds.fd = s; + fds.events = POLLIN; + fds.revents = 0; + clear_error(ec); + int result = error_wrapper(::poll(&fds, 1, -1), ec); + if (result >= 0) + clear_error(ec); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int poll_write(socket_type s, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + FD_SET fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + clear_error(ec); + int result = error_wrapper(::select(s, 0, &fds, 0, 0), ec); + if (result >= 0) + clear_error(ec); + return result; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + clear_error(ec); + int result = error_wrapper(::poll(&fds, 1, -1), ec); + if (result >= 0) + clear_error(ec); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int poll_connect(socket_type s, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + FD_SET write_fds; + FD_ZERO(&write_fds); + FD_SET(s, &write_fds); + FD_SET except_fds; + FD_ZERO(&except_fds); + FD_SET(s, &except_fds); + clear_error(ec); + int result = error_wrapper(::select(s, 0, &write_fds, &except_fds, 0), ec); + if (result >= 0) + clear_error(ec); + return result; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + clear_error(ec); + int result = error_wrapper(::poll(&fds, 1, -1), ec); + if (result >= 0) + clear_error(ec); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline const char* inet_ntop(int af, const void* src, char* dest, size_t length, + unsigned long scope_id, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For memcpy. + + if (af != AF_INET && af != AF_INET6) + { + ec = asio::error::address_family_not_supported; + return 0; + } + + union + { + socket_addr_type base; + sockaddr_storage_type storage; + sockaddr_in4_type v4; + sockaddr_in6_type v6; + } address; + DWORD address_length; + if (af == AF_INET) + { + address_length = sizeof(sockaddr_in4_type); + address.v4.sin_family = AF_INET; + address.v4.sin_port = 0; + memcpy(&address.v4.sin_addr, src, sizeof(in4_addr_type)); + } + else // AF_INET6 + { + address_length = sizeof(sockaddr_in6_type); + address.v6.sin6_family = AF_INET6; + address.v6.sin6_port = 0; + address.v6.sin6_flowinfo = 0; + address.v6.sin6_scope_id = scope_id; + memcpy(&address.v6.sin6_addr, src, sizeof(in6_addr_type)); + } + + DWORD string_length = static_cast<DWORD>(length); +#if defined(BOOST_NO_ANSI_APIS) + LPWSTR string_buffer = (LPWSTR)_alloca(length * sizeof(WCHAR)); + int result = error_wrapper(::WSAAddressToStringW(&address.base, + address_length, 0, string_buffer, &string_length), ec); + ::WideCharToMultiByte(CP_ACP, 0, string_buffer, -1, dest, length, 0, 0); +#else + int result = error_wrapper(::WSAAddressToStringA( + &address.base, address_length, 0, dest, &string_length), ec); +#endif + + // Windows may set error code on success. + if (result != socket_error_retval) + clear_error(ec); + + // Windows may not set an error code on failure. + else if (result == socket_error_retval && !ec) + ec = asio::error::invalid_argument; + + return result == socket_error_retval ? 0 : dest; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + const char* result = error_wrapper(::inet_ntop(af, src, dest, length), ec); + if (result == 0 && !ec) + ec = asio::error::invalid_argument; + if (result != 0 && af == AF_INET6 && scope_id != 0) + { + using namespace std; // For strcat and sprintf. + char if_name[IF_NAMESIZE + 1] = "%"; + const in6_addr_type* ipv6_address = static_cast<const in6_addr_type*>(src); + bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address); + if (!is_link_local || if_indextoname(scope_id, if_name + 1) == 0) + sprintf(if_name + 1, "%lu", scope_id); + strcat(dest, if_name); + } + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int inet_pton(int af, const char* src, void* dest, + unsigned long* scope_id, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For memcpy and strcmp. + + if (af != AF_INET && af != AF_INET6) + { + ec = asio::error::address_family_not_supported; + return -1; + } + + union + { + socket_addr_type base; + sockaddr_storage_type storage; + sockaddr_in4_type v4; + sockaddr_in6_type v6; + } address; + int address_length = sizeof(sockaddr_storage_type); +#if defined(BOOST_NO_ANSI_APIS) + int num_wide_chars = strlen(src) + 1; + LPWSTR wide_buffer = (LPWSTR)_alloca(num_wide_chars * sizeof(WCHAR)); + ::MultiByteToWideChar(CP_ACP, 0, src, -1, wide_buffer, num_wide_chars); + int result = error_wrapper(::WSAStringToAddressW( + wide_buffer, af, 0, &address.base, &address_length), ec); +#else + int result = error_wrapper(::WSAStringToAddressA( + const_cast<char*>(src), af, 0, &address.base, &address_length), ec); +#endif + + if (af == AF_INET) + { + if (result != socket_error_retval) + { + memcpy(dest, &address.v4.sin_addr, sizeof(in4_addr_type)); + clear_error(ec); + } + else if (strcmp(src, "255.255.255.255") == 0) + { + static_cast<in4_addr_type*>(dest)->s_addr = INADDR_NONE; + clear_error(ec); + } + } + else // AF_INET6 + { + if (result != socket_error_retval) + { + memcpy(dest, &address.v6.sin6_addr, sizeof(in6_addr_type)); + if (scope_id) + *scope_id = address.v6.sin6_scope_id; + clear_error(ec); + } + } + + // Windows may not set an error code on failure. + if (result == socket_error_retval && !ec) + ec = asio::error::invalid_argument; + + if (result != socket_error_retval) + clear_error(ec); + + return result == socket_error_retval ? -1 : 1; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::inet_pton(af, src, dest), ec); + if (result <= 0 && !ec) + ec = asio::error::invalid_argument; + if (result > 0 && af == AF_INET6 && scope_id) + { + using namespace std; // For strchr and atoi. + *scope_id = 0; + if (const char* if_name = strchr(src, '%')) + { + in6_addr_type* ipv6_address = static_cast<in6_addr_type*>(dest); + bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address); + if (is_link_local) + *scope_id = if_nametoindex(if_name + 1); + if (*scope_id == 0) + *scope_id = atoi(if_name + 1); + } + } + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int gethostname(char* name, int namelen, asio::error_code& ec) +{ + clear_error(ec); + int result = error_wrapper(::gethostname(name, namelen), ec); +#if defined(BOOST_WINDOWS) + if (result == 0) + clear_error(ec); +#endif + return result; +} + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) \ + || defined(__MACH__) && defined(__APPLE__) + +// The following functions are only needed for emulation of getaddrinfo and +// getnameinfo. + +inline asio::error_code translate_netdb_error(int error) +{ + switch (error) + { + case 0: + return asio::error_code(); + case HOST_NOT_FOUND: + return asio::error::host_not_found; + case TRY_AGAIN: + return asio::error::host_not_found_try_again; + case NO_RECOVERY: + return asio::error::no_recovery; + case NO_DATA: + return asio::error::no_data; + default: + BOOST_ASSERT(false); + return asio::error::invalid_argument; + } +} + +inline hostent* gethostbyaddr(const char* addr, int length, int af, + hostent* result, char* buffer, int buflength, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + (void)(buffer); + (void)(buflength); + hostent* retval = error_wrapper(::gethostbyaddr(addr, length, af), ec); + if (!retval) + return 0; + clear_error(ec); + *result = *retval; + return retval; +#elif defined(__sun) || defined(__QNX__) + int error = 0; + hostent* retval = error_wrapper(::gethostbyaddr_r(addr, length, af, result, + buffer, buflength, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + (void)(buffer); + (void)(buflength); + int error = 0; + hostent* retval = error_wrapper(::getipnodebyaddr( + addr, length, af, &error), ec); + if (error) + ec = translate_netdb_error(error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + hostent* retval = 0; + int error = 0; + error_wrapper(::gethostbyaddr_r(addr, length, af, result, buffer, + buflength, &retval, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#endif +} + +inline hostent* gethostbyname(const char* name, int af, struct hostent* result, + char* buffer, int buflength, int ai_flags, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + (void)(buffer); + (void)(buflength); + (void)(ai_flags); + if (af != AF_INET) + { + ec = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = error_wrapper(::gethostbyname(name), ec); + if (!retval) + return 0; + clear_error(ec); + *result = *retval; + return result; +#elif defined(__sun) || defined(__QNX__) + (void)(ai_flags); + if (af != AF_INET) + { + ec = asio::error::address_family_not_supported; + return 0; + } + int error = 0; + hostent* retval = error_wrapper(::gethostbyname_r(name, result, buffer, + buflength, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + (void)(buffer); + (void)(buflength); + int error = 0; + hostent* retval = error_wrapper(::getipnodebyname( + name, af, ai_flags, &error), ec); + if (error) + ec = translate_netdb_error(error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + (void)(ai_flags); + if (af != AF_INET) + { + ec = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = 0; + int error = 0; + error_wrapper(::gethostbyname_r(name, result, + buffer, buflength, &retval, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#endif +} + +inline void freehostent(hostent* h) +{ +#if defined(__MACH__) && defined(__APPLE__) + if (h) + ::freehostent(h); +#else + (void)(h); +#endif +} + +// Emulation of getaddrinfo based on implementation in: +// Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998. + +struct gai_search +{ + const char* host; + int family; +}; + +inline int gai_nsearch(const char* host, + const addrinfo_type* hints, gai_search (&search)[2]) +{ + int search_count = 0; + if (host == 0 || host[0] == '\0') + { + if (hints->ai_flags & AI_PASSIVE) + { + // No host and AI_PASSIVE implies wildcard bind. + switch (hints->ai_family) + { + case AF_INET: + search[search_count].host = "0.0.0.0"; + search[search_count].family = AF_INET; + ++search_count; + break; + case AF_INET6: + search[search_count].host = "0::0"; + search[search_count].family = AF_INET6; + ++search_count; + break; + case AF_UNSPEC: + search[search_count].host = "0::0"; + search[search_count].family = AF_INET6; + ++search_count; + search[search_count].host = "0.0.0.0"; + search[search_count].family = AF_INET; + ++search_count; + break; + default: + break; + } + } + else + { + // No host and not AI_PASSIVE means connect to local host. + switch (hints->ai_family) + { + case AF_INET: + search[search_count].host = "localhost"; + search[search_count].family = AF_INET; + ++search_count; + break; + case AF_INET6: + search[search_count].host = "localhost"; + search[search_count].family = AF_INET6; + ++search_count; + break; + case AF_UNSPEC: + search[search_count].host = "localhost"; + search[search_count].family = AF_INET6; + ++search_count; + search[search_count].host = "localhost"; + search[search_count].family = AF_INET; + ++search_count; + break; + default: + break; + } + } + } + else + { + // Host is specified. + switch (hints->ai_family) + { + case AF_INET: + search[search_count].host = host; + search[search_count].family = AF_INET; + ++search_count; + break; + case AF_INET6: + search[search_count].host = host; + search[search_count].family = AF_INET6; + ++search_count; + break; + case AF_UNSPEC: + search[search_count].host = host; + search[search_count].family = AF_INET6; + ++search_count; + search[search_count].host = host; + search[search_count].family = AF_INET; + ++search_count; + break; + default: + break; + } + } + return search_count; +} + +template <typename T> +inline T* gai_alloc(std::size_t size = sizeof(T)) +{ + using namespace std; + T* p = static_cast<T*>(::operator new(size, std::nothrow)); + if (p) + memset(p, 0, size); + return p; +} + +inline void gai_free(void* p) +{ + ::operator delete(p); +} + +inline void gai_strcpy(char* target, const char* source, std::size_t max_size) +{ + using namespace std; +#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE) + strcpy_s(target, max_size, source); +#else + *target = 0; + strncat(target, source, max_size); +#endif +} + +enum { gai_clone_flag = 1 << 30 }; + +inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints, + const void* addr, int family) +{ + using namespace std; + + addrinfo_type* ai = gai_alloc<addrinfo_type>(); + if (ai == 0) + return EAI_MEMORY; + + ai->ai_next = 0; + **next = ai; + *next = &ai->ai_next; + + ai->ai_canonname = 0; + ai->ai_socktype = hints->ai_socktype; + if (ai->ai_socktype == 0) + ai->ai_flags |= gai_clone_flag; + ai->ai_protocol = hints->ai_protocol; + ai->ai_family = family; + + switch (ai->ai_family) + { + case AF_INET: + { + sockaddr_in4_type* sinptr = gai_alloc<sockaddr_in4_type>(); + if (sinptr == 0) + return EAI_MEMORY; + sinptr->sin_family = AF_INET; + memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type)); + ai->ai_addr = reinterpret_cast<sockaddr*>(sinptr); + ai->ai_addrlen = sizeof(sockaddr_in4_type); + break; + } + case AF_INET6: + { + sockaddr_in6_type* sin6ptr = gai_alloc<sockaddr_in6_type>(); + if (sin6ptr == 0) + return EAI_MEMORY; + sin6ptr->sin6_family = AF_INET6; + memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type)); + ai->ai_addr = reinterpret_cast<sockaddr*>(sin6ptr); + ai->ai_addrlen = sizeof(sockaddr_in6_type); + break; + } + default: + break; + } + + return 0; +} + +inline addrinfo_type* gai_clone(addrinfo_type* ai) +{ + using namespace std; + + addrinfo_type* new_ai = gai_alloc<addrinfo_type>(); + if (new_ai == 0) + return new_ai; + + new_ai->ai_next = ai->ai_next; + ai->ai_next = new_ai; + + new_ai->ai_flags = 0; + new_ai->ai_family = ai->ai_family; + new_ai->ai_socktype = ai->ai_socktype; + new_ai->ai_protocol = ai->ai_protocol; + new_ai->ai_canonname = 0; + new_ai->ai_addrlen = ai->ai_addrlen; + new_ai->ai_addr = gai_alloc<sockaddr>(ai->ai_addrlen); + memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen); + + return new_ai; +} + +inline int gai_port(addrinfo_type* aihead, int port, int socktype) +{ + int num_found = 0; + + for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next) + { + if (ai->ai_flags & gai_clone_flag) + { + if (ai->ai_socktype != 0) + { + ai = gai_clone(ai); + if (ai == 0) + return -1; + // ai now points to newly cloned entry. + } + } + else if (ai->ai_socktype != socktype) + { + // Ignore if mismatch on socket type. + continue; + } + + ai->ai_socktype = socktype; + + switch (ai->ai_family) + { + case AF_INET: + { + sockaddr_in4_type* sinptr = + reinterpret_cast<sockaddr_in4_type*>(ai->ai_addr); + sinptr->sin_port = port; + ++num_found; + break; + } + case AF_INET6: + { + sockaddr_in6_type* sin6ptr = + reinterpret_cast<sockaddr_in6_type*>(ai->ai_addr); + sin6ptr->sin6_port = port; + ++num_found; + break; + } + default: + break; + } + } + + return num_found; +} + +inline int gai_serv(addrinfo_type* aihead, + const addrinfo_type* hints, const char* serv) +{ + using namespace std; + + int num_found = 0; + + if ( +#if defined(AI_NUMERICSERV) + (hints->ai_flags & AI_NUMERICSERV) || +#endif + isdigit(serv[0])) + { + int port = htons(atoi(serv)); + if (hints->ai_socktype) + { + // Caller specifies socket type. + int rc = gai_port(aihead, port, hints->ai_socktype); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + else + { + // Caller does not specify socket type. + int rc = gai_port(aihead, port, SOCK_STREAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + rc = gai_port(aihead, port, SOCK_DGRAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + else + { + // Try service name with TCP first, then UDP. + if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAM) + { + servent* sptr = getservbyname(serv, "tcp"); + if (sptr != 0) + { + int rc = gai_port(aihead, sptr->s_port, SOCK_STREAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAM) + { + servent* sptr = getservbyname(serv, "udp"); + if (sptr != 0) + { + int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + } + + if (num_found == 0) + { + if (hints->ai_socktype == 0) + { + // All calls to getservbyname() failed. + return EAI_NONAME; + } + else + { + // Service not supported for socket type. + return EAI_SERVICE; + } + } + + return 0; +} + +inline int gai_echeck(const char* host, const char* service, + int flags, int family, int socktype, int protocol) +{ + (void)(flags); + (void)(protocol); + + // Host or service must be specified. + if (host == 0 || host[0] == '\0') + if (service == 0 || service[0] == '\0') + return EAI_NONAME; + + // Check combination of family and socket type. + switch (family) + { + case AF_UNSPEC: + break; + case AF_INET: + case AF_INET6: + if (service != 0 && service[0] != '\0') + if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM) + return EAI_SOCKTYPE; + break; + default: + return EAI_FAMILY; + } + + return 0; +} + +inline void freeaddrinfo_emulation(addrinfo_type* aihead) +{ + addrinfo_type* ai = aihead; + while (ai) + { + gai_free(ai->ai_addr); + gai_free(ai->ai_canonname); + addrinfo_type* ainext = ai->ai_next; + gai_free(ai); + ai = ainext; + } +} + +inline int getaddrinfo_emulation(const char* host, const char* service, + const addrinfo_type* hintsp, addrinfo_type** result) +{ + // Set up linked list of addrinfo structures. + addrinfo_type* aihead = 0; + addrinfo_type** ainext = &aihead; + char* canon = 0; + + // Supply default hints if not specified by caller. + addrinfo_type hints = addrinfo_type(); + hints.ai_family = AF_UNSPEC; + if (hintsp) + hints = *hintsp; + + // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED + // and AI_ALL flags. +#if defined(AI_V4MAPPED) + if (hints.ai_family != AF_INET6) + hints.ai_flags &= ~AI_V4MAPPED; +#endif +#if defined(AI_ALL) + if (hints.ai_family != AF_INET6) + hints.ai_flags &= ~AI_ALL; +#endif + + // Basic error checking. + int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family, + hints.ai_socktype, hints.ai_protocol); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + return rc; + } + + gai_search search[2]; + int search_count = gai_nsearch(host, &hints, search); + for (gai_search* sptr = search; sptr < search + search_count; ++sptr) + { + // Check for IPv4 dotted decimal string. + in4_addr_type inaddr; + asio::error_code ec; + if (socket_ops::inet_pton(AF_INET, sptr->host, &inaddr, 0, ec) == 1) + { + if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return EAI_FAMILY; + } + if (sptr->family == AF_INET) + { + rc = gai_aistruct(&ainext, &hints, &inaddr, AF_INET); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return rc; + } + } + continue; + } + + // Check for IPv6 hex string. + in6_addr_type in6addr; + if (socket_ops::inet_pton(AF_INET6, sptr->host, &in6addr, 0, ec) == 1) + { + if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET6) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return EAI_FAMILY; + } + if (sptr->family == AF_INET6) + { + rc = gai_aistruct(&ainext, &hints, &in6addr, AF_INET6); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return rc; + } + } + continue; + } + + // Look up hostname. + hostent hent; + char hbuf[8192] = ""; + hostent* hptr = socket_ops::gethostbyname(sptr->host, + sptr->family, &hent, hbuf, sizeof(hbuf), hints.ai_flags, ec); + if (hptr == 0) + { + if (search_count == 2) + { + // Failure is OK if there are multiple searches. + continue; + } + freeaddrinfo_emulation(aihead); + gai_free(canon); + if (ec == asio::error::host_not_found) + return EAI_NONAME; + if (ec == asio::error::host_not_found_try_again) + return EAI_AGAIN; + if (ec == asio::error::no_recovery) + return EAI_FAIL; + if (ec == asio::error::no_data) + return EAI_NONAME; + return EAI_NONAME; + } + + // Check for address family mismatch if one was specified. + if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + socket_ops::freehostent(hptr); + return EAI_FAMILY; + } + + // Save canonical name first time. + if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0] + && (hints.ai_flags & AI_CANONNAME) && canon == 0) + { + std::size_t canon_len = strlen(hptr->h_name) + 1; + canon = gai_alloc<char>(canon_len); + if (canon == 0) + { + freeaddrinfo_emulation(aihead); + socket_ops::freehostent(hptr); + return EAI_MEMORY; + } + gai_strcpy(canon, hptr->h_name, canon_len); + } + + // Create an addrinfo structure for each returned address. + for (char** ap = hptr->h_addr_list; *ap; ++ap) + { + rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + socket_ops::freehostent(hptr); + return EAI_FAMILY; + } + } + + socket_ops::freehostent(hptr); + } + + // Check if we found anything. + if (aihead == 0) + { + gai_free(canon); + return EAI_NONAME; + } + + // Return canonical name in first entry. + if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME)) + { + if (canon) + { + aihead->ai_canonname = canon; + canon = 0; + } + else + { + std::size_t canonname_len = strlen(search[0].host) + 1; + aihead->ai_canonname = gai_alloc<char>(canonname_len); + if (aihead->ai_canonname == 0) + { + freeaddrinfo_emulation(aihead); + return EAI_MEMORY; + } + gai_strcpy(aihead->ai_canonname, search[0].host, canonname_len); + } + } + gai_free(canon); + + // Process the service name. + if (service != 0 && service[0] != '\0') + { + rc = gai_serv(aihead, &hints, service); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + return rc; + } + } + + // Return result to caller. + *result = aihead; + return 0; +} + +inline asio::error_code getnameinfo_emulation( + const socket_addr_type* sa, std::size_t salen, char* host, + std::size_t hostlen, char* serv, std::size_t servlen, int flags, + asio::error_code& ec) +{ + using namespace std; + + const char* addr; + size_t addr_len; + unsigned short port; + switch (sa->sa_family) + { + case AF_INET: + if (salen != sizeof(sockaddr_in4_type)) + { + return ec = asio::error::invalid_argument; + } + addr = reinterpret_cast<const char*>( + &reinterpret_cast<const sockaddr_in4_type*>(sa)->sin_addr); + addr_len = sizeof(in4_addr_type); + port = reinterpret_cast<const sockaddr_in4_type*>(sa)->sin_port; + break; + case AF_INET6: + if (salen != sizeof(sockaddr_in6_type)) + { + return ec = asio::error::invalid_argument; + } + addr = reinterpret_cast<const char*>( + &reinterpret_cast<const sockaddr_in6_type*>(sa)->sin6_addr); + addr_len = sizeof(in6_addr_type); + port = reinterpret_cast<const sockaddr_in6_type*>(sa)->sin6_port; + break; + default: + return ec = asio::error::address_family_not_supported; + } + + if (host && hostlen > 0) + { + if (flags & NI_NUMERICHOST) + { + if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen, 0, ec) == 0) + { + return ec; + } + } + else + { + hostent hent; + char hbuf[8192] = ""; + hostent* hptr = socket_ops::gethostbyaddr(addr, + static_cast<int>(addr_len), sa->sa_family, + &hent, hbuf, sizeof(hbuf), ec); + if (hptr && hptr->h_name && hptr->h_name[0] != '\0') + { + if (flags & NI_NOFQDN) + { + char* dot = strchr(hptr->h_name, '.'); + if (dot) + { + *dot = 0; + } + } + gai_strcpy(host, hptr->h_name, hostlen); + socket_ops::freehostent(hptr); + } + else + { + socket_ops::freehostent(hptr); + if (flags & NI_NAMEREQD) + { + return ec = asio::error::host_not_found; + } + if (socket_ops::inet_ntop(sa->sa_family, + addr, host, hostlen, 0, ec) == 0) + { + return ec; + } + } + } + } + + if (serv && servlen > 0) + { + if (flags & NI_NUMERICSERV) + { + if (servlen < 6) + { + return ec = asio::error::no_buffer_space; + } +#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE) + sprintf_s(serv, servlen, "%u", ntohs(port)); +#else + sprintf(serv, "%u", ntohs(port)); +#endif + } + else + { +#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + ::pthread_mutex_lock(&mutex); +#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + servent* sptr = ::getservbyport(port, (flags & NI_DGRAM) ? "udp" : 0); + if (sptr && sptr->s_name && sptr->s_name[0] != '\0') + { + gai_strcpy(serv, sptr->s_name, servlen); + } + else + { + if (servlen < 6) + { + return ec = asio::error::no_buffer_space; + } +#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE) + sprintf_s(serv, servlen, "%u", ntohs(port)); +#else + sprintf(serv, "%u", ntohs(port)); +#endif + } +#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + ::pthread_mutex_unlock(&mutex); +#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + } + } + + clear_error(ec); + return ec; +} + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // || defined(__MACH__) && defined(__APPLE__) + +inline asio::error_code translate_addrinfo_error(int error) +{ + switch (error) + { + case 0: + return asio::error_code(); + case EAI_AGAIN: + return asio::error::host_not_found_try_again; + case EAI_BADFLAGS: + return asio::error::invalid_argument; + case EAI_FAIL: + return asio::error::no_recovery; + case EAI_FAMILY: + return asio::error::address_family_not_supported; + case EAI_MEMORY: + return asio::error::no_memory; + case EAI_NONAME: +#if defined(EAI_ADDRFAMILY) + case EAI_ADDRFAMILY: +#endif +#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) + case EAI_NODATA: +#endif + return asio::error::host_not_found; + case EAI_SERVICE: + return asio::error::service_not_found; + case EAI_SOCKTYPE: + return asio::error::socket_type_not_supported; + default: // Possibly the non-portable EAI_SYSTEM. +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return asio::error_code( + WSAGetLastError(), asio::error::get_system_category()); +#else + return asio::error_code( + errno, asio::error::get_system_category()); +#endif + } +} + +inline asio::error_code getaddrinfo(const char* host, + const char* service, const addrinfo_type* hints, addrinfo_type** result, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE) + // Building for Windows XP, Windows Server 2003, or later. + int error = ::getaddrinfo(host, service, hints, result); + return ec = translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gai_t)(const char*, + const char*, const addrinfo_type*, addrinfo_type**); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo")) + { + int error = gai(host, service, hints, result); + return ec = translate_addrinfo_error(error); + } + } + int error = getaddrinfo_emulation(host, service, hints, result); + return ec = translate_addrinfo_error(error); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + int error = getaddrinfo_emulation(host, service, hints, result); + return ec = translate_addrinfo_error(error); +#else + int error = ::getaddrinfo(host, service, hints, result); + return ec = translate_addrinfo_error(error); +#endif +} + +inline void freeaddrinfo(addrinfo_type* ai) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE) + // Building for Windows XP, Windows Server 2003, or later. + ::freeaddrinfo(ai); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *fai_t)(addrinfo_type*); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo")) + { + fai(ai); + return; + } + } + freeaddrinfo_emulation(ai); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + freeaddrinfo_emulation(ai); +#else + ::freeaddrinfo(ai); +#endif +} + +inline asio::error_code getnameinfo(const socket_addr_type* addr, + std::size_t addrlen, char* host, std::size_t hostlen, + char* serv, std::size_t servlen, int flags, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE) + // Building for Windows XP, Windows Server 2003, or later. + clear_error(ec); + int error = ::getnameinfo(addr, static_cast<socklen_t>(addrlen), + host, static_cast<DWORD>(hostlen), + serv, static_cast<DWORD>(servlen), flags); + return ec = translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gni_t)(const socket_addr_type*, + int, char*, DWORD, char*, DWORD, int); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo")) + { + clear_error(ec); + int error = gni(addr, static_cast<int>(addrlen), + host, static_cast<DWORD>(hostlen), + serv, static_cast<DWORD>(servlen), flags); + return ec = translate_addrinfo_error(error); + } + } + clear_error(ec); + return getnameinfo_emulation(addr, addrlen, + host, hostlen, serv, servlen, flags, ec); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + using namespace std; // For memcpy. + sockaddr_storage_type tmp_addr; + memcpy(&tmp_addr, addr, addrlen); + tmp_addr.ss_len = addrlen; + addr = reinterpret_cast<socket_addr_type*>(&tmp_addr); + clear_error(ec); + return getnameinfo_emulation(addr, addrlen, + host, hostlen, serv, servlen, flags, ec); +#else + clear_error(ec); + int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags); + return ec = translate_addrinfo_error(error); +#endif +} + +inline u_long_type network_to_host_long(u_long_type value) +{ + return ntohl(value); +} + +inline u_long_type host_to_network_long(u_long_type value) +{ + return htonl(value); +} + +inline u_short_type network_to_host_short(u_short_type value) +{ + return ntohs(value); +} + +inline u_short_type host_to_network_short(u_short_type value) +{ + return htons(value); +} + +} // namespace socket_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_OPS_HPP diff --git a/ext/asio/detail/socket_option.hpp b/ext/asio/detail/socket_option.hpp new file mode 100644 index 0000000000..ac070b7018 --- /dev/null +++ b/ext/asio/detail/socket_option.hpp @@ -0,0 +1,319 @@ +// +// socket_option.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 ASIO_DETAIL_SOCKET_OPTION_HPP +#define ASIO_DETAIL_SOCKET_OPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <cstddef> +#include <stdexcept> +#include <boost/config.hpp> +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { +namespace socket_option { + +// Helper template for implementing boolean-based options. +template <int Level, int Name> +class boolean +{ +public: + // Default constructor. + boolean() + : value_(0) + { + } + + // Construct with a specific option value. + explicit boolean(bool v) + : value_(v ? 1 : 0) + { + } + + // Set the current value of the boolean. + boolean& operator=(bool v) + { + value_ = v ? 1 : 0; + return *this; + } + + // Get the current value of the boolean. + bool value() const + { + return !!value_; + } + + // Convert to bool. + operator bool() const + { + return !!value_; + } + + // Test for false. + bool operator!() const + { + return !value_; + } + + // Get the level of the socket option. + template <typename Protocol> + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template <typename Protocol> + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the boolean data. + template <typename Protocol> + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the boolean data. + template <typename Protocol> + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the boolean data. + template <typename Protocol> + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the boolean data. + template <typename Protocol> + void resize(const Protocol&, std::size_t s) + { + // On some platforms (e.g. Windows Vista), the getsockopt function will + // return the size of a boolean socket option as one byte, even though a + // four byte integer was passed in. + switch (s) + { + case sizeof(char): + value_ = *reinterpret_cast<char*>(&value_) ? 1 : 0; + break; + case sizeof(value_): + break; + default: + { + std::length_error ex("boolean socket option resize"); + boost::throw_exception(ex); + } + } + } + +private: + int value_; +}; + +// Helper template for implementing integer options. +template <int Level, int Name> +class integer +{ +public: + // Default constructor. + integer() + : value_(0) + { + } + + // Construct with a specific option value. + explicit integer(int v) + : value_(v) + { + } + + // Set the value of the int option. + integer& operator=(int v) + { + value_ = v; + return *this; + } + + // Get the current value of the int option. + int value() const + { + return value_; + } + + // Get the level of the socket option. + template <typename Protocol> + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template <typename Protocol> + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the int data. + template <typename Protocol> + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the int data. + template <typename Protocol> + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the int data. + template <typename Protocol> + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the int data. + template <typename Protocol> + void resize(const Protocol&, std::size_t s) + { + if (s != sizeof(value_)) + { + std::length_error ex("integer socket option resize"); + boost::throw_exception(ex); + } + } + +private: + int value_; +}; + +// Helper template for implementing linger options. +template <int Level, int Name> +class linger +{ +public: + // Default constructor. + linger() + { + value_.l_onoff = 0; + value_.l_linger = 0; + } + + // Construct with specific option values. + linger(bool e, int t) + { + enabled(e); + timeout BOOST_PREVENT_MACRO_SUBSTITUTION(t); + } + + // Set the value for whether linger is enabled. + void enabled(bool value) + { + value_.l_onoff = value ? 1 : 0; + } + + // Get the value for whether linger is enabled. + bool enabled() const + { + return value_.l_onoff != 0; + } + + // Set the value for the linger timeout. + void timeout BOOST_PREVENT_MACRO_SUBSTITUTION(int value) + { +#if defined(WIN32) + value_.l_linger = static_cast<u_short>(value); +#else + value_.l_linger = value; +#endif + } + + // Get the value for the linger timeout. + int timeout BOOST_PREVENT_MACRO_SUBSTITUTION() const + { + return static_cast<int>(value_.l_linger); + } + + // Get the level of the socket option. + template <typename Protocol> + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template <typename Protocol> + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the linger data. + template <typename Protocol> + ::linger* data(const Protocol&) + { + return &value_; + } + + // Get the address of the linger data. + template <typename Protocol> + const ::linger* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the linger data. + template <typename Protocol> + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the int data. + template <typename Protocol> + void resize(const Protocol&, std::size_t s) + { + if (s != sizeof(value_)) + { + std::length_error ex("linger socket option resize"); + boost::throw_exception(ex); + } + } + +private: + ::linger value_; +}; + +} // namespace socket_option +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_OPTION_HPP diff --git a/ext/asio/detail/socket_select_interrupter.hpp b/ext/asio/detail/socket_select_interrupter.hpp new file mode 100644 index 0000000000..62e1063e30 --- /dev/null +++ b/ext/asio/detail/socket_select_interrupter.hpp @@ -0,0 +1,192 @@ +// +// socket_select_interrupter.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 ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <cstdlib> +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +class socket_select_interrupter +{ +public: + // Constructor. + socket_select_interrupter() + { + asio::error_code ec; + socket_holder acceptor(socket_ops::socket( + AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); + if (acceptor.get() == invalid_socket) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + int opt = 1; + socket_ops::setsockopt(acceptor.get(), + SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec); + + using namespace std; // For memset. + sockaddr_in4_type addr; + std::size_t addr_len = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = 0; + if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr, + addr_len, ec) == socket_error_retval) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + if (socket_ops::getsockname(acceptor.get(), (socket_addr_type*)&addr, + &addr_len, ec) == socket_error_retval) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + // Some broken firewalls on Windows will intermittently cause getsockname to + // return 0.0.0.0 when the socket is actually bound to 127.0.0.1. We + // explicitly specify the target address here to work around this problem. + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + if (socket_ops::listen(acceptor.get(), + SOMAXCONN, ec) == socket_error_retval) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + socket_holder client(socket_ops::socket( + AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); + if (client.get() == invalid_socket) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr, + addr_len, ec) == socket_error_retval) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec)); + if (server.get() == invalid_socket) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(client.get(), FIONBIO, &non_blocking, ec)) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + opt = 1; + socket_ops::setsockopt(client.get(), + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); + + non_blocking = 1; + if (socket_ops::ioctl(server.get(), FIONBIO, &non_blocking, ec)) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + opt = 1; + socket_ops::setsockopt(server.get(), + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); + + read_descriptor_ = server.release(); + write_descriptor_ = client.release(); + } + + // Destructor. + ~socket_select_interrupter() + { + asio::error_code ec; + if (read_descriptor_ != invalid_socket) + socket_ops::close(read_descriptor_, ec); + if (write_descriptor_ != invalid_socket) + socket_ops::close(write_descriptor_, ec); + } + + // Interrupt the select call. + void interrupt() + { + char byte = 0; + socket_ops::buf b; + socket_ops::init_buf(b, &byte, 1); + asio::error_code ec; + socket_ops::send(write_descriptor_, &b, 1, 0, ec); + } + + // Reset the select interrupt. Returns true if the call was interrupted. + bool reset() + { + char data[1024]; + socket_ops::buf b; + socket_ops::init_buf(b, data, sizeof(data)); + asio::error_code ec; + int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); + return was_interrupted; + } + + // Get the read descriptor to be passed to select. + socket_type read_descriptor() const + { + return read_descriptor_; + } + +private: + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // byte will be written on the other end of the connection and this + // descriptor will become readable. + socket_type read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // byte may be written to this to wake up the select which is waiting for the + // other end to become readable. + socket_type write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP diff --git a/ext/asio/detail/socket_types.hpp b/ext/asio/detail/socket_types.hpp new file mode 100644 index 0000000000..34b3d3e631 --- /dev/null +++ b/ext/asio/detail/socket_types.hpp @@ -0,0 +1,216 @@ +// +// socket_types.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 ASIO_DETAIL_SOCKET_TYPES_HPP +#define ASIO_DETAIL_SOCKET_TYPES_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/push_options.hpp" +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) +# error WinSock.h has already been included +# endif // defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) +# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) || defined(__BORLANDC__) +# pragma message( \ + "Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:\n"\ + "- add -D_WIN32_WINNT=0x0501 to the compiler command line; or\n"\ + "- add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.\n"\ + "Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).") +# else // defined(_MSC_VER) || defined(__BORLANDC__) +# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. +# warning For example, add -D_WIN32_WINNT=0x0501 to the compiler command line. +# warning Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target). +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +# define _WIN32_WINNT 0x0501 +# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) +# if defined(_WIN32) && !defined(WIN32) +# if !defined(_WINSOCK2API_) +# define WIN32 // Needed for correct types in winsock2.h +# else // !defined(_WINSOCK2API_) +# error Please define the macro WIN32 in your compiler options +# endif // !defined(_WINSOCK2API_) +# endif // defined(_WIN32) && !defined(WIN32) +# endif // defined(_MSC_VER) +# if defined(__BORLANDC__) +# include <stdlib.h> // Needed for __errno +# if defined(__WIN32__) && !defined(WIN32) +# if !defined(_WINSOCK2API_) +# define WIN32 // Needed for correct types in winsock2.h +# else // !defined(_WINSOCK2API_) +# error Please define the macro WIN32 in your compiler options +# endif // !defined(_WINSOCK2API_) +# endif // defined(__WIN32__) && !defined(WIN32) +# if !defined(_WSPIAPI_H_) +# define _WSPIAPI_H_ +# define ASIO_WSPIAPI_H_DEFINED +# endif // !defined(_WSPIAPI_H_) +# endif // defined(__BORLANDC__) +# if !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# endif // !defined(WIN32_LEAN_AND_MEAN) +# endif // !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) +# if !defined(ASIO_NO_NOMINMAX) +# if !defined(NOMINMAX) +# define NOMINMAX 1 +# endif // !defined(NOMINMAX) +# endif // !defined(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) +# endif // defined(__CYGWIN__) +# include <winsock2.h> +# include <ws2tcpip.h> +# include <mswsock.h> +# if defined(ASIO_WSPIAPI_H_DEFINED) +# undef _WSPIAPI_H_ +# undef ASIO_WSPIAPI_H_DEFINED +# endif // defined(ASIO_WSPIAPI_H_DEFINED) +# if !defined(ASIO_NO_DEFAULT_LINKED_LIBS) +# if defined(UNDER_CE) +# pragma comment(lib, "ws2.lib") +# elif defined(_MSC_VER) || defined(__BORLANDC__) +# pragma comment(lib, "ws2_32.lib") +# pragma comment(lib, "mswsock.lib") +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +# endif // !defined(ASIO_NO_DEFAULT_LINKED_LIBS) +# include "asio/detail/old_win_sdk_compat.hpp" +#else +# include <sys/ioctl.h> +# include <sys/poll.h> +# include <sys/types.h> +# if defined(__hpux) && !defined(__HP_aCC) +# include <sys/time.h> +# else +# include <sys/select.h> +# endif +# include <sys/socket.h> +# include <sys/uio.h> +# include <sys/un.h> +# include <netinet/in.h> +# include <netinet/tcp.h> +# include <arpa/inet.h> +# include <netdb.h> +# include <net/if.h> +# include <limits.h> +# if defined(__sun) +# include <sys/filio.h> +# include <sys/sockio.h> +# endif +#endif +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef SOCKET socket_type; +const SOCKET invalid_socket = INVALID_SOCKET; +const int socket_error_retval = SOCKET_ERROR; +const int max_addr_v4_str_len = 256; +const int max_addr_v6_str_len = 256; +typedef sockaddr socket_addr_type; +typedef in_addr in4_addr_type; +typedef ip_mreq in4_mreq_type; +typedef sockaddr_in sockaddr_in4_type; +# if defined(ASIO_HAS_OLD_WIN_SDK) +typedef in6_addr_emulation in6_addr_type; +typedef ipv6_mreq_emulation in6_mreq_type; +typedef sockaddr_in6_emulation sockaddr_in6_type; +typedef sockaddr_storage_emulation sockaddr_storage_type; +typedef addrinfo_emulation addrinfo_type; +# else +typedef in6_addr in6_addr_type; +typedef ipv6_mreq in6_mreq_type; +typedef sockaddr_in6 sockaddr_in6_type; +typedef sockaddr_storage sockaddr_storage_type; +typedef addrinfo addrinfo_type; +# endif +typedef unsigned long ioctl_arg_type; +typedef u_long u_long_type; +typedef u_short u_short_type; +const int shutdown_receive = SD_RECEIVE; +const int shutdown_send = SD_SEND; +const int shutdown_both = SD_BOTH; +const int message_peek = MSG_PEEK; +const int message_out_of_band = MSG_OOB; +const int message_do_not_route = MSG_DONTROUTE; +# if defined (_WIN32_WINNT) +const int max_iov_len = 64; +# else +const int max_iov_len = 16; +# endif +#else +typedef int socket_type; +const int invalid_socket = -1; +const int socket_error_retval = -1; +const int max_addr_v4_str_len = INET_ADDRSTRLEN; +#if defined(INET6_ADDRSTRLEN) +const int max_addr_v6_str_len = INET6_ADDRSTRLEN + 1 + IF_NAMESIZE; +#else // defined(INET6_ADDRSTRLEN) +const int max_addr_v6_str_len = 256; +#endif // defined(INET6_ADDRSTRLEN) +typedef sockaddr socket_addr_type; +typedef in_addr in4_addr_type; +# if defined(__hpux) +// HP-UX doesn't provide ip_mreq when _XOPEN_SOURCE_EXTENDED is defined. +struct in4_mreq_type +{ + struct in_addr imr_multiaddr; + struct in_addr imr_interface; +}; +# else +typedef ip_mreq in4_mreq_type; +# endif +typedef sockaddr_in sockaddr_in4_type; +typedef in6_addr in6_addr_type; +typedef ipv6_mreq in6_mreq_type; +typedef sockaddr_in6 sockaddr_in6_type; +typedef sockaddr_storage sockaddr_storage_type; +typedef sockaddr_un sockaddr_un_type; +typedef addrinfo addrinfo_type; +typedef int ioctl_arg_type; +typedef uint32_t u_long_type; +typedef uint16_t u_short_type; +const int shutdown_receive = SHUT_RD; +const int shutdown_send = SHUT_WR; +const int shutdown_both = SHUT_RDWR; +const int message_peek = MSG_PEEK; +const int message_out_of_band = MSG_OOB; +const int message_do_not_route = MSG_DONTROUTE; +# if defined(IOV_MAX) +const int max_iov_len = IOV_MAX; +# else +// POSIX platforms are not required to define IOV_MAX. +const int max_iov_len = 16; +# endif +#endif +const int custom_socket_option_level = 0xA5100000; +const int enable_connection_aborted_option = 1; +const int always_fail_option = 2; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_TYPES_HPP diff --git a/ext/asio/detail/solaris_fenced_block.hpp b/ext/asio/detail/solaris_fenced_block.hpp new file mode 100644 index 0000000000..d337f3b62e --- /dev/null +++ b/ext/asio/detail/solaris_fenced_block.hpp @@ -0,0 +1,57 @@ +// +// 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 ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP +#define ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(__sun) + +#include "asio/detail/push_options.hpp" +#include <atomic.h> +#include "asio/detail/pop_options.hpp" + +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 + +#endif // defined(__sun) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP diff --git a/ext/asio/detail/strand_service.hpp b/ext/asio/detail/strand_service.hpp new file mode 100644 index 0000000000..ea50f412a1 --- /dev/null +++ b/ext/asio/detail/strand_service.hpp @@ -0,0 +1,276 @@ +// +// strand_service.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 ASIO_DETAIL_STRAND_SERVICE_HPP +#define ASIO_DETAIL_STRAND_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/functional/hash.hpp> +#include <boost/scoped_ptr.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/call_stack.hpp" +#include "asio/detail/completion_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/service_base.hpp" + +namespace asio { +namespace detail { + +// Default service implementation for a strand. +class strand_service + : public asio::detail::service_base<strand_service> +{ +private: + struct on_do_complete_exit; + struct on_dispatch_exit; + +public: + + // The underlying implementation of a strand. + class strand_impl + : public operation + { + public: + strand_impl() + : operation(&strand_service::do_complete), + count_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class strand_service; + friend struct on_do_complete_exit; + friend struct on_dispatch_exit; + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The count of handlers in the strand, including the upcall (if any). + std::size_t count_; + + // The handlers waiting on the strand. + op_queue<operation> queue_; + }; + + typedef strand_impl* implementation_type; + + // Construct a new strand service for the specified io_service. + explicit strand_service(asio::io_service& io_service) + : asio::detail::service_base<strand_service>(io_service), + io_service_(asio::use_service<io_service_impl>(io_service)), + mutex_(), + salt_(0) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + op_queue<operation> ops; + + asio::detail::mutex::scoped_lock lock(mutex_); + + for (std::size_t i = 0; i < num_implementations; ++i) + if (strand_impl* impl = implementations_[i].get()) + ops.push(impl->queue_); + } + + // Construct a new strand implementation. + void construct(implementation_type& impl) + { + std::size_t index = boost::hash_value(&impl); + boost::hash_combine(index, salt_++); + index = index % num_implementations; + + asio::detail::mutex::scoped_lock lock(mutex_); + + if (!implementations_[index]) + implementations_[index].reset(new strand_impl); + impl = implementations_[index].get(); + } + + // Destroy a strand implementation. + void destroy(implementation_type& impl) + { + impl = 0; + } + + // Request the io_service to invoke the given handler. + 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)) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + return; + } + + // 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) + { + // 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; + + asio::detail::fenced_block b; + 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. + template <typename Handler> + void post(implementation_type& impl, Handler handler) + { + // 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); + + // 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, + asio::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_; + + ~on_do_complete_exit() + { + impl_->mutex_.lock(); + bool more_handlers = (--impl_->count_ > 0); + impl_->mutex_.unlock(); + + if (more_handlers) + owner_->post_immediate_completion(impl_); + } + }; + + // Helper class to re-post the strand on exit. + struct on_dispatch_exit + { + io_service_impl* io_service_; + strand_impl* impl_; + + ~on_dispatch_exit() + { + 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_; + + // Mutex to protect access to the array of implementations. + asio::detail::mutex mutex_; + + // Number of implementations shared between all strand objects. + enum { num_implementations = 193 }; + + // The head of a linked list of all implementations. + boost::scoped_ptr<strand_impl> implementations_[num_implementations]; + + // Extra value used when hashing to prevent recycled memory locations from + // getting the same strand implementation. + std::size_t salt_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_STRAND_SERVICE_HPP diff --git a/ext/asio/detail/task_io_service.hpp b/ext/asio/detail/task_io_service.hpp new file mode 100644 index 0000000000..eb77c1d8de --- /dev/null +++ b/ext/asio/detail/task_io_service.hpp @@ -0,0 +1,465 @@ +// +// task_io_service.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 ASIO_DETAIL_TASK_IO_SERVICE_HPP +#define ASIO_DETAIL_TASK_IO_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/error_code.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/call_stack.hpp" +#include "asio/detail/completion_handler.hpp" +#include "asio/detail/event.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/task_io_service_fwd.hpp" +#include "asio/detail/task_io_service_operation.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/detail/atomic_count.hpp> +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +template <typename Task> +class task_io_service + : public asio::detail::service_base<task_io_service<Task> > +{ +public: + typedef task_io_service_operation<Task> operation; + + // Constructor. + task_io_service(asio::io_service& io_service) + : asio::detail::service_base<task_io_service<Task> >(io_service), + mutex_(), + task_(0), + task_interrupted_(true), + outstanding_work_(0), + stopped_(false), + shutdown_(false), + first_idle_thread_(0) + { + } + + void init(size_t /*concurrency_hint*/) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + // Destroy handler objects. + while (!op_queue_.empty()) + { + operation* o = op_queue_.front(); + op_queue_.pop(); + if (o != &task_operation_) + o->destroy(); + } + + // Reset to initial state. + task_ = 0; + } + + // Initialise the task, if required. + void init_task() + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_ && !task_) + { + task_ = &use_service<Task>(this->get_io_service()); + op_queue_.push(&task_operation_); + wake_one_thread_and_unlock(lock); + } + } + + // Run the event loop until interrupted or no more work. + size_t run(asio::error_code& ec) + { + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + typename call_stack<task_io_service>::context ctx(this); + + idle_thread_info this_idle_thread; + this_idle_thread.next = 0; + + asio::detail::mutex::scoped_lock lock(mutex_); + + size_t n = 0; + for (; do_one(lock, &this_idle_thread); lock.lock()) + if (n != (std::numeric_limits<size_t>::max)()) + ++n; + return n; + } + + // Run until interrupted or one operation is performed. + size_t run_one(asio::error_code& ec) + { + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + typename call_stack<task_io_service>::context ctx(this); + + idle_thread_info this_idle_thread; + this_idle_thread.next = 0; + + asio::detail::mutex::scoped_lock lock(mutex_); + + return do_one(lock, &this_idle_thread); + } + + // Poll for operations without blocking. + size_t poll(asio::error_code& ec) + { + if (outstanding_work_ == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + typename call_stack<task_io_service>::context ctx(this); + + asio::detail::mutex::scoped_lock lock(mutex_); + + size_t n = 0; + for (; do_one(lock, 0); lock.lock()) + if (n != (std::numeric_limits<size_t>::max)()) + ++n; + return n; + } + + // Poll for one operation without blocking. + size_t poll_one(asio::error_code& ec) + { + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + typename call_stack<task_io_service>::context ctx(this); + + asio::detail::mutex::scoped_lock lock(mutex_); + + return do_one(lock, 0); + } + + // Interrupt the event processing loop. + void stop() + { + asio::detail::mutex::scoped_lock lock(mutex_); + stop_all_threads(lock); + } + + // Reset in preparation for a subsequent run invocation. + void reset() + { + asio::detail::mutex::scoped_lock lock(mutex_); + 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)) + { + asio::detail::fenced_block b; + 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. + 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); + + 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) + { + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue_.push(op); + wake_one_thread_and_unlock(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()) + { + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue_.push(ops); + wake_one_thread_and_unlock(lock); + } + } + +private: + struct idle_thread_info; + + size_t do_one(asio::detail::mutex::scoped_lock& lock, + idle_thread_info* this_idle_thread) + { + bool polling = !this_idle_thread; + bool task_has_run = false; + while (!stopped_) + { + if (!op_queue_.empty()) + { + // Prepare to execute first handler from queue. + operation* o = op_queue_.front(); + op_queue_.pop(); + bool more_handlers = (!op_queue_.empty()); + + if (o == &task_operation_) + { + 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; + op_queue_.push(&task_operation_); + return 0; + } + task_has_run = true; + + if (!more_handlers || !wake_one_idle_thread_and_unlock(lock)) + lock.unlock(); + + 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 + { + 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; + + // Complete the operation. May throw an exception. + o->complete(*this); // deletes the operation object + + return 1; + } + } + else if (this_idle_thread) + { + // Nothing to run right now, so just wait for work to do. + this_idle_thread->next = first_idle_thread_; + first_idle_thread_ = this_idle_thread; + this_idle_thread->wakeup_event.clear(lock); + this_idle_thread->wakeup_event.wait(lock); + } + else + { + return 0; + } + } + + return 0; + } + + // Stop the task and all idle threads. + void stop_all_threads( + asio::detail::mutex::scoped_lock& lock) + { + stopped_ = true; + + 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; + task_->interrupt(); + } + } + + // 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( + asio::detail::mutex::scoped_lock& lock) + { + if (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_and_unlock(lock); + return true; + } + return false; + } + + // Wake a single idle thread, or the task, and always unlock the mutex. + void wake_one_thread_and_unlock( + asio::detail::mutex::scoped_lock& lock) + { + if (!wake_one_idle_thread_and_unlock(lock)) + { + if (!task_interrupted_ && task_) + { + task_interrupted_ = true; + task_->interrupt(); + } + lock.unlock(); + } + } + + // Helper class to perform task-related operations on block exit. + struct task_cleanup; + friend struct task_cleanup; + struct task_cleanup + { + ~task_cleanup() + { + // 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_); + } + + task_io_service* task_io_service_; + asio::detail::mutex::scoped_lock* lock_; + op_queue<operation>* ops_; + }; + + // Helper class to call work_finished() on block exit. + struct work_finished_on_block_exit + { + ~work_finished_on_block_exit() + { + task_io_service_->work_finished(); + } + + task_io_service* task_io_service_; + }; + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The task to be run by this service. + Task* task_; + + // Operation object to represent the position of the task in the queue. + struct task_operation : public operation + { + task_operation() : operation(0) {} + } task_operation_; + + // Whether the task has been interrupted. + bool task_interrupted_; + + // The count of unfinished work. + boost::detail::atomic_count outstanding_work_; + + // The queue of handlers that are ready to be delivered. + op_queue<operation> op_queue_; + + // Flag to indicate that the dispatcher has been stopped. + bool stopped_; + + // Flag to indicate that the dispatcher has been shut down. + bool shutdown_; + + // Structure containing information about an idle thread. + struct idle_thread_info + { + event wakeup_event; + idle_thread_info* next; + }; + + // The threads that are currently idle. + idle_thread_info* first_idle_thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TASK_IO_SERVICE_HPP diff --git a/ext/asio/detail/task_io_service_fwd.hpp b/ext/asio/detail/task_io_service_fwd.hpp new file mode 100644 index 0000000000..5b18d1d700 --- /dev/null +++ b/ext/asio/detail/task_io_service_fwd.hpp @@ -0,0 +1,31 @@ +// +// task_io_service_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 ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP +#define ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template <typename Task> +class task_io_service; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP diff --git a/ext/asio/detail/task_io_service_operation.hpp b/ext/asio/detail/task_io_service_operation.hpp new file mode 100644 index 0000000000..6776f6992f --- /dev/null +++ b/ext/asio/detail/task_io_service_operation.hpp @@ -0,0 +1,69 @@ +// +// 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 ASIO_DETAIL_TASK_IO_SERVICE_OPERATION_HPP +#define ASIO_DETAIL_TASK_IO_SERVICE_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/error_code.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/task_io_service_fwd.hpp" + +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, asio::error_code(), 0); + } + + void destroy() + { + func_(0, this, asio::error_code(), 0); + } + +protected: + typedef void (*func_type)(task_io_service<Task>*, + task_io_service_operation*, asio::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 + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TASK_IO_SERVICE_OPERATION_HPP diff --git a/ext/asio/detail/thread.hpp b/ext/asio/detail/thread.hpp new file mode 100644 index 0000000000..3c9280bcd3 --- /dev/null +++ b/ext/asio/detail/thread.hpp @@ -0,0 +1,58 @@ +// +// thread.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 ASIO_DETAIL_THREAD_HPP +#define ASIO_DETAIL_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +# include "asio/detail/null_thread.hpp" +#elif defined(BOOST_WINDOWS) +# if defined(UNDER_CE) +# include "asio/detail/wince_thread.hpp" +# else +# include "asio/detail/win_thread.hpp" +# endif +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_thread.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +typedef null_thread thread; +#elif defined(BOOST_WINDOWS) +# if defined(UNDER_CE) +typedef wince_thread thread; +# else +typedef win_thread thread; +# endif +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_thread thread; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_THREAD_HPP diff --git a/ext/asio/detail/throw_error.hpp b/ext/asio/detail/throw_error.hpp new file mode 100644 index 0000000000..51d6e48f4f --- /dev/null +++ b/ext/asio/detail/throw_error.hpp @@ -0,0 +1,44 @@ +// +// throw_error.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 ASIO_DETAIL_THROW_ERROR_HPP +#define ASIO_DETAIL_THROW_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/error_code.hpp" +#include "asio/system_error.hpp" + +namespace asio { +namespace detail { + +inline void throw_error(const asio::error_code& err) +{ + if (err) + { + asio::system_error e(err); + boost::throw_exception(e); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_THROW_ERROR_HPP diff --git a/ext/asio/detail/timer_op.hpp b/ext/asio/detail/timer_op.hpp new file mode 100644 index 0000000000..bf3c3ae593 --- /dev/null +++ b/ext/asio/detail/timer_op.hpp @@ -0,0 +1,44 @@ +// +// 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 ASIO_DETAIL_TIMER_OP_HPP +#define ASIO_DETAIL_TIMER_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/operation.hpp" + +namespace asio { +namespace detail { + +class timer_op + : public operation +{ +public: + // The error code to be passed to the completion handler. + asio::error_code ec_; + +protected: + timer_op(func_type func) + : operation(func) + { + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_OP_HPP diff --git a/ext/asio/detail/timer_queue.hpp b/ext/asio/detail/timer_queue.hpp new file mode 100644 index 0000000000..2e4d2d5d97 --- /dev/null +++ b/ext/asio/detail/timer_queue.hpp @@ -0,0 +1,276 @@ +// +// timer_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 ASIO_DETAIL_TIMER_QUEUE_HPP +#define ASIO_DETAIL_TIMER_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <cstddef> +#include <memory> +#include <vector> +#include <boost/config.hpp> +#include <boost/limits.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/timer_op.hpp" +#include "asio/detail/timer_queue_base.hpp" + +namespace asio { +namespace detail { + +template <typename Time_Traits> +class timer_queue + : public timer_queue_base +{ +public: + // The time type. + typedef typename Time_Traits::time_type time_type; + + // The duration type. + typedef typename Time_Traits::duration_type duration_type; + + // Constructor. + timer_queue() + : timers_(), + 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. + 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); + + // Insert the new timer into the hash. + 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, timer())); + result.first->second.op_queue_.push(op); + if (result.second) + { + // 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); + } + + return (heap_[0] == &result.first->second); + } + + // Whether there are no timers in the queue. + virtual bool empty() const + { + return heap_.empty(); + } + + // Get the time for the timer that is earliest in the queue. + virtual long wait_duration_msec(long max_duration) const + { + 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::milliseconds(max_duration)) + duration = boost::posix_time::milliseconds(max_duration); + else if (duration < boost::posix_time::milliseconds(0)) + duration = boost::posix_time::milliseconds(0); + + return duration.total_milliseconds(); + } + + // Get the time for the timer that is earliest in the queue. + virtual long wait_duration_usec(long max_duration) const + { + 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(); + } + + // Dequeue all timers not later than the current time. + virtual void get_ready_timers(op_queue<operation>& ops) + { + const time_type now = Time_Traits::now(); + while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0]->time_)) + { + timer* t = heap_[0]; + ops.push(t->op_queue_); + remove_timer(t); + } + } + + // Dequeue all timers. + virtual void get_all_timers(op_queue<operation>& ops) + { + typename hash_map<void*, timer>::iterator i = timers_.begin(); + typename hash_map<void*, timer>::iterator end = timers_.end(); + while (i != end) + { + ops.push(i->second.op_queue_); + typename hash_map<void*, timer>::iterator old_i = i++; + timers_.erase(old_i); + } + + heap_.clear(); + timers_.clear(); + } + + // Cancel and dequeue the timers with the given token. + std::size_t cancel_timer(void* timer_token, op_queue<operation>& ops) + { + 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_ = asio::error::operation_aborted; + it->second.op_queue_.pop(); + ops.push(op); + ++num_cancelled; + } + remove_timer(&it->second); + } + return num_cancelled; + } + +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 operations waiting on the timer. + op_queue<timer_op> op_queue_; + + // The index of the timer in the heap. + size_t heap_index_; + + // The token associated with the timer. + void* token_; + }; + + // Move the item at the given index up the heap to its correct position. + void up_heap(size_t index) + { + size_t parent = (index - 1) / 2; + while (index > 0 + && Time_Traits::less_than(heap_[index]->time_, heap_[parent]->time_)) + { + swap_heap(index, parent); + index = parent; + parent = (index - 1) / 2; + } + } + + // Move the item at the given index down the heap to its correct position. + void down_heap(size_t index) + { + size_t child = index * 2 + 1; + while (child < heap_.size()) + { + size_t min_child = (child + 1 == heap_.size() + || Time_Traits::less_than( + heap_[child]->time_, heap_[child + 1]->time_)) + ? child : child + 1; + if (Time_Traits::less_than(heap_[index]->time_, heap_[min_child]->time_)) + break; + swap_heap(index, min_child); + index = min_child; + child = index * 2 + 1; + } + } + + // Swap two entries in the heap. + void swap_heap(size_t index1, size_t index2) + { + timer* tmp = heap_[index1]; + heap_[index1] = heap_[index2]; + heap_[index2] = tmp; + heap_[index1]->heap_index_ = index1; + heap_[index2]->heap_index_ = index2; + } + + // Remove a timer from the heap and list of timers. + void remove_timer(timer* t) + { + // Remove the timer from the heap. + size_t index = t->heap_index_; + if (!heap_.empty() && index < heap_.size()) + { + if (index == heap_.size() - 1) + { + heap_.pop_back(); + } + else + { + swap_heap(index, heap_.size() - 1); + heap_.pop_back(); + size_t parent = (index - 1) / 2; + if (index > 0 && Time_Traits::less_than( + heap_[index]->time_, heap_[parent]->time_)) + up_heap(index); + else + down_heap(index); + } + } + + // Remove the timer from the hash. + typedef typename hash_map<void*, timer>::iterator iterator; + iterator it = timers_.find(t->token_); + if (it != timers_.end()) + timers_.erase(it); + } + + // A hash of timer token to linked lists of timers. + hash_map<void*, timer> timers_; + + // The heap of timers, with the earliest timer at the front. + std::vector<timer*> heap_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_QUEUE_HPP diff --git a/ext/asio/detail/timer_queue_base.hpp b/ext/asio/detail/timer_queue_base.hpp new file mode 100644 index 0000000000..f978667535 --- /dev/null +++ b/ext/asio/detail/timer_queue_base.hpp @@ -0,0 +1,64 @@ +// +// timer_queue_base.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 ASIO_DETAIL_TIMER_QUEUE_BASE_HPP +#define ASIO_DETAIL_TIMER_QUEUE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/operation.hpp" + +namespace asio { +namespace detail { + +class timer_queue_base + : private noncopyable +{ +public: + // Constructor. + timer_queue_base() : next_(0) {} + + // Destructor. + virtual ~timer_queue_base() {} + + // Whether there are no timers in the queue. + virtual bool empty() const = 0; + + // Get the time to wait until the next timer. + 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; + + // Dequeue all ready timers. + virtual void get_ready_timers(op_queue<operation>& ops) = 0; + + // Dequeue all timers. + virtual void get_all_timers(op_queue<operation>& ops) = 0; + +private: + friend class timer_queue_set; + + // Next timer queue in the set. + timer_queue_base* next_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_QUEUE_BASE_HPP diff --git a/ext/asio/detail/timer_queue_fwd.hpp b/ext/asio/detail/timer_queue_fwd.hpp new file mode 100644 index 0000000000..53172448bc --- /dev/null +++ b/ext/asio/detail/timer_queue_fwd.hpp @@ -0,0 +1,31 @@ +// +// 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 ASIO_DETAIL_TIMER_QUEUE_FWD_HPP +#define ASIO_DETAIL_TIMER_QUEUE_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template <typename Time_Traits> +class timer_queue; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_QUEUE_FWD_HPP diff --git a/ext/asio/detail/timer_queue_set.hpp b/ext/asio/detail/timer_queue_set.hpp new file mode 100644 index 0000000000..6860867074 --- /dev/null +++ b/ext/asio/detail/timer_queue_set.hpp @@ -0,0 +1,115 @@ +// +// 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 ASIO_DETAIL_TIMER_QUEUE_SET_HPP +#define ASIO_DETAIL_TIMER_QUEUE_SET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/timer_queue_base.hpp" + +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 + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_QUEUE_SET_HPP diff --git a/ext/asio/detail/timer_scheduler.hpp b/ext/asio/detail/timer_scheduler.hpp new file mode 100644 index 0000000000..6822d3f793 --- /dev/null +++ b/ext/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 ASIO_DETAIL_TIMER_SCHEDULER_HPP +#define ASIO_DETAIL_TIMER_SCHEDULER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/timer_scheduler_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_service.hpp" +#elif defined(ASIO_HAS_EPOLL) +# include "asio/detail/epoll_reactor.hpp" +#elif defined(ASIO_HAS_KQUEUE) +# include "asio/detail/kqueue_reactor.hpp" +#elif defined(ASIO_HAS_DEV_POLL) +# include "asio/detail/dev_poll_reactor.hpp" +#else +# include "asio/detail/select_reactor.hpp" +#endif + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_SCHEDULER_HPP diff --git a/ext/asio/detail/timer_scheduler_fwd.hpp b/ext/asio/detail/timer_scheduler_fwd.hpp new file mode 100644 index 0000000000..d766a87fb2 --- /dev/null +++ b/ext/asio/detail/timer_scheduler_fwd.hpp @@ -0,0 +1,46 @@ +// +// 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 ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP +#define ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/dev_poll_reactor_fwd.hpp" +#include "asio/detail/epoll_reactor_fwd.hpp" +#include "asio/detail/kqueue_reactor_fwd.hpp" +#include "asio/detail/select_reactor_fwd.hpp" +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +namespace asio { +namespace detail { + +#if defined(ASIO_HAS_IOCP) +typedef win_iocp_io_service timer_scheduler; +#elif defined(ASIO_HAS_EPOLL) +typedef epoll_reactor timer_scheduler; +#elif defined(ASIO_HAS_KQUEUE) +typedef kqueue_reactor timer_scheduler; +#elif defined(ASIO_HAS_DEV_POLL) +typedef dev_poll_reactor timer_scheduler; +#else +typedef select_reactor<false> timer_scheduler; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP diff --git a/ext/asio/detail/tss_ptr.hpp b/ext/asio/detail/tss_ptr.hpp new file mode 100644 index 0000000000..ac67d9f0ce --- /dev/null +++ b/ext/asio/detail/tss_ptr.hpp @@ -0,0 +1,65 @@ +// +// tss_ptr.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 ASIO_DETAIL_TSS_PTR_HPP +#define ASIO_DETAIL_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +# include "asio/detail/null_tss_ptr.hpp" +#elif defined(BOOST_WINDOWS) +# include "asio/detail/win_tss_ptr.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_tss_ptr.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +template <typename T> +class tss_ptr +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + : public null_tss_ptr<T> +#elif defined(BOOST_WINDOWS) + : public win_tss_ptr<T> +#elif defined(BOOST_HAS_PTHREADS) + : public posix_tss_ptr<T> +#endif +{ +public: + void operator=(T* value) + { +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + null_tss_ptr<T>::operator=(value); +#elif defined(BOOST_WINDOWS) + win_tss_ptr<T>::operator=(value); +#elif defined(BOOST_HAS_PTHREADS) + posix_tss_ptr<T>::operator=(value); +#endif + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TSS_PTR_HPP diff --git a/ext/asio/detail/win_event.hpp b/ext/asio/detail/win_event.hpp new file mode 100644 index 0000000000..cabb2c389c --- /dev/null +++ b/ext/asio/detail/win_event.hpp @@ -0,0 +1,112 @@ +// +// win_event.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 ASIO_DETAIL_WIN_EVENT_HPP +#define ASIO_DETAIL_WIN_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/assert.hpp> +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +class win_event + : private noncopyable +{ +public: + // Constructor. + win_event() + : event_(::CreateEvent(0, true, false, 0)) + { + if (!event_) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, + asio::error::get_system_category()), + "event"); + boost::throw_exception(e); + } + } + + // Destructor. + ~win_event() + { + ::CloseHandle(event_); + } + + // Signal the event. + template <typename Lock> + void signal(Lock& lock) + { + BOOST_ASSERT(lock.locked()); + (void)lock; + ::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) + { + BOOST_ASSERT(lock.locked()); + (void)lock; + ::ResetEvent(event_); + } + + // Wait for the event to become signalled. + template <typename Lock> + void wait(Lock& lock) + { + BOOST_ASSERT(lock.locked()); + lock.unlock(); + ::WaitForSingleObject(event_, INFINITE); + lock.lock(); + } + +private: + HANDLE event_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_EVENT_HPP diff --git a/ext/asio/detail/win_fd_set_adapter.hpp b/ext/asio/detail/win_fd_set_adapter.hpp new file mode 100644 index 0000000000..012a10ff76 --- /dev/null +++ b/ext/asio/detail/win_fd_set_adapter.hpp @@ -0,0 +1,88 @@ +// +// win_fd_set_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 ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class win_fd_set_adapter +{ +public: + enum { win_fd_set_size = 1024 }; + + win_fd_set_adapter() + : max_descriptor_(invalid_socket) + { + fd_set_.fd_count = 0; + } + + bool set(socket_type descriptor) + { + for (u_int i = 0; i < fd_set_.fd_count; ++i) + if (fd_set_.fd_array[i] == descriptor) + return true; + if (fd_set_.fd_count < win_fd_set_size) + { + fd_set_.fd_array[fd_set_.fd_count++] = descriptor; + return true; + } + return false; + } + + bool is_set(socket_type descriptor) const + { + return !!__WSAFDIsSet(descriptor, + const_cast<fd_set*>(reinterpret_cast<const fd_set*>(&fd_set_))); + } + + operator fd_set*() + { + return reinterpret_cast<fd_set*>(&fd_set_); + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + +private: + // This structure is defined to be compatible with the Windows API fd_set + // structure, but without being dependent on the value of FD_SETSIZE. + struct win_fd_set + { + u_int fd_count; + SOCKET fd_array[win_fd_set_size]; + }; + + win_fd_set fd_set_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP diff --git a/ext/asio/detail/win_fenced_block.hpp b/ext/asio/detail/win_fenced_block.hpp new file mode 100644 index 0000000000..6338488f91 --- /dev/null +++ b/ext/asio/detail/win_fenced_block.hpp @@ -0,0 +1,75 @@ +// +// 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 ASIO_DETAIL_WIN_FENCED_BLOCK_HPP +#define ASIO_DETAIL_WIN_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) && !defined(UNDER_CE) + +#include "asio/detail/socket_types.hpp" + +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 + +#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_FENCED_BLOCK_HPP diff --git a/ext/asio/detail/win_iocp_handle_service.hpp b/ext/asio/detail/win_iocp_handle_service.hpp new file mode 100644 index 0000000000..bfc159ce72 --- /dev/null +++ b/ext/asio/detail/win_iocp_handle_service.hpp @@ -0,0 +1,735 @@ +// +// win_iocp_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.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 ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" +#include <boost/cstdint.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/win_iocp_io_service.hpp" + +namespace asio { +namespace detail { + +class win_iocp_handle_service +{ +public: + // The native type of a stream handle. + typedef HANDLE native_type; + + // The implementation type of the stream handle. + class implementation_type + { + public: + // Default constructor. + implementation_type() + : handle_(INVALID_HANDLE_VALUE), + safe_cancellation_thread_id_(0), + next_(0), + prev_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class win_iocp_handle_service; + + // The native stream handle representation. + native_type handle_; + + // The ID of the thread from which it is safe to cancel asynchronous + // operations. 0 means no asynchronous operations have been started yet. + // ~0 means asynchronous operations have been started from more than one + // thread, and cancellation is not supported for the handle. + DWORD safe_cancellation_thread_id_; + + // Pointers to adjacent handle implementations in linked list. + implementation_type* next_; + implementation_type* prev_; + }; + + win_iocp_handle_service(asio::io_service& io_service) + : iocp_service_(asio::use_service<win_iocp_io_service>(io_service)), + mutex_(), + impl_list_(0) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + // Close all implementations, causing all operations to complete. + asio::detail::mutex::scoped_lock lock(mutex_); + implementation_type* impl = impl_list_; + while (impl) + { + close_for_destruction(*impl); + impl = impl->next_; + } + } + + // Construct a new handle implementation. + void construct(implementation_type& impl) + { + impl.handle_ = INVALID_HANDLE_VALUE; + impl.safe_cancellation_thread_id_ = 0; + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; + } + + // Destroy a handle implementation. + void destroy(implementation_type& impl) + { + close_for_destruction(impl); + + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + } + + // Assign a native handle to a handle implementation. + asio::error_code assign(implementation_type& impl, + const native_type& native_handle, asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (iocp_service_.register_handle(native_handle, ec)) + return ec; + + impl.handle_ = native_handle; + ec = asio::error_code(); + return ec; + } + + // Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return impl.handle_ != INVALID_HANDLE_VALUE; + } + + // Destroy a handle implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + if (is_open(impl)) + { + if (!::CloseHandle(impl.handle_)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + impl.handle_ = INVALID_HANDLE_VALUE; + impl.safe_cancellation_thread_id_ = 0; + } + + ec = asio::error_code(); + return ec; + } + + // Get the native handle representation. + native_type native(const implementation_type& impl) const + { + return impl.handle_; + } + + // Cancel all operations associated with the handle. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + } + else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( + ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) + { + // The version of Windows supports cancellation from any thread. + typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); + cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr; + if (!cancel_io_ex(impl.handle_, 0)) + { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_NOT_FOUND) + { + // ERROR_NOT_FOUND means that there were no operations to be + // cancelled. We swallow this error to match the behaviour on other + // platforms. + ec = asio::error_code(); + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + } + else + { + ec = asio::error_code(); + } + } + else if (impl.safe_cancellation_thread_id_ == 0) + { + // No operations have been started, so there's nothing to cancel. + ec = asio::error_code(); + } + else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) + { + // Asynchronous operations have been started from the current thread only, + // so it is safe to try to cancel them using CancelIo. + if (!::CancelIo(impl.handle_)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + } + else + { + // Asynchronous operations have been started from more than one thread, + // so cancellation is not safe. + ec = asio::error::operation_not_supported; + } + + return ec; + } + + class overlapped_wrapper + : public OVERLAPPED + { + public: + explicit overlapped_wrapper(asio::error_code& ec) + { + Internal = 0; + InternalHigh = 0; + Offset = 0; + OffsetHigh = 0; + + // Create a non-signalled manual-reset event, for GetOverlappedResult. + hEvent = ::CreateEvent(0, TRUE, FALSE, 0); + if (hEvent) + { + // As documented in GetQueuedCompletionStatus, setting the low order + // bit of this event prevents our synchronous writes from being treated + // as completion port events. + *reinterpret_cast<DWORD_PTR*>(&hEvent) |= 1; + } + else + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + } + + ~overlapped_wrapper() + { + if (hEvent) + { + ::CloseHandle(hEvent); + } + } + }; + + // Write the given data. Returns the number of bytes written. + template <typename ConstBufferSequence> + size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return write_some_at(impl, 0, buffers, ec); + } + + // Write the given data at the specified offset. Returns the number of bytes + // written. + template <typename ConstBufferSequence> + size_t write_some_at(implementation_type& impl, boost::uint64_t offset, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + asio::const_buffer buffer = + buffer_sequence_adapter<asio::const_buffer, + ConstBufferSequence>::first(buffers); + + // A request to write 0 bytes on a handle is a no-op. + if (asio::buffer_size(buffer) == 0) + { + ec = asio::error_code(); + return 0; + } + + overlapped_wrapper overlapped(ec); + if (ec) + { + return 0; + } + + // Write the data. + overlapped.Offset = offset & 0xFFFFFFFF; + overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::WriteFile(impl.handle_, + asio::buffer_cast<LPCVOID>(buffer), + static_cast<DWORD>(asio::buffer_size(buffer)), 0, &overlapped); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error != ERROR_IO_PENDING) + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + } + + // Wait for the operation to complete. + DWORD bytes_transferred = 0; + ok = ::GetOverlappedResult(impl.handle_, + &overlapped, &bytes_transferred, TRUE); + if (!ok) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; + } + + template <typename ConstBufferSequence, typename Handler> + class write_op : public operation + { + public: + write_op(const ConstBufferSequence& buffers, Handler handler) + : operation(&write_op::do_complete), + buffers_(buffers), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation 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); + + // Make the upcall if required. + if (owner) + { +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + buffer_sequence_adapter<asio::const_buffer, + ConstBufferSequence>::validate(o->buffers_); +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // 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, asio::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + ConstBufferSequence buffers_; + Handler handler_; + }; + + // Start an asynchronous write. The data being written 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) + { + async_write_some_at(impl, 0, buffers, handler); + } + + // Start an asynchronous write at a specified offset. The data being written + // must be valid for the lifetime of the asynchronous operation. + template <typename ConstBufferSequence, typename Handler> + void async_write_some_at(implementation_type& impl, boost::uint64_t offset, + 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, buffers, handler); + + start_write_op(impl, offset, + buffer_sequence_adapter<asio::const_buffer, + ConstBufferSequence>::first(buffers), ptr.get()); + ptr.release(); + } + + // Read some data. Returns the number of bytes received. + template <typename MutableBufferSequence> + size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return read_some_at(impl, 0, buffers, ec); + } + + // Read some data at a specified offset. Returns the number of bytes received. + template <typename MutableBufferSequence> + size_t read_some_at(implementation_type& impl, boost::uint64_t offset, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + asio::mutable_buffer buffer = + buffer_sequence_adapter<asio::mutable_buffer, + MutableBufferSequence>::first(buffers); + + // A request to read 0 bytes on a stream handle is a no-op. + if (asio::buffer_size(buffer) == 0) + { + ec = asio::error_code(); + return 0; + } + + overlapped_wrapper overlapped(ec); + if (ec) + { + return 0; + } + + // Read some data. + overlapped.Offset = offset & 0xFFFFFFFF; + overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::ReadFile(impl.handle_, + asio::buffer_cast<LPVOID>(buffer), + static_cast<DWORD>(asio::buffer_size(buffer)), 0, &overlapped); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA) + { + if (last_error == ERROR_HANDLE_EOF) + { + ec = asio::error::eof; + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + return 0; + } + } + + // Wait for the operation to complete. + DWORD bytes_transferred = 0; + ok = ::GetOverlappedResult(impl.handle_, + &overlapped, &bytes_transferred, TRUE); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_HANDLE_EOF) + { + ec = asio::error::eof; + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; + } + + template <typename MutableBufferSequence, typename Handler> + class read_op : public operation + { + public: + read_op(const MutableBufferSequence& buffers, Handler handler) + : operation(&read_op::do_complete), + buffers_(buffers), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation 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); + + // Make the upcall if required. + if (owner) + { +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + buffer_sequence_adapter<asio::mutable_buffer, + MutableBufferSequence>::validate(o->buffers_); +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_HANDLE_EOF) + { + ec = asio::error::eof; + } + + // 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, asio::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + MutableBufferSequence buffers_; + Handler handler_; + }; + + // Start an asynchronous read. The buffer for the data being received 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) + { + async_read_some_at(impl, 0, buffers, handler); + } + + // Start an asynchronous read at a specified offset. The buffer for the data + // being received must be valid for the lifetime of the asynchronous + // operation. + template <typename MutableBufferSequence, typename Handler> + void async_read_some_at(implementation_type& impl, boost::uint64_t offset, + 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, buffers, handler); + + start_read_op(impl, offset, + buffer_sequence_adapter<asio::mutable_buffer, + MutableBufferSequence>::first(buffers), ptr.get()); + ptr.release(); + } + +private: + // Prevent the use of the null_buffers type with this service. + size_t write_some(implementation_type& impl, + const null_buffers& buffers, asio::error_code& ec); + size_t write_some_at(implementation_type& impl, boost::uint64_t offset, + const null_buffers& buffers, asio::error_code& ec); + template <typename Handler> + void async_write_some(implementation_type& impl, + const null_buffers& buffers, Handler handler); + template <typename Handler> + void async_write_some_at(implementation_type& impl, boost::uint64_t offset, + const null_buffers& buffers, Handler handler); + size_t read_some(implementation_type& impl, + const null_buffers& buffers, asio::error_code& ec); + size_t read_some_at(implementation_type& impl, boost::uint64_t offset, + const null_buffers& buffers, asio::error_code& ec); + template <typename Handler> + void async_read_some(implementation_type& impl, + const null_buffers& buffers, Handler handler); + template <typename Handler> + 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 asio::const_buffer& buffer, operation* op) + { + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + { + iocp_service_.on_completion(op, asio::error::bad_descriptor); + } + else if (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_, + asio::buffer_cast<LPCVOID>(buffer), + static_cast<DWORD>(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 asio::mutable_buffer& buffer, operation* op) + { + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + { + iocp_service_.on_completion(op, asio::error::bad_descriptor); + } + else if (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_, + asio::buffer_cast<LPVOID>(buffer), + static_cast<DWORD>(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(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(ASIO_ENABLE_CANCELIO) + (void)impl; +#endif // defined(ASIO_ENABLE_CANCELIO) + } + + // Helper function to close a handle when the associated object is being + // destroyed. + void close_for_destruction(implementation_type& impl) + { + if (is_open(impl)) + { + ::CloseHandle(impl.handle_); + impl.handle_ = INVALID_HANDLE_VALUE; + impl.safe_cancellation_thread_id_ = 0; + } + } + + // The IOCP service used for running asynchronous operations and dispatching + // handlers. + win_iocp_io_service& iocp_service_; + + // Mutex to protect access to the linked list of implementations. + asio::detail::mutex mutex_; + + // The head of a linked list of all implementations. + implementation_type* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP diff --git a/ext/asio/detail/win_iocp_io_service.hpp b/ext/asio/detail/win_iocp_io_service.hpp new file mode 100644 index 0000000000..fd899c1146 --- /dev/null +++ b/ext/asio/detail/win_iocp_io_service.hpp @@ -0,0 +1,686 @@ +// +// win_iocp_io_service.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 ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" +#include <boost/limits.hpp> +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/call_stack.hpp" +#include "asio/detail/completion_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/op_queue.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_op.hpp" +#include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/timer_queue_fwd.hpp" +#include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/win_iocp_operation.hpp" + +namespace asio { +namespace detail { + +class timer_op; + +class win_iocp_io_service + : public asio::detail::service_base<win_iocp_io_service> +{ +public: + typedef win_iocp_operation operation; + + // Constructor. + win_iocp_io_service(asio::io_service& io_service) + : asio::detail::service_base<win_iocp_io_service>(io_service), + iocp_(), + outstanding_work_(0), + stopped_(0), + shutdown_(0), + timer_thread_(0), + timer_interrupt_issued_(false) + { + } + + void init(size_t concurrency_hint) + { + iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, + static_cast<DWORD>((std::min<size_t>)(concurrency_hint, DWORD(~0)))); + if (!iocp_.handle) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, + asio::error::get_system_category()), + "iocp"); + boost::throw_exception(e); + } + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + ::InterlockedExchange(&shutdown_, 1); + + while (::InterlockedExchangeAdd(&outstanding_work_, 0) > 0) + { + 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(); + } + } + } + } + + // Initialise the task. Nothing to do here. + void init_task() + { + } + + // Register a handle with the IO completion port. + asio::error_code register_handle( + HANDLE handle, asio::error_code& ec) + { + if (::CreateIoCompletionPort(handle, iocp_.handle, 0, 0) == 0) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + return ec; + } + + // Run the event loop until stopped or no more work. + size_t run(asio::error_code& ec) + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + call_stack<win_iocp_io_service>::context ctx(this); + + size_t n = 0; + while (do_one(true, ec)) + if (n != (std::numeric_limits<size_t>::max)()) + ++n; + return n; + } + + // Run until stopped or one operation is performed. + size_t run_one(asio::error_code& ec) + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + call_stack<win_iocp_io_service>::context ctx(this); + + return do_one(true, ec); + } + + // Poll for operations without blocking. + size_t poll(asio::error_code& ec) + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + call_stack<win_iocp_io_service>::context ctx(this); + + size_t n = 0; + while (do_one(false, ec)) + if (n != (std::numeric_limits<size_t>::max)()) + ++n; + return n; + } + + // Poll for one operation without blocking. + size_t poll_one(asio::error_code& ec) + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + call_stack<win_iocp_io_service>::context ctx(this); + + return do_one(false, ec); + } + + // Stop the event processing loop. + void stop() + { + if (::InterlockedExchange(&stopped_, 1) == 0) + { + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, + asio::error::get_system_category()), + "pqcs"); + boost::throw_exception(e); + } + } + } + + // Reset in preparation for a subsequent run invocation. + void reset() + { + ::InterlockedExchange(&stopped_, 0); + } + + // Notify that some work has started. + void work_started() + { + ::InterlockedIncrement(&outstanding_work_); + } + + // Notify that some work has finished. + void work_finished() + { + if (::InterlockedDecrement(&outstanding_work_) == 0) + stop(); + } + + // Request invocation of the given handler. + template <typename Handler> + void dispatch(Handler handler) + { + if (call_stack<win_iocp_io_service>::contains(this)) + { + asio::detail::fenced_block b; + 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. + 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); + + 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. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + asio::detail::mutex::scoped_lock lock(timer_mutex_); + completed_ops_.push(op); + } + } + + // 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. + asio::detail::mutex::scoped_lock lock(timer_mutex_); + completed_ops_.push(op); + completed_ops_.push(ops); + } + } + } + + // 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. + 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 = 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, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + 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 asio::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 = 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. + asio::detail::mutex::scoped_lock lock(timer_mutex_); + completed_ops_.push(op); + } + } + + // Add a new timer queue to the service. + template <typename Time_Traits> + void add_timer_queue(timer_queue<Time_Traits>& timer_queue) + { + asio::detail::mutex::scoped_lock lock(timer_mutex_); + timer_queues_.insert(&timer_queue); + } + + // Remove a timer queue from the service. + template <typename Time_Traits> + void remove_timer_queue(timer_queue<Time_Traits>& timer_queue) + { + asio::detail::mutex::scoped_lock lock(timer_mutex_); + timer_queues_.erase(&timer_queue); + } + + // 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, timer_op* op, void* token) + { + // If the service has been shut down we silently discard the timer. + if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) + return; + + asio::detail::mutex::scoped_lock lock(timer_mutex_); + bool interrupt = timer_queue.enqueue_timer(time, op, token); + work_started(); + if (interrupt && !timer_interrupt_issued_) + { + timer_interrupt_issued_ = true; + lock.unlock(); + ::PostQueuedCompletionStatus(iocp_.handle, + 0, steal_timer_dispatching, 0); + } + } + + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + template <typename Time_Traits> + std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token) + { + // If the service has been shut down we silently ignore the cancellation. + if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) + return 0; + + asio::detail::mutex::scoped_lock lock(timer_mutex_); + 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; + lock.unlock(); + ::PostQueuedCompletionStatus(iocp_.handle, + 0, steal_timer_dispatching, 0); + } + return n; + } + +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). + size_t do_one(bool block, asio::error_code& ec) + { + long this_thread_id = static_cast<long>(::GetCurrentThreadId()); + + for (;;) + { + // Try to acquire responsibility for dispatching timers. + bool dispatching_timers = (::InterlockedCompareExchange( + &timer_thread_, this_thread_id, 0) == 0); + + // Calculate timeout for GetQueuedCompletionStatus call. + DWORD timeout = max_timeout; + if (dispatching_timers) + { + asio::detail::mutex::scoped_lock lock(timer_mutex_); + timer_interrupt_issued_ = false; + timeout = get_timeout(); + } + + // Get the next operation from the queue. + DWORD bytes_transferred = 0; + dword_ptr_t completion_key = 0; + LPOVERLAPPED overlapped = 0; + ::SetLastError(0); + BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, + &completion_key, &overlapped, block ? timeout : 0); + DWORD last_error = ::GetLastError(); + + // Dispatch any pending timers. + if (dispatching_timers) + { + 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) + { + if (block && last_error == WAIT_TIMEOUT) + { + // Relinquish responsibility for dispatching timers. + if (dispatching_timers) + { + ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id); + } + + continue; + } + + // Transfer responsibility for dispatching timers to another thread. + if (dispatching_timers && ::InterlockedCompareExchange( + &timer_thread_, 0, this_thread_id) == this_thread_id) + { + ::PostQueuedCompletionStatus(iocp_.handle, + 0, transfer_timer_dispatching, 0); + } + + ec = asio::error_code(); + return 0; + } + else if (overlapped) + { + operation* op = static_cast<operation*>(overlapped); + asio::error_code result_ec(last_error, + asio::error::get_system_category()); + + // Transfer responsibility for dispatching timers to another thread. + if (dispatching_timers && ::InterlockedCompareExchange( + &timer_thread_, 0, this_thread_id) == this_thread_id) + { + ::PostQueuedCompletionStatus(iocp_.handle, + 0, transfer_timer_dispatching, 0); + } + + // We may have been passed the last_error and bytes_transferred in the + // OVERLAPPED structure itself. + if (completion_key == overlapped_contains_result) + { + result_ec = asio::error_code(static_cast<int>(op->Offset), + static_cast<asio::error_category>(op->Internal)); + bytes_transferred = op->OffsetHigh; + } + + // Otherwise ensure any result has been saved into the OVERLAPPED + // structure. + else + { + op->Internal = 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 = asio::error_code(); + return 1; + } + } + else if (completion_key == transfer_timer_dispatching) + { + // Woken up to try to acquire responsibility for dispatching timers. + ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id); + } + else if (completion_key == steal_timer_dispatching) + { + // Woken up to steal responsibility for dispatching timers. + ::InterlockedExchange(&timer_thread_, 0); + } + else + { + // Relinquish responsibility for dispatching timers. If the io_service + // is not being stopped then the thread will get an opportunity to + // reacquire timer responsibility on the next loop iteration. + if (dispatching_timers) + { + ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id); + } + + // The stopped_ flag is always checked to ensure that any leftover + // interrupts from a previous run invocation are ignored. + if (::InterlockedExchangeAdd(&stopped_, 0) != 0) + { + // Wake up next thread that is blocked on GetQueuedCompletionStatus. + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + + ec = asio::error_code(); + return 0; + } + } + } + } + + // 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() + { + return timer_queues_.wait_duration_msec(max_timeout); + } + + // Helper class to call work_finished() on block exit. + struct work_finished_on_block_exit + { + ~work_finished_on_block_exit() + { + io_service_->work_finished(); + } + + win_iocp_io_service* io_service_; + }; + + // The IO completion port used for queueing operations. + struct iocp_holder + { + HANDLE handle; + iocp_holder() : handle(0) {} + ~iocp_holder() { if (handle) ::CloseHandle(handle); } + } iocp_; + + // The count of unfinished work. + long outstanding_work_; + + // Flag to indicate whether the event loop has been stopped. + long stopped_; + + // Flag to indicate whether the service has been shut down. + long shutdown_; + + enum + { + // Maximum GetQueuedCompletionStatus timeout, in milliseconds. + max_timeout = 500, + + // Completion key value to indicate that responsibility for dispatching + // timers is being cooperatively transferred from one thread to another. + transfer_timer_dispatching = 1, + + // Completion key value to indicate that responsibility for dispatching + // timers should be stolen from another thread. + 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. + long timer_thread_; + + // Mutex for protecting access to the timer queues. + mutex timer_mutex_; + + // Whether a thread has been interrupted to process a new timeout. + bool timer_interrupt_issued_; + + // The timer queues. + timer_queue_set timer_queues_; + + // The operations that are ready to dispatch. + op_queue<operation> completed_ops_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP diff --git a/ext/asio/detail/win_iocp_io_service_fwd.hpp b/ext/asio/detail/win_iocp_io_service_fwd.hpp new file mode 100644 index 0000000000..29d2a05481 --- /dev/null +++ b/ext/asio/detail/win_iocp_io_service_fwd.hpp @@ -0,0 +1,51 @@ +// +// win_iocp_io_service_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 ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP +#define ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" + +// This service is only supported on Win32 (NT4 and later). +#if !defined(ASIO_DISABLE_IOCP) +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +#if !defined(UNDER_CE) + +// Define this to indicate that IOCP is supported on the target platform. +#define ASIO_HAS_IOCP 1 + +namespace asio { +namespace detail { + +class win_iocp_io_service; +class win_iocp_overlapped_ptr; + +} // namespace detail +} // namespace asio + +#endif // !defined(UNDER_CE) +#endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +#endif // !defined(ASIO_DISABLE_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP diff --git a/ext/asio/detail/win_iocp_operation.hpp b/ext/asio/detail/win_iocp_operation.hpp new file mode 100644 index 0000000000..ac8106255a --- /dev/null +++ b/ext/asio/detail/win_iocp_operation.hpp @@ -0,0 +1,89 @@ +// +// 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 ASIO_DETAIL_WIN_IOCP_OPERATION_HPP +#define ASIO_DETAIL_WIN_IOCP_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error_code.hpp" +#include "asio/detail/op_queue.hpp" + +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 asio::error_code& ec = asio::error_code(), + std::size_t bytes_transferred = 0) + { + func_(&owner, this, ec, bytes_transferred); + } + + void destroy() + { + func_(0, this, asio::error_code(), 0); + } + +protected: + typedef void (*func_type)(win_iocp_io_service*, + win_iocp_operation*, asio::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 + +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_OPERATION_HPP diff --git a/ext/asio/detail/win_iocp_overlapped_ptr.hpp b/ext/asio/detail/win_iocp_overlapped_ptr.hpp new file mode 100644 index 0000000000..47a3f70cf1 --- /dev/null +++ b/ext/asio/detail/win_iocp_overlapped_ptr.hpp @@ -0,0 +1,174 @@ +// +// win_iocp_overlapped_ptr.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 ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP +#define ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/win_iocp_io_service.hpp" +#include "asio/detail/win_iocp_operation.hpp" + +namespace asio { +namespace detail { + +// Wraps a handler to create an OVERLAPPED object for use with overlapped I/O. +class win_iocp_overlapped_ptr + : private noncopyable +{ +public: + // Construct an empty win_iocp_overlapped_ptr. + win_iocp_overlapped_ptr() + : ptr_(0), + iocp_service_(0) + { + } + + // Construct an win_iocp_overlapped_ptr to contain the specified handler. + template <typename Handler> + explicit win_iocp_overlapped_ptr( + asio::io_service& io_service, Handler handler) + : ptr_(0), + iocp_service_(0) + { + this->reset(io_service, handler); + } + + // Destructor automatically frees the OVERLAPPED object unless released. + ~win_iocp_overlapped_ptr() + { + reset(); + } + + // Reset to empty. + void reset() + { + if (ptr_) + { + ptr_->destroy(); + ptr_ = 0; + iocp_service_->work_finished(); + iocp_service_ = 0; + } + } + + // Reset to contain the specified handler, freeing any current OVERLAPPED + // object. + template <typename Handler> + void reset(asio::io_service& io_service, Handler handler) + { + 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, handler); + io_service.impl_.work_started(); + reset(); + ptr_ = ptr.release(); + iocp_service_ = &io_service.impl_; + } + + // Get the contained OVERLAPPED object. + OVERLAPPED* get() + { + return ptr_; + } + + // Get the contained OVERLAPPED object. + const OVERLAPPED* get() const + { + return ptr_; + } + + // Release ownership of the OVERLAPPED object. + OVERLAPPED* release() + { + if (ptr_) + iocp_service_->on_pending(ptr_); + + OVERLAPPED* tmp = ptr_; + ptr_ = 0; + iocp_service_ = 0; + return tmp; + } + + // Post completion notification for overlapped operation. Releases ownership. + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + if (ptr_) + { + iocp_service_->on_completion(ptr_, ec, + static_cast<DWORD>(bytes_transferred)); + ptr_ = 0; + iocp_service_ = 0; + } + } + +private: + template <typename Handler> + struct overlapped_op : public win_iocp_operation + { + overlapped_op(Handler handler) + : win_iocp_operation(&overlapped_op::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation object. + 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, asio::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + Handler handler_; + }; + + win_iocp_operation* ptr_; + win_iocp_io_service* iocp_service_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP diff --git a/ext/asio/detail/win_iocp_serial_port_service.hpp b/ext/asio/detail/win_iocp_serial_port_service.hpp new file mode 100644 index 0000000000..ed5f75e91c --- /dev/null +++ b/ext/asio/detail/win_iocp_serial_port_service.hpp @@ -0,0 +1,288 @@ +// +// win_iocp_serial_port_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.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 ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <cstring> +#include <string> +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/win_iocp_handle_service.hpp" + +namespace asio { +namespace detail { + +// Extend win_iocp_handle_service to provide serial port support. +class win_iocp_serial_port_service +{ +public: + // The native type of a stream handle. + typedef win_iocp_handle_service::native_type native_type; + + // The implementation type of the stream handle. + typedef win_iocp_handle_service::implementation_type implementation_type; + + win_iocp_serial_port_service(asio::io_service& io_service) + : handle_service_(io_service) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + // Construct a new handle implementation. + void construct(implementation_type& impl) + { + handle_service_.construct(impl); + } + + // Destroy a handle implementation. + void destroy(implementation_type& impl) + { + handle_service_.destroy(impl); + } + + // Open the serial port using the specified device name. + asio::error_code open(implementation_type& impl, + const std::string& device, asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + // For convenience, add a leading \\.\ sequence if not already present. + std::string name = (device[0] == '\\') ? device : "\\\\.\\" + device; + + // Open a handle to the serial port. + ::HANDLE handle = ::CreateFileA(name.c_str(), + GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + if (handle == INVALID_HANDLE_VALUE) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // Determine the initial serial port parameters. + using namespace std; // For memcpy. + ::DCB dcb; + memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(handle, &dcb)) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(handle); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // Set some default serial port parameters. This implementation does not + // support changing these, so they might as well be in a known state. + dcb.fBinary = TRUE; // Win32 only supports binary mode. + dcb.fDsrSensitivity = FALSE; + dcb.fNull = FALSE; // Do not ignore NULL characters. + dcb.fAbortOnError = FALSE; // Ignore serial framing errors. + if (!::SetCommState(handle, &dcb)) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(handle); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // Set up timeouts so that the serial port will behave similarly to a + // network socket. Reads wait for at least one byte, then return with + // whatever they have. Writes return once everything is out the door. + ::COMMTIMEOUTS timeouts; + timeouts.ReadIntervalTimeout = 1; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + if (!::SetCommTimeouts(handle, &timeouts)) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(handle); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // We're done. Take ownership of the serial port handle. + if (handle_service_.assign(impl, handle, ec)) + ::CloseHandle(handle); + return ec; + } + + // Assign a native handle to a handle implementation. + asio::error_code assign(implementation_type& impl, + const native_type& native_handle, asio::error_code& ec) + { + return handle_service_.assign(impl, native_handle, ec); + } + + // Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return handle_service_.is_open(impl); + } + + // Destroy a handle implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return handle_service_.close(impl, ec); + } + + // Get the native handle representation. + native_type native(implementation_type& impl) + { + return handle_service_.native(impl); + } + + // Cancel all operations associated with the handle. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return handle_service_.cancel(impl, ec); + } + + // Set an option on the serial port. + template <typename SettableSerialPortOption> + asio::error_code set_option(implementation_type& impl, + const SettableSerialPortOption& option, asio::error_code& ec) + { + using namespace std; // For memcpy. + + ::DCB dcb; + memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(handle_service_.native(impl), &dcb)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + if (option.store(dcb, ec)) + return ec; + + if (!::SetCommState(handle_service_.native(impl), &dcb)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + ec = asio::error_code(); + return ec; + } + + // Get an option from the serial port. + template <typename GettableSerialPortOption> + asio::error_code get_option(const implementation_type& impl, + GettableSerialPortOption& option, asio::error_code& ec) const + { + using namespace std; // For memcpy. + + ::DCB dcb; + memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(handle_service_.native(impl), &dcb)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + return option.load(dcb, ec); + } + + // Send a break sequence to the serial port. + asio::error_code send_break(implementation_type&, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Write the given data. Returns the number of bytes sent. + template <typename ConstBufferSequence> + size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return handle_service_.write_some(impl, buffers, ec); + } + + // Start an asynchronous write. The data being written 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) + { + handle_service_.async_write_some(impl, buffers, handler); + } + + // Read some data. Returns the number of bytes received. + template <typename MutableBufferSequence> + size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return handle_service_.read_some(impl, buffers, ec); + } + + // Start an asynchronous read. The buffer for the data being received 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) + { + handle_service_.async_read_some(impl, buffers, handler); + } + +private: + // The implementation used for initiating asynchronous operations. + win_iocp_handle_service handle_service_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP diff --git a/ext/asio/detail/win_iocp_socket_service.hpp b/ext/asio/detail/win_iocp_socket_service.hpp new file mode 100644 index 0000000000..cb1d2037de --- /dev/null +++ b/ext/asio/detail/win_iocp_socket_service.hpp @@ -0,0 +1,2010 @@ +// +// win_iocp_socket_service.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 ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" +#include <cstring> +#include <boost/shared_ptr.hpp> +#include <boost/type_traits/is_same.hpp> +#include <boost/weak_ptr.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/null_buffers_op.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/win_iocp_io_service.hpp" + +namespace asio { +namespace detail { + +template <typename Protocol> +class win_iocp_socket_service +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + struct noop_deleter { void operator()(void*) {} }; + typedef boost::shared_ptr<void> shared_cancel_token_type; + typedef boost::weak_ptr<void> weak_cancel_token_type; + + // The native type of a socket. + class native_type + { + public: + native_type(socket_type s) + : socket_(s), + have_remote_endpoint_(false) + { + } + + native_type(socket_type s, const endpoint_type& ep) + : socket_(s), + have_remote_endpoint_(true), + remote_endpoint_(ep) + { + } + + void operator=(socket_type s) + { + socket_ = s; + have_remote_endpoint_ = false; + remote_endpoint_ = endpoint_type(); + } + + operator socket_type() const + { + return socket_; + } + + HANDLE as_handle() const + { + return reinterpret_cast<HANDLE>(socket_); + } + + bool have_remote_endpoint() const + { + return have_remote_endpoint_; + } + + endpoint_type remote_endpoint() const + { + return remote_endpoint_; + } + + private: + socket_type socket_; + bool have_remote_endpoint_; + endpoint_type remote_endpoint_; + }; + + // The implementation type of the socket. + class implementation_type + { + public: + // Default constructor. + implementation_type() + : socket_(invalid_socket), + flags_(0), + cancel_token_(), + protocol_(endpoint_type().protocol()), + next_(0), + prev_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class win_iocp_socket_service; + + // The native socket representation. + native_type socket_; + + enum + { + enable_connection_aborted = 1, // User wants connection_aborted errors. + close_might_block = 2, // User set linger option for blocking close. + user_set_non_blocking = 4 // The user wants a non-blocking socket. + }; + + // Flags indicating the current state of the socket. + unsigned char flags_; + + // We use a shared pointer as a cancellation token here to work around the + // broken Windows support for cancellation. MSDN says that when you call + // closesocket any outstanding WSARecv or WSASend operations will complete + // with the error ERROR_OPERATION_ABORTED. In practice they complete with + // ERROR_NETNAME_DELETED, which means you can't tell the difference between + // a local cancellation and the socket being hard-closed by the peer. + shared_cancel_token_type cancel_token_; + + // The protocol associated with the socket. + protocol_type protocol_; + + // Per-descriptor data used by the reactor. + reactor::per_descriptor_data reactor_data_; + +#if defined(ASIO_ENABLE_CANCELIO) + // The ID of the thread from which it is safe to cancel asynchronous + // operations. 0 means no asynchronous operations have been started yet. + // ~0 means asynchronous operations have been started from more than one + // thread, and cancellation is not supported for the socket. + DWORD safe_cancellation_thread_id_; +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Pointers to adjacent socket implementations in linked list. + implementation_type* next_; + implementation_type* prev_; + }; + + // Constructor. + win_iocp_socket_service(asio::io_service& io_service) + : io_service_(io_service), + iocp_service_(use_service<win_iocp_io_service>(io_service)), + reactor_(0), + mutex_(), + impl_list_(0) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + // Close all implementations, causing all operations to complete. + asio::detail::mutex::scoped_lock lock(mutex_); + implementation_type* impl = impl_list_; + while (impl) + { + asio::error_code ignored_ec; + close_for_destruction(*impl); + impl = impl->next_; + } + } + + // Construct a new socket implementation. + void construct(implementation_type& impl) + { + impl.socket_ = invalid_socket; + impl.flags_ = 0; + impl.cancel_token_.reset(); +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; + } + + // Destroy a socket implementation. + void destroy(implementation_type& impl) + { + close_for_destruction(impl); + + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + } + + // Open a new socket implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + socket_holder sock(socket_ops::socket(protocol.family(), protocol.type(), + protocol.protocol(), ec)); + if (sock.get() == invalid_socket) + return ec; + + HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock.get()); + if (iocp_service_.register_handle(sock_as_handle, ec)) + return ec; + + impl.socket_ = sock.release(); + impl.flags_ = 0; + impl.cancel_token_.reset(static_cast<void*>(0), noop_deleter()); + impl.protocol_ = protocol; + ec = asio::error_code(); + return ec; + } + + // Assign a native socket to a socket implementation. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_type& native_socket, + asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (iocp_service_.register_handle(native_socket.as_handle(), ec)) + return ec; + + impl.socket_ = native_socket; + impl.flags_ = 0; + impl.cancel_token_.reset(static_cast<void*>(0), noop_deleter()); + impl.protocol_ = protocol; + ec = asio::error_code(); + return ec; + } + + // Determine whether the socket is open. + bool is_open(const implementation_type& impl) const + { + return impl.socket_ != invalid_socket; + } + + // Destroy a socket implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + if (is_open(impl)) + { + // 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* r = static_cast<reactor*>( + interlocked_compare_exchange_pointer( + reinterpret_cast<void**>(&reactor_), 0, 0)); + if (r) + r->close_descriptor(impl.socket_, impl.reactor_data_); + + if (socket_ops::close(impl.socket_, ec) == socket_error_retval) + return ec; + + impl.socket_ = invalid_socket; + impl.flags_ = 0; + impl.cancel_token_.reset(); +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + } + + ec = asio::error_code(); + return ec; + } + + // Get the native socket representation. + native_type native(implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( + ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) + { + // The version of Windows supports cancellation from any thread. + typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); + cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr; + socket_type sock = impl.socket_; + HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock); + if (!cancel_io_ex(sock_as_handle, 0)) + { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_NOT_FOUND) + { + // ERROR_NOT_FOUND means that there were no operations to be + // cancelled. We swallow this error to match the behaviour on other + // platforms. + ec = asio::error_code(); + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + } + else + { + ec = asio::error_code(); + } + } +#if defined(ASIO_ENABLE_CANCELIO) + else if (impl.safe_cancellation_thread_id_ == 0) + { + // No operations have been started, so there's nothing to cancel. + ec = asio::error_code(); + } + else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) + { + // Asynchronous operations have been started from the current thread only, + // so it is safe to try to cancel them using CancelIo. + socket_type sock = impl.socket_; + HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock); + if (!::CancelIo(sock_as_handle)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + } + else + { + // Asynchronous operations have been started from more than one thread, + // so cancellation is not safe. + ec = asio::error::operation_not_supported; + } +#else // defined(ASIO_ENABLE_CANCELIO) + else + { + // Cancellation is not supported as CancelIo may not be used. + ec = asio::error::operation_not_supported; + } +#endif // defined(ASIO_ENABLE_CANCELIO) + + return ec; + } + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return false; + } + + asio::detail::ioctl_arg_type value = 0; + socket_ops::ioctl(impl.socket_, SIOCATMARK, &value, ec); + return ec ? false : value != 0; + } + + // Determine the number of bytes available for reading. + std::size_t available(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + asio::detail::ioctl_arg_type value = 0; + socket_ops::ioctl(impl.socket_, FIONREAD, &value, ec); + return ec ? static_cast<std::size_t>(0) : static_cast<std::size_t>(value); + } + + // Bind the socket to the specified local endpoint. + asio::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); + return ec; + } + + // Place the socket into the state where it will listen for new connections. + asio::error_code listen(implementation_type& impl, int backlog, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::listen(impl.socket_, backlog, ec); + return ec; + } + + // Set a socket option. + template <typename Option> + asio::error_code set_option(implementation_type& impl, + const Option& option, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + if (option.level(impl.protocol_) == custom_socket_option_level + && option.name(impl.protocol_) == enable_connection_aborted_option) + { + if (option.size(impl.protocol_) != sizeof(int)) + { + ec = asio::error::invalid_argument; + } + else + { + if (*reinterpret_cast<const int*>(option.data(impl.protocol_))) + impl.flags_ |= implementation_type::enable_connection_aborted; + else + impl.flags_ &= ~implementation_type::enable_connection_aborted; + ec = asio::error_code(); + } + return ec; + } + else + { + if (option.level(impl.protocol_) == SOL_SOCKET + && option.name(impl.protocol_) == SO_LINGER) + { + const ::linger* linger_option = + reinterpret_cast<const ::linger*>(option.data(impl.protocol_)); + if (linger_option->l_onoff != 0 && linger_option->l_linger != 0) + impl.flags_ |= implementation_type::close_might_block; + else + impl.flags_ &= ~implementation_type::close_might_block; + } + + socket_ops::setsockopt(impl.socket_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_), ec); + return ec; + } + } + + // Set a socket option. + template <typename Option> + asio::error_code get_option(const implementation_type& impl, + Option& option, asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + if (option.level(impl.protocol_) == custom_socket_option_level + && option.name(impl.protocol_) == enable_connection_aborted_option) + { + if (option.size(impl.protocol_) != sizeof(int)) + { + ec = asio::error::invalid_argument; + } + else + { + int* target = reinterpret_cast<int*>(option.data(impl.protocol_)); + if (impl.flags_ & implementation_type::enable_connection_aborted) + *target = 1; + else + *target = 0; + option.resize(impl.protocol_, sizeof(int)); + ec = asio::error_code(); + } + return ec; + } + else + { + size_t size = option.size(impl.protocol_); + socket_ops::getsockopt(impl.socket_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); + return ec; + } + } + + // Perform an IO control command on the socket. + template <typename IO_Control_Command> + asio::error_code io_control(implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::ioctl(impl.socket_, command.name(), + static_cast<ioctl_arg_type*>(command.data()), ec); + + if (!ec && command.name() == static_cast<int>(FIONBIO)) + { + 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; + } + + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return endpoint_type(); + } + + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return endpoint_type(); + } + + if (impl.socket_.have_remote_endpoint()) + { + // Check if socket is still connected. + DWORD connect_time = 0; + size_t connect_time_len = sizeof(connect_time); + if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_CONNECT_TIME, + &connect_time, &connect_time_len, ec) == socket_error_retval) + { + return endpoint_type(); + } + if (connect_time == 0xFFFFFFFF) + { + ec = asio::error::not_connected; + return endpoint_type(); + } + + ec = asio::error_code(); + return impl.socket_.remote_endpoint(); + } + else + { + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + } + + /// Disable sends or receives on the socket. + asio::error_code shutdown(implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::shutdown(impl.socket_, what, ec); + return ec; + } + + // Send the given data to the peer. Returns the number of bytes sent. + template <typename ConstBufferSequence> + size_t send(implementation_type& impl, const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + buffer_sequence_adapter<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 && bufs.all_empty()) + { + ec = asio::error_code(); + return 0; + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASend(impl.socket_, bufs.buffers(), + bufs.count(), &bytes_transferred, flags, 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_NETNAME_DELETED) + last_error = WSAECONNRESET; + else if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; + } + + // Wait until data can be sent without blocking. + size_t send(implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, ec); + + return 0; + } + + template <typename ConstBufferSequence, typename Handler> + class send_op : public operation + { + public: + send_op(weak_cancel_token_type cancel_token, + const ConstBufferSequence& buffers, Handler handler) + : operation(&send_op::do_complete), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation 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); + + // Make the upcall if required. + if (owner) + { +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + buffer_sequence_adapter<asio::const_buffer, + ConstBufferSequence>::validate(o->buffers_); +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (o->cancel_token_.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // 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, asio::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + weak_cancel_token_type cancel_token_; + ConstBufferSequence buffers_; + 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.cancel_token_, buffers, handler); + + buffer_sequence_adapter<asio::const_buffer, + ConstBufferSequence> bufs(buffers); + + start_send_op(impl, bufs.buffers(), bufs.count(), flags, + impl.protocol_.type() == SOCK_STREAM && bufs.all_empty(), ptr.get()); + 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) + { + // 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_reactor_op(impl, reactor::write_op, ptr.get()); + ptr.release(); + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template <typename ConstBufferSequence> + size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + buffer_sequence_adapter<asio::const_buffer, + ConstBufferSequence> bufs(buffers); + + // Send the data. + DWORD bytes_transferred = 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(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; + } + + // Wait until data can be sent without blocking. + size_t send_to(implementation_type& impl, const null_buffers&, + socket_base::message_flags, const endpoint_type&, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, ec); + + return 0; + } + + template <typename ConstBufferSequence, typename Handler> + class send_to_op : public operation + { + public: + send_to_op(weak_cancel_token_type cancel_token, + const ConstBufferSequence& buffers, Handler handler) + : operation(&send_to_op::do_complete), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation 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 defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + buffer_sequence_adapter<asio::const_buffer, + ConstBufferSequence>::validate(o->buffers_); +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // 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, asio::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + weak_cancel_token_type cancel_token_; + ConstBufferSequence buffers_; + 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.cancel_token_, buffers, handler); + + buffer_sequence_adapter<asio::const_buffer, + ConstBufferSequence> bufs(buffers); + + 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. + template <typename Handler> + void async_send_to(implementation_type& impl, const null_buffers&, + socket_base::message_flags, const endpoint_type&, Handler handler) + { + // 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_reactor_op(impl, reactor::write_op, ptr.get()); + ptr.release(); + } + + // Receive some data from the peer. Returns the number of bytes received. + template <typename MutableBufferSequence> + size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + buffer_sequence_adapter<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 && bufs.all_empty()) + { + ec = asio::error_code(); + return 0; + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(impl.socket_, bufs.buffers(), + bufs.count(), &bytes_transferred, &recv_flags, 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_NETNAME_DELETED) + last_error = WSAECONNRESET; + else if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + if (bytes_transferred == 0 && impl.protocol_.type() == SOCK_STREAM) + { + ec = asio::error::eof; + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; + } + + // Wait until data can be received without blocking. + size_t receive(implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, ec); + + return 0; + } + + template <typename MutableBufferSequence, typename Handler> + class receive_op : public operation + { + public: + receive_op(int protocol_type, weak_cancel_token_type cancel_token, + const MutableBufferSequence& buffers, Handler handler) + : operation(&receive_op::do_complete), + protocol_type_(protocol_type), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation 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); + + // Make the upcall if required. + if (owner) + { +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + buffer_sequence_adapter<asio::mutable_buffer, + MutableBufferSequence>::validate(o->buffers_); +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (o->cancel_token_.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // Check for connection closed. + else if (!ec && bytes_transferred == 0 + && o->protocol_type_ == SOCK_STREAM + && !buffer_sequence_adapter<asio::mutable_buffer, + MutableBufferSequence>::all_empty(o->buffers_) + && !boost::is_same<MutableBufferSequence, null_buffers>::value) + { + ec = asio::error::eof; + } + + // 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, asio::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + int protocol_type_; + weak_cancel_token_type cancel_token_; + MutableBufferSequence buffers_; + 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, protocol_type, + impl.cancel_token_, buffers, handler); + + buffer_sequence_adapter<asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); + + 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. + template <typename Handler> + void async_receive(implementation_type& impl, const null_buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + 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. + + // Allocate and construct an operation to wrap the handler. + 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, + impl.cancel_token_, buffers, handler); + + ::WSABUF buf = { 0, 0 }; + start_receive_op(impl, &buf, 1, flags, false, ptr.get()); + ptr.release(); + } + 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_reactor_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + ptr.get()); + ptr.release(); + } + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template <typename MutableBufferSequence> + size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + buffer_sequence_adapter<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.buffers(), + bufs.count(), &bytes_transferred, &recv_flags, + sender_endpoint.data(), &endpoint_size, 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + if (bytes_transferred == 0 && impl.protocol_.type() == SOCK_STREAM) + { + ec = asio::error::eof; + return 0; + } + + sender_endpoint.resize(static_cast<std::size_t>(endpoint_size)); + + ec = asio::error_code(); + return bytes_transferred; + } + + // Wait until data can be received without blocking. + size_t receive_from(implementation_type& impl, + const null_buffers&, endpoint_type& sender_endpoint, + socket_base::message_flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, ec); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + return 0; + } + + template <typename MutableBufferSequence, typename Handler> + class receive_from_op : public operation + { + public: + 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())), + buffers_(buffers), + handler_(handler) + { + } + + int& endpoint_size() + { + return endpoint_size_; + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation 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 defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + buffer_sequence_adapter<asio::mutable_buffer, + MutableBufferSequence>::validate(o->buffers_); +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // 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, asio::error_code, std::size_t> + handler(o->handler_, ec, bytes_transferred); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + int protocol_type_; + endpoint_type& endpoint_; + int endpoint_size_; + weak_cancel_token_type cancel_token_; + MutableBufferSequence buffers_; + 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_endp, + 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, + protocol_type, sender_endp, buffers, handler); + + buffer_sequence_adapter<asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); + + 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. + template <typename Handler> + void async_receive_from(implementation_type& impl, + const null_buffers&, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler handler) + { + // 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(); + + 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. + template <typename Socket> + asio::error_code accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + // We cannot accept a socket that is already open. + if (peer.is_open()) + { + ec = asio::error::already_open; + return ec; + } + + for (;;) + { + socket_holder new_socket; + std::size_t addr_len = 0; + if (peer_endpoint) + { + addr_len = peer_endpoint->capacity(); + new_socket.reset(socket_ops::accept(impl.socket_, + peer_endpoint->data(), &addr_len, ec)); + } + else + { + new_socket.reset(socket_ops::accept(impl.socket_, 0, 0, ec)); + } + + if (ec) + { + if (ec == asio::error::connection_aborted + && !(impl.flags_ & implementation_type::enable_connection_aborted)) + { + // Retry accept operation. + continue; + } + else + { + return ec; + } + } + + if (peer_endpoint) + peer_endpoint->resize(addr_len); + + peer.assign(impl.protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + return ec; + } + } + + template <typename Socket, typename Handler> + class accept_op : public operation + { + public: + 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), + peer_(peer), + protocol_(protocol), + peer_endpoint_(peer_endpoint), + enable_connection_aborted_(enable_connection_aborted), + handler_(handler) + { + } + + socket_holder& new_socket() + { + return new_socket_; + } + + void* output_buffer() + { + return output_buffer_; + } + + DWORD address_length() + { + return sizeof(sockaddr_storage_type) + 16; + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t /*bytes_transferred*/) + { + // 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) + { + // Map Windows error ERROR_NETNAME_DELETED to connection_aborted. + if (ec.value() == ERROR_NETNAME_DELETED) + { + ec = 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 (ec == asio::error::connection_aborted + && !o->enable_connection_aborted_) + { + // 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) + { + // 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 = asio::error_code(last_error, + asio::error::get_system_category()); + + // Check if the operation completed immediately. + if (!result && last_error != WSA_IO_PENDING) + { + 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 + { + // 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 = asio::error::invalid_argument; + } + else + { + 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)); + } + } + + // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname + // and getpeername will work on the accepted socket. + if (!ec) + { + 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); + } + + // If the socket was successfully accepted, transfer ownership of the + // socket to the peer object. + if (!ec) + { + o->peer_.assign(o->protocol_, + native_type(o->new_socket_.get(), peer_endpoint), ec); + if (!ec) + o->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, asio::error_code> + handler(o->handler_, ec); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + win_iocp_io_service& iocp_service_; + socket_type socket_; + socket_holder new_socket_; + Socket& peer_; + protocol_type protocol_; + endpoint_type* peer_endpoint_; + unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2]; + bool enable_connection_aborted_; + 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); + 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. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + // Perform the connect operation. + socket_ops::connect(impl.socket_, + peer_endpoint.data(), peer_endpoint.size(), ec); + return ec; + } + + class connect_op_base : public reactor_op + { + public: + connect_op_base(socket_type socket, func_type complete_func) + : reactor_op(&connect_op_base::do_perform, complete_func), + socket_(socket) + { + } + + static bool do_perform(reactor_op* base) + { + 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(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) + { + o->ec_ = asio::error_code(connect_error, + asio::error::get_system_category()); + } + + return true; + } + + 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) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::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, asio::error_code> + handler(o->handler_, o->ec_); + ptr.reset(); + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + + private: + Handler handler_; + }; + + // Start an asynchronous connect. + template <typename Handler> + 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, 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, asio::error::bad_descriptor); + else + { + 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); + } + } + + // 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(); + + if (noop) + iocp_service_.on_completion(op); + else if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + 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); + } + } + + // 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, asio::error::bad_descriptor); + else + { + 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); + } + } + + // 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, asio::error::bad_descriptor); + else if (peer_is_open) + iocp_service_.on_completion(op, asio::error::already_open); + else + { + asio::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 + { + 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); + } + } + } + + // 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)) + { + r.start_op(op_type, impl.socket_, impl.reactor_data_, op, false); + return; + } + else + op->ec_ = 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)) + { + ioctl_arg_type non_blocking = 1; + if (!socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, op->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_); + } + + if (op->ec_ == asio::error::in_progress + || op->ec_ == asio::error::would_block) + { + op->ec_ = asio::error_code(); + r.start_op(reactor::connect_op, impl.socket_, + impl.reactor_data_, op, true); + return; + } + } + } + } + else + op->ec_ = asio::error::bad_descriptor; + + iocp_service_.post_immediate_completion(op); + } + + // Helper function to close a socket when the associated object is being + // destroyed. + void close_for_destruction(implementation_type& impl) + { + if (is_open(impl)) + { + // 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* r = static_cast<reactor*>( + interlocked_compare_exchange_pointer( + reinterpret_cast<void**>(&reactor_), 0, 0)); + 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 + // default so that the closure is performed in the background. + if (impl.flags_ & implementation_type::close_might_block) + { + ::linger opt; + opt.l_onoff = 0; + opt.l_linger = 0; + asio::error_code ignored_ec; + socket_ops::setsockopt(impl.socket_, + SOL_SOCKET, SO_LINGER, &opt, sizeof(opt), ignored_ec); + } + + asio::error_code ignored_ec; + socket_ops::close(impl.socket_, ignored_ec); + impl.socket_ = invalid_socket; + impl.flags_ = 0; + impl.cancel_token_.reset(); +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + } + } + + // Update the ID of the thread from which cancellation is safe. + void update_cancellation_thread_id(implementation_type& impl) + { +#if defined(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(ASIO_ENABLE_CANCELIO) + (void)impl; +#endif // defined(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 + // - platform SDKs where MSVC's /Wp64 option causes spurious warnings. + void* interlocked_compare_exchange_pointer(void** dest, void* exch, void* cmp) + { +#if defined(_M_IX86) + return reinterpret_cast<void*>(InterlockedCompareExchange( + reinterpret_cast<PLONG>(dest), reinterpret_cast<LONG>(exch), + reinterpret_cast<LONG>(cmp))); +#else + return InterlockedCompareExchangePointer(dest, exch, cmp); +#endif + } + + // Helper function to emulate InterlockedExchangePointer functionality for: + // - very old Platform SDKs; and + // - platform SDKs where MSVC's /Wp64 option causes spurious warnings. + void* interlocked_exchange_pointer(void** dest, void* val) + { +#if defined(_M_IX86) + return reinterpret_cast<void*>(InterlockedExchange( + reinterpret_cast<PLONG>(dest), reinterpret_cast<LONG>(val))); +#else + return InterlockedExchangePointer(dest, val); +#endif + } + + // The io_service used to obtain the reactor, if required. + 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* reactor_; + + // Mutex to protect access to the linked list of implementations. + asio::detail::mutex mutex_; + + // The head of a linked list of all implementations. + implementation_type* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP diff --git a/ext/asio/detail/win_mutex.hpp b/ext/asio/detail/win_mutex.hpp new file mode 100644 index 0000000000..1280a4e402 --- /dev/null +++ b/ext/asio/detail/win_mutex.hpp @@ -0,0 +1,121 @@ +// +// win_mutex.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 ASIO_DETAIL_WIN_MUTEX_HPP +#define ASIO_DETAIL_WIN_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +class win_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock<win_mutex> scoped_lock; + + // Constructor. + win_mutex() + { + int error = do_init(); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, + asio::error::get_system_category()), + "mutex"); + boost::throw_exception(e); + } + } + + // Destructor. + ~win_mutex() + { + ::DeleteCriticalSection(&crit_section_); + } + + // Lock the mutex. + void lock() + { + ::EnterCriticalSection(&crit_section_); + } + + // Unlock the mutex. + void unlock() + { + ::LeaveCriticalSection(&crit_section_); + } + +private: + // Initialisation must be performed in a separate function to the constructor + // since the compiler does not support the use of structured exceptions and + // C++ exceptions in the same function. + int do_init() + { +#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) + { + return ERROR_OUTOFMEMORY; + } + + return 0; +#endif + } + + ::CRITICAL_SECTION crit_section_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_MUTEX_HPP diff --git a/ext/asio/detail/win_signal_blocker.hpp b/ext/asio/detail/win_signal_blocker.hpp new file mode 100644 index 0000000000..50d16b8a2b --- /dev/null +++ b/ext/asio/detail/win_signal_blocker.hpp @@ -0,0 +1,67 @@ +// +// win_signal_blocker.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 ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class win_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + win_signal_blocker() + { + // No-op. + } + + // Destructor restores the previous signal mask. + ~win_signal_blocker() + { + // No-op. + } + + // Block all signals for the calling thread. + void block() + { + // No-op. + } + + // Restore the previous signal mask. + void unblock() + { + // No-op. + } +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP diff --git a/ext/asio/detail/win_thread.hpp b/ext/asio/detail/win_thread.hpp new file mode 100644 index 0000000000..9bf0665c5e --- /dev/null +++ b/ext/asio/detail/win_thread.hpp @@ -0,0 +1,232 @@ +// +// win_thread.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 ASIO_DETAIL_WIN_THREAD_HPP +#define ASIO_DETAIL_WIN_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) && !defined(UNDER_CE) + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/throw_exception.hpp> +#include <memory> +#include <process.h> +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +unsigned int __stdcall win_thread_function(void* arg); + +#if defined(WINVER) && (WINVER < 0x0500) +void __stdcall apc_function(ULONG data); +#else +void __stdcall apc_function(ULONG_PTR data); +#endif + +template <typename T> +class win_thread_base +{ +public: + static bool terminate_threads() + { + return ::InterlockedExchangeAdd(&terminate_threads_, 0) != 0; + } + + static void set_terminate_threads(bool b) + { + ::InterlockedExchange(&terminate_threads_, b ? 1 : 0); + } + +private: + static long terminate_threads_; +}; + +template <typename T> +long win_thread_base<T>::terminate_threads_ = 0; + +class win_thread + : private noncopyable, + public win_thread_base<win_thread> +{ +public: + // Constructor. + template <typename Function> + win_thread(Function f) + : exit_event_(0) + { + std::auto_ptr<func_base> arg(new func<Function>(f)); + + ::HANDLE entry_event = 0; + arg->entry_event_ = entry_event = ::CreateEvent(0, true, false, 0); + if (!entry_event) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, + asio::error::get_system_category()), + "thread.entry_event"); + boost::throw_exception(e); + } + + arg->exit_event_ = exit_event_ = ::CreateEvent(0, true, false, 0); + if (!exit_event_) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(entry_event); + asio::system_error e( + asio::error_code(last_error, + asio::error::get_system_category()), + "thread.exit_event"); + boost::throw_exception(e); + } + + unsigned int thread_id = 0; + thread_ = reinterpret_cast<HANDLE>(::_beginthreadex(0, 0, + win_thread_function, arg.get(), 0, &thread_id)); + if (!thread_) + { + DWORD last_error = ::GetLastError(); + if (entry_event) + ::CloseHandle(entry_event); + if (exit_event_) + ::CloseHandle(exit_event_); + asio::system_error e( + asio::error_code(last_error, + asio::error::get_system_category()), + "thread"); + boost::throw_exception(e); + } + arg.release(); + + if (entry_event) + { + ::WaitForSingleObject(entry_event, INFINITE); + ::CloseHandle(entry_event); + } + } + + // Destructor. + ~win_thread() + { + ::CloseHandle(thread_); + + // The exit_event_ handle is deliberately allowed to leak here since it + // is an error for the owner of an internal thread not to join() it. + } + + // Wait for the thread to exit. + void join() + { + ::WaitForSingleObject(exit_event_, INFINITE); + ::CloseHandle(exit_event_); + if (terminate_threads()) + { + ::TerminateThread(thread_, 0); + } + else + { + ::QueueUserAPC(apc_function, thread_, 0); + ::WaitForSingleObject(thread_, INFINITE); + } + } + +private: + friend unsigned int __stdcall win_thread_function(void* arg); + +#if defined(WINVER) && (WINVER < 0x0500) + friend void __stdcall apc_function(ULONG); +#else + friend void __stdcall apc_function(ULONG_PTR); +#endif + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + ::HANDLE entry_event_; + ::HANDLE exit_event_; + }; + + template <typename Function> + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::HANDLE thread_; + ::HANDLE exit_event_; +}; + +inline unsigned int __stdcall win_thread_function(void* arg) +{ + std::auto_ptr<win_thread::func_base> func( + static_cast<win_thread::func_base*>(arg)); + + ::SetEvent(func->entry_event_); + + func->run(); + + // Signal that the thread has finished its work, but rather than returning go + // to sleep to put the thread into a well known state. If the thread is being + // joined during global object destruction then it may be killed using + // TerminateThread (to avoid a deadlock in DllMain). Otherwise, the SleepEx + // call will be interrupted using QueueUserAPC and the thread will shut down + // cleanly. + HANDLE exit_event = func->exit_event_; + func.reset(); + ::SetEvent(exit_event); + ::SleepEx(INFINITE, TRUE); + + return 0; +} + +#if defined(WINVER) && (WINVER < 0x0500) +inline void __stdcall apc_function(ULONG) {} +#else +inline void __stdcall apc_function(ULONG_PTR) {} +#endif + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_THREAD_HPP diff --git a/ext/asio/detail/win_tss_ptr.hpp b/ext/asio/detail/win_tss_ptr.hpp new file mode 100644 index 0000000000..5a4ed33cc2 --- /dev/null +++ b/ext/asio/detail/win_tss_ptr.hpp @@ -0,0 +1,95 @@ +// +// win_tss_ptr.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 ASIO_DETAIL_WIN_TSS_PTR_HPP +#define ASIO_DETAIL_WIN_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +template <typename T> +class win_tss_ptr + : private noncopyable +{ +public: +#if defined(UNDER_CE) + enum { out_of_indexes = 0xFFFFFFFF }; +#else + enum { out_of_indexes = TLS_OUT_OF_INDEXES }; +#endif + + // Constructor. + win_tss_ptr() + { + tss_key_ = ::TlsAlloc(); + if (tss_key_ == out_of_indexes) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, + asio::error::get_system_category()), + "tss"); + boost::throw_exception(e); + } + } + + // Destructor. + ~win_tss_ptr() + { + ::TlsFree(tss_key_); + } + + // Get the value. + operator T*() const + { + return static_cast<T*>(::TlsGetValue(tss_key_)); + } + + // Set the value. + void operator=(T* value) + { + ::TlsSetValue(tss_key_, value); + } + +private: + // Thread-specific storage to allow unlocked access to determine whether a + // thread is a member of the pool. + DWORD tss_key_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_TSS_PTR_HPP diff --git a/ext/asio/detail/wince_thread.hpp b/ext/asio/detail/wince_thread.hpp new file mode 100644 index 0000000000..0b6de488a2 --- /dev/null +++ b/ext/asio/detail/wince_thread.hpp @@ -0,0 +1,124 @@ +// +// wince_thread.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 ASIO_DETAIL_WINCE_THREAD_HPP +#define ASIO_DETAIL_WINCE_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) && defined(UNDER_CE) + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/throw_exception.hpp> +#include <memory> +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +DWORD WINAPI wince_thread_function(LPVOID arg); + +class wince_thread + : private noncopyable +{ +public: + // Constructor. + template <typename Function> + wince_thread(Function f) + { + std::auto_ptr<func_base> arg(new func<Function>(f)); + DWORD thread_id = 0; + thread_ = ::CreateThread(0, 0, wince_thread_function, + arg.get(), 0, &thread_id); + if (!thread_) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, + asio::error::get_system_category()), + "thread"); + boost::throw_exception(e); + } + arg.release(); + } + + // Destructor. + ~wince_thread() + { + ::CloseHandle(thread_); + } + + // Wait for the thread to exit. + void join() + { + ::WaitForSingleObject(thread_, INFINITE); + } + +private: + friend DWORD WINAPI wince_thread_function(LPVOID arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + template <typename Function> + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::HANDLE thread_; +}; + +inline DWORD WINAPI wince_thread_function(LPVOID arg) +{ + std::auto_ptr<wince_thread::func_base> func( + static_cast<wince_thread::func_base*>(arg)); + func->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) && defined(UNDER_CE) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WINCE_THREAD_HPP diff --git a/ext/asio/detail/winsock_init.hpp b/ext/asio/detail/winsock_init.hpp new file mode 100644 index 0000000000..ae5c4bf598 --- /dev/null +++ b/ext/asio/detail/winsock_init.hpp @@ -0,0 +1,120 @@ +// +// winsock_init.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 ASIO_DETAIL_WINSOCK_INIT_HPP +#define ASIO_DETAIL_WINSOCK_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/config.hpp> +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" +#include <boost/shared_ptr.hpp> +#include <boost/throw_exception.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +template <int Major = 2, int Minor = 0> +class winsock_init + : private noncopyable +{ +private: + // Structure to perform the actual initialisation. + struct do_init + { + do_init() + { + WSADATA wsa_data; + result_ = ::WSAStartup(MAKEWORD(Major, Minor), &wsa_data); + } + + ~do_init() + { + ::WSACleanup(); + } + + int result() const + { + return result_; + } + + // Helper function to manage a do_init singleton. The static instance of the + // winsock_init object ensures that this function is always called before + // main, and therefore before any other threads can get started. The do_init + // instance must be static in this function to ensure that it gets + // initialised before any other global objects try to use it. + static boost::shared_ptr<do_init> instance() + { + static boost::shared_ptr<do_init> init(new do_init); + return init; + } + + private: + int result_; + }; + +public: + // Constructor. + winsock_init() + : ref_(do_init::instance()) + { + // Check whether winsock was successfully initialised. This check is not + // performed for the global instance since there will be nobody around to + // catch the exception. + if (this != &instance_ && ref_->result() != 0) + { + asio::system_error e( + asio::error_code(ref_->result(), + asio::error::get_system_category()), + "winsock"); + boost::throw_exception(e); + } + } + + // Destructor. + ~winsock_init() + { + } + +private: + // Instance to force initialisation of winsock at global scope. + static winsock_init instance_; + + // Reference to singleton do_init object to ensure that winsock does not get + // cleaned up until the last user has finished with it. + boost::shared_ptr<do_init> ref_; +}; + +template <int Major, int Minor> +winsock_init<Major, Minor> winsock_init<Major, Minor>::instance_; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WINSOCK_INIT_HPP diff --git a/ext/asio/detail/wrapped_handler.hpp b/ext/asio/detail/wrapped_handler.hpp new file mode 100644 index 0000000000..e40a7f1585 --- /dev/null +++ b/ext/asio/detail/wrapped_handler.hpp @@ -0,0 +1,209 @@ +// +// wrapped_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 ASIO_DETAIL_WRAPPED_HANDLER_HPP +#define ASIO_DETAIL_WRAPPED_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include <boost/type_traits/add_reference.hpp> +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +namespace asio { +namespace detail { + +template <typename Dispatcher, typename Handler> +class wrapped_handler +{ +public: + typedef void result_type; + + wrapped_handler( + typename boost::add_reference<Dispatcher>::type dispatcher, + Handler handler) + : dispatcher_(dispatcher), + handler_(handler) + { + } + + void operator()() + { + dispatcher_.dispatch(handler_); + } + + void operator()() const + { + dispatcher_.dispatch(handler_); + } + + template <typename Arg1> + void operator()(const Arg1& arg1) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); + } + + template <typename Arg1> + void operator()(const Arg1& arg1) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); + } + + template <typename Arg1, typename Arg2> + void operator()(const Arg1& arg1, const Arg2& arg2) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); + } + + template <typename Arg1, typename Arg2> + void operator()(const Arg1& arg1, const Arg2& arg2) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); + } + + template <typename Arg1, typename Arg2, typename Arg3> + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); + } + + template <typename Arg1, typename Arg2, typename Arg3> + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); + } + + template <typename Arg1, typename Arg2, typename Arg3, typename Arg4> + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4) + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); + } + + template <typename Arg1, typename Arg2, typename Arg3, typename Arg4> + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4) const + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); + } + + template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, + typename Arg5> + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4, const Arg5& arg5) + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); + } + + template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, + typename Arg5> + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4, const Arg5& arg5) const + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); + } + +//private: + Dispatcher dispatcher_; + Handler handler_; +}; + +template <typename Handler, typename Context> +class rewrapped_handler +{ +public: + explicit rewrapped_handler(const Handler& handler, const Context& context) + : handler_(handler), + context_(context) + { + } + + void operator()() + { + handler_(); + } + + void operator()() const + { + handler_(); + } + +//private: + Handler handler_; + Context context_; +}; + +template <typename Dispatcher, typename Handler> +inline void* asio_handler_allocate(std::size_t size, + wrapped_handler<Dispatcher, Handler>* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); +} + +template <typename Dispatcher, typename Handler> +inline void asio_handler_deallocate(void* pointer, std::size_t size, + wrapped_handler<Dispatcher, Handler>* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); +} + +template <typename Function, typename Dispatcher, typename Handler> +inline void asio_handler_invoke(const Function& function, + wrapped_handler<Dispatcher, Handler>* this_handler) +{ + this_handler->dispatcher_.dispatch( + rewrapped_handler<Function, Handler>( + function, this_handler->handler_)); +} + +template <typename Handler, typename Context> +inline void* asio_handler_allocate(std::size_t size, + rewrapped_handler<Handler, Context>* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, this_handler->context_); +} + +template <typename Handler, typename Context> +inline void asio_handler_deallocate(void* pointer, std::size_t size, + rewrapped_handler<Handler, Context>* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->context_); +} + +template <typename Function, typename Handler, typename Context> +inline void asio_handler_invoke(const Function& function, + rewrapped_handler<Handler, Context>* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, this_handler->context_); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WRAPPED_HANDLER_HPP |