From 4d46faf0141529681af311fa41deff8d9ada1387 Mon Sep 17 00:00:00 2001 From: Marcos Pinto Date: Thu, 10 Jul 2008 02:25:28 +0000 Subject: [PATCH] asio 1.1.1 sync --- libtorrent/include/asio/basic_io_object.hpp | 12 +- libtorrent/include/asio/basic_raw_socket.hpp | 798 +++++++++++++++++ libtorrent/include/asio/basic_serial_port.hpp | 608 +++++++++++++ libtorrent/include/asio/basic_socket.hpp | 6 +- libtorrent/include/asio/basic_streambuf.hpp | 8 +- libtorrent/include/asio/buffer.hpp | 259 +++++- libtorrent/include/asio/buffers_iterator.hpp | 318 +++++++ .../include/asio/detail/consuming_buffers.hpp | 20 + .../asio/detail/deadline_timer_service.hpp | 13 +- .../include/asio/detail/descriptor_ops.hpp | 138 +++ .../include/asio/detail/dev_poll_reactor.hpp | 127 +-- .../include/asio/detail/epoll_reactor.hpp | 176 ++-- .../asio/detail/handler_base_from_member.hpp | 76 ++ .../include/asio/detail/handler_queue.hpp | 10 + libtorrent/include/asio/detail/hash_map.hpp | 34 +- .../asio/detail/indirect_handler_queue.hpp | 291 ++++++ .../include/asio/detail/kqueue_reactor.hpp | 188 ++-- .../include/asio/detail/null_thread.hpp | 5 +- .../asio/detail/old_win_sdk_compat.hpp | 5 + .../include/asio/detail/posix_thread.hpp | 5 +- .../include/asio/detail/push_options.hpp | 2 + .../detail/reactive_descriptor_service.hpp | 709 +++++++++++++++ .../detail/reactive_serial_port_service.hpp | 267 ++++++ .../asio/detail/reactive_socket_service.hpp | 446 +++++++--- .../include/asio/detail/reactor_op_queue.hpp | 209 +++-- .../include/asio/detail/select_reactor.hpp | 131 ++- .../include/asio/detail/service_registry.hpp | 10 + libtorrent/include/asio/detail/socket_ops.hpp | 16 + .../include/asio/detail/socket_types.hpp | 2 + .../include/asio/detail/strand_service.hpp | 21 +- .../include/asio/detail/task_io_service.hpp | 6 + .../asio/detail/task_io_service_2lock.hpp | 462 ++++++++++ .../include/asio/detail/timer_queue.hpp | 101 ++- .../include/asio/detail/timer_queue_base.hpp | 4 +- .../asio/detail/win_iocp_handle_service.hpp | 832 ++++++++++++++++++ .../asio/detail/win_iocp_io_service.hpp | 41 +- .../detail/win_iocp_serial_port_service.hpp | 292 ++++++ .../asio/detail/win_iocp_socket_service.hpp | 432 +++++++-- libtorrent/include/asio/detail/win_thread.hpp | 73 +- .../include/asio/detail/wince_thread.hpp | 5 +- .../include/asio/detail/wrapped_handler.hpp | 48 +- libtorrent/include/asio/error.hpp | 3 + .../include/asio/handler_alloc_hook.hpp | 2 +- libtorrent/include/asio/impl/read_at.ipp | 337 +++++++ libtorrent/include/asio/impl/read_until.ipp | 374 ++++++-- .../include/asio/impl/serial_port_base.ipp | 541 ++++++++++++ libtorrent/include/asio/impl/write_at.ipp | 296 +++++++ libtorrent/include/asio/io_service.hpp | 32 + libtorrent/include/asio/ip/icmp.hpp | 118 +++ .../include/asio/local/basic_endpoint.hpp | 265 ++++++ .../include/asio/local/connect_pair.hpp | 100 +++ .../include/asio/local/datagram_protocol.hpp | 78 ++ .../include/asio/local/stream_protocol.hpp | 86 ++ .../include/asio/posix/basic_descriptor.hpp | 280 ++++++ .../asio/posix/basic_stream_descriptor.hpp | 304 +++++++ .../include/asio/posix/descriptor_base.hpp | 93 ++ .../include/asio/posix/stream_descriptor.hpp | 39 + .../asio/posix/stream_descriptor_service.hpp | 200 +++++ .../include/asio/raw_socket_service.hpp | 323 +++++++ libtorrent/include/asio/read.hpp | 6 + libtorrent/include/asio/read_at.hpp | 568 ++++++++++++ libtorrent/include/asio/read_until.hpp | 433 ++++++++- libtorrent/include/asio/serial_port.hpp | 38 + libtorrent/include/asio/serial_port_base.hpp | 157 ++++ .../include/asio/serial_port_service.hpp | 222 +++++ .../asio/ssl/detail/openssl_operation.hpp | 9 +- libtorrent/include/asio/thread.hpp | 2 +- libtorrent/include/asio/version.hpp | 2 +- .../include/asio/windows/basic_handle.hpp | 211 +++++ .../windows/basic_random_access_handle.hpp | 320 +++++++ .../asio/windows/basic_stream_handle.hpp | 302 +++++++ .../asio/windows/random_access_handle.hpp | 39 + .../windows/random_access_handle_service.hpp | 179 ++++ .../include/asio/windows/stream_handle.hpp | 39 + .../asio/windows/stream_handle_service.hpp | 177 ++++ libtorrent/include/asio/write.hpp | 11 +- libtorrent/include/asio/write_at.hpp | 555 ++++++++++++ 77 files changed, 13277 insertions(+), 670 deletions(-) create mode 100644 libtorrent/include/asio/basic_raw_socket.hpp create mode 100644 libtorrent/include/asio/basic_serial_port.hpp create mode 100644 libtorrent/include/asio/buffers_iterator.hpp create mode 100644 libtorrent/include/asio/detail/descriptor_ops.hpp create mode 100644 libtorrent/include/asio/detail/handler_base_from_member.hpp create mode 100644 libtorrent/include/asio/detail/indirect_handler_queue.hpp create mode 100644 libtorrent/include/asio/detail/reactive_descriptor_service.hpp create mode 100644 libtorrent/include/asio/detail/reactive_serial_port_service.hpp create mode 100644 libtorrent/include/asio/detail/task_io_service_2lock.hpp create mode 100644 libtorrent/include/asio/detail/win_iocp_handle_service.hpp create mode 100644 libtorrent/include/asio/detail/win_iocp_serial_port_service.hpp create mode 100644 libtorrent/include/asio/impl/read_at.ipp create mode 100644 libtorrent/include/asio/impl/serial_port_base.ipp create mode 100644 libtorrent/include/asio/impl/write_at.ipp create mode 100644 libtorrent/include/asio/ip/icmp.hpp create mode 100644 libtorrent/include/asio/local/basic_endpoint.hpp create mode 100644 libtorrent/include/asio/local/connect_pair.hpp create mode 100644 libtorrent/include/asio/local/datagram_protocol.hpp create mode 100644 libtorrent/include/asio/local/stream_protocol.hpp create mode 100644 libtorrent/include/asio/posix/basic_descriptor.hpp create mode 100644 libtorrent/include/asio/posix/basic_stream_descriptor.hpp create mode 100644 libtorrent/include/asio/posix/descriptor_base.hpp create mode 100644 libtorrent/include/asio/posix/stream_descriptor.hpp create mode 100644 libtorrent/include/asio/posix/stream_descriptor_service.hpp create mode 100644 libtorrent/include/asio/raw_socket_service.hpp create mode 100644 libtorrent/include/asio/read_at.hpp create mode 100644 libtorrent/include/asio/serial_port.hpp create mode 100644 libtorrent/include/asio/serial_port_base.hpp create mode 100644 libtorrent/include/asio/serial_port_service.hpp create mode 100644 libtorrent/include/asio/windows/basic_handle.hpp create mode 100644 libtorrent/include/asio/windows/basic_random_access_handle.hpp create mode 100644 libtorrent/include/asio/windows/basic_stream_handle.hpp create mode 100644 libtorrent/include/asio/windows/random_access_handle.hpp create mode 100644 libtorrent/include/asio/windows/random_access_handle_service.hpp create mode 100644 libtorrent/include/asio/windows/stream_handle.hpp create mode 100644 libtorrent/include/asio/windows/stream_handle_service.hpp create mode 100644 libtorrent/include/asio/write_at.hpp diff --git a/libtorrent/include/asio/basic_io_object.hpp b/libtorrent/include/asio/basic_io_object.hpp index 790498d46..6e369ab4d 100644 --- a/libtorrent/include/asio/basic_io_object.hpp +++ b/libtorrent/include/asio/basic_io_object.hpp @@ -63,6 +63,10 @@ public: protected: /// Construct a basic_io_object. + /** + * Performs: + * @code service.construct(implementation); @endcode + */ explicit basic_io_object(asio::io_service& io_service) : service(asio::use_service(io_service)) { @@ -70,15 +74,19 @@ protected: } /// Protected destructor to prevent deletion through this type. + /** + * Performs: + * @code service.destroy(implementation); @endcode + */ ~basic_io_object() { service.destroy(implementation); } - // The backend service implementation. + /// The service associated with the I/O object. service_type& service; - // The underlying native implementation. + /// The underlying implementation of the I/O object. implementation_type implementation; }; diff --git a/libtorrent/include/asio/basic_raw_socket.hpp b/libtorrent/include/asio/basic_raw_socket.hpp new file mode 100644 index 000000000..5e2f033bd --- /dev/null +++ b/libtorrent/include/asio/basic_raw_socket.hpp @@ -0,0 +1,798 @@ +// +// basic_raw_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_BASIC_RAW_SOCKET_HPP +#define ASIO_BASIC_RAW_SOCKET_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 +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_socket.hpp" +#include "asio/raw_socket_service.hpp" +#include "asio/error.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { + +/// Provides raw-oriented socket functionality. +/** + * The basic_raw_socket class template provides asynchronous and blocking + * raw-oriented socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template > +class basic_raw_socket + : public basic_socket +{ +public: + /// The native representation of a socket. + typedef typename RawSocketService::native_type native_type; + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_raw_socket without opening it. + /** + * This constructor creates a raw socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param io_service The io_service object that the raw socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + */ + explicit basic_raw_socket(asio::io_service& io_service) + : basic_socket(io_service) + { + } + + /// Construct and open a basic_raw_socket. + /** + * This constructor creates and opens a raw socket. + * + * @param io_service The io_service object that the raw socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_raw_socket(asio::io_service& io_service, + const protocol_type& protocol) + : basic_socket(io_service, protocol) + { + } + + /// Construct a basic_raw_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a raw socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param io_service The io_service object that the raw socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param endpoint An endpoint on the local machine to which the raw + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_raw_socket(asio::io_service& io_service, + const endpoint_type& endpoint) + : basic_socket(io_service, endpoint) + { + } + + /// Construct a basic_raw_socket on an existing native socket. + /** + * This constructor creates a raw socket object to hold an existing + * native socket. + * + * @param io_service The io_service object that the raw socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_raw_socket(asio::io_service& io_service, + const protocol_type& protocol, const native_type& native_socket) + : basic_socket( + io_service, protocol, native_socket) + { + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected raw socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code socket.send(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.send(this->implementation, buffers, 0, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected raw socket. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->service.send( + this->implementation, buffers, flags, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected raw socket. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->service.send(this->implementation, buffers, flags, ec); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected raw + * socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send(const ConstBufferSequence& buffers, WriteHandler handler) + { + this->service.async_send(this->implementation, buffers, 0, handler); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to send data on the raw socket. The function call + * will block until the data has been sent successfully or an error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected raw + * socket. + */ + template + void async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, WriteHandler handler) + { + this->service.async_send(this->implementation, buffers, flags, handler); + } + + /// Send raw data to the specified endpoint. + /** + * This function is used to send raw data to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.send_to(asio::buffer(data, size), destination); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination) + { + asio::error_code ec; + std::size_t s = this->service.send_to( + this->implementation, buffers, destination, 0, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Send raw data to the specified endpoint. + /** + * This function is used to send raw data to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->service.send_to( + this->implementation, buffers, destination, flags, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Send raw data to the specified endpoint. + /** + * This function is used to send raw data to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + return this->service.send_to(this->implementation, + buffers, destination, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send raw data to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_send_to( + * asio::buffer(data, size), destination, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, WriteHandler handler) + { + this->service.async_send_to(this->implementation, buffers, destination, 0, + handler); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send raw data to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ + template + void async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + WriteHandler handler) + { + this->service.async_send_to(this->implementation, buffers, destination, + flags, handler); + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the raw socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected raw + * socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.receive(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.receive( + this->implementation, buffers, 0, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the raw socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected raw + * socket. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->service.receive( + this->implementation, buffers, flags, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the raw socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected raw + * socket. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->service.receive(this->implementation, buffers, flags, ec); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the raw + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * raw socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive(const MutableBufferSequence& buffers, ReadHandler handler) + { + this->service.async_receive(this->implementation, buffers, 0, handler); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the raw + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * raw socket. + */ + template + void async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, ReadHandler handler) + { + this->service.async_receive(this->implementation, buffers, flags, handler); + } + + /// Receive raw data with the endpoint of the sender. + /** + * This function is used to receive raw data. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * asio::ip::udp::endpoint sender_endpoint; + * socket.receive_from( + * asio::buffer(data, size), sender_endpoint); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint) + { + asio::error_code ec; + std::size_t s = this->service.receive_from( + this->implementation, buffers, sender_endpoint, 0, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Receive raw data with the endpoint of the sender. + /** + * This function is used to receive raw data. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->service.receive_from( + this->implementation, buffers, sender_endpoint, flags, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Receive raw data with the endpoint of the sender. + /** + * This function is used to receive raw data. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + return this->service.receive_from(this->implementation, buffers, + sender_endpoint, flags, ec); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive raw data. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.async_receive_from( + * asio::buffer(data, size), 0, sender_endpoint, handler); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, ReadHandler handler) + { + this->service.async_receive_from(this->implementation, buffers, + sender_endpoint, 0, handler); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive raw data. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the data. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ + template + void async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + ReadHandler handler) + { + this->service.async_receive_from(this->implementation, buffers, + sender_endpoint, flags, handler); + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_RAW_SOCKET_HPP diff --git a/libtorrent/include/asio/basic_serial_port.hpp b/libtorrent/include/asio/basic_serial_port.hpp new file mode 100644 index 000000000..ffe2b0213 --- /dev/null +++ b/libtorrent/include/asio/basic_serial_port.hpp @@ -0,0 +1,608 @@ +// +// basic_serial_port.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_BASIC_SERIAL_PORT_HPP +#define ASIO_BASIC_SERIAL_PORT_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 +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_io_object.hpp" +#include "asio/error.hpp" +#include "asio/serial_port_base.hpp" +#include "asio/serial_port_service.hpp" +#include "asio/detail/throw_error.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { + +/// Provides serial port functionality. +/** + * The basic_serial_port class template provides functionality that is common + * to all serial ports. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_serial_port + : public basic_io_object, + public serial_port_base +{ +public: + /// The native representation of a serial port. + typedef typename SerialPortService::native_type native_type; + + /// A basic_serial_port is always the lowest layer. + typedef basic_serial_port lowest_layer_type; + + /// Construct a basic_serial_port without opening it. + /** + * This constructor creates a serial port without opening it. + * + * @param io_service The io_service object that the serial port will use to + * dispatch handlers for any asynchronous operations performed on the port. + */ + explicit basic_serial_port(asio::io_service& io_service) + : basic_io_object(io_service) + { + } + + /// Construct and open a basic_serial_port. + /** + * This constructor creates and opens a serial port for the specified device + * name. + * + * @param io_service The io_service object that the serial port will use to + * dispatch handlers for any asynchronous operations performed on the port. + * + * @param device The platform-specific device name for this serial + * port. + */ + explicit basic_serial_port(asio::io_service& io_service, + const char* device) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.open(this->implementation, device, ec); + asio::detail::throw_error(ec); + } + + /// Construct and open a basic_serial_port. + /** + * This constructor creates and opens a serial port for the specified device + * name. + * + * @param io_service The io_service object that the serial port will use to + * dispatch handlers for any asynchronous operations performed on the port. + * + * @param device The platform-specific device name for this serial + * port. + */ + explicit basic_serial_port(asio::io_service& io_service, + const std::string& device) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.open(this->implementation, device, ec); + asio::detail::throw_error(ec); + } + + /// Construct a basic_serial_port on an existing native serial port. + /** + * This constructor creates a serial port object to hold an existing native + * serial port. + * + * @param io_service The io_service object that the serial port will use to + * dispatch handlers for any asynchronous operations performed on the port. + * + * @param native_serial_port A native serial port. + * + * @throws asio::system_error Thrown on failure. + */ + basic_serial_port(asio::io_service& io_service, + const native_type& native_serial_port) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.assign(this->implementation, native_serial_port, ec); + asio::detail::throw_error(ec); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_serial_port cannot contain any further layers, it + * simply returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Open the serial port using the specified device name. + /** + * This function opens the serial port for the specified device name. + * + * @param device The platform-specific device name. + * + * @throws asio::system_error Thrown on failure. + */ + void open(const std::string& device) + { + asio::error_code ec; + this->service.open(this->implementation, device, ec); + asio::detail::throw_error(ec); + } + + /// Open the serial port using the specified device name. + /** + * This function opens the serial port using the given platform-specific + * device name. + * + * @param device The platform-specific device name. + * + * @param ec Set the indicate what error occurred, if any. + */ + asio::error_code open(const std::string& device, + asio::error_code& ec) + { + return this->service.open(this->implementation, device, ec); + } + + /// Assign an existing native serial port to the serial port. + /* + * This function opens the serial port to hold an existing native serial port. + * + * @param native_serial_port A native serial port. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const native_type& native_serial_port) + { + asio::error_code ec; + this->service.assign(this->implementation, native_serial_port, ec); + asio::detail::throw_error(ec); + } + + /// Assign an existing native serial port to the serial port. + /* + * This function opens the serial port to hold an existing native serial port. + * + * @param native_serial_port A native serial port. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code assign(const native_type& native_serial_port, + asio::error_code& ec) + { + return this->service.assign(this->implementation, native_serial_port, ec); + } + + /// Determine whether the serial port is open. + bool is_open() const + { + return this->service.is_open(this->implementation); + } + + /// Close the serial port. + /** + * This function is used to close the serial port. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + this->service.close(this->implementation, ec); + asio::detail::throw_error(ec); + } + + /// Close the serial port. + /** + * This function is used to close the serial port. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code close(asio::error_code& ec) + { + return this->service.close(this->implementation, ec); + } + + /// Get the native serial port representation. + /** + * This function may be used to obtain the underlying representation of the + * serial port. This is intended to allow access to native serial port + * functionality that is not otherwise provided. + */ + native_type native() + { + return this->service.native(this->implementation); + } + + /// Cancel all asynchronous operations associated with the serial port. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->service.cancel(this->implementation, ec); + asio::detail::throw_error(ec); + } + + /// Cancel all asynchronous operations associated with the serial port. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code cancel(asio::error_code& ec) + { + return this->service.cancel(this->implementation, ec); + } + + /// Send a break sequence to the serial port. + /** + * This function causes a break sequence of platform-specific duration to be + * sent out the serial port. + * + * @throws asio::system_error Thrown on failure. + */ + void send_break() + { + asio::error_code ec; + this->service.send_break(this->implementation, ec); + asio::detail::throw_error(ec); + } + + /// Send a break sequence to the serial port. + /** + * This function causes a break sequence of platform-specific duration to be + * sent out the serial port. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code send_break(asio::error_code& ec) + { + return this->service.send_break(this->implementation, ec); + } + + /// Set an option on the serial port. + /** + * This function is used to set an option on the serial port. + * + * @param option The option value to be set on the serial port. + * + * @throws asio::system_error Thrown on failure. + * + * @sa SettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + void set_option(const SettableSerialPortOption& option) + { + asio::error_code ec; + this->service.set_option(this->implementation, option, ec); + asio::detail::throw_error(ec); + } + + /// Set an option on the serial port. + /** + * This function is used to set an option on the serial port. + * + * @param option The option value to be set on the serial port. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + asio::error_code set_option(const SettableSerialPortOption& option, + asio::error_code& ec) + { + return this->service.set_option(this->implementation, option, ec); + } + + /// Get an option from the serial port. + /** + * This function is used to get the current value of an option on the serial + * port. + * + * @param option The option value to be obtained from the serial port. + * + * @throws asio::system_error Thrown on failure. + * + * @sa GettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + void get_option(GettableSerialPortOption& option) + { + asio::error_code ec; + this->service.get_option(this->implementation, option, ec); + asio::detail::throw_error(ec); + } + + /// Get an option from the serial port. + /** + * This function is used to get the current value of an option on the serial + * port. + * + * @param option The option value to be obtained from the serial port. + * + * @param ec Set to indicate what error occured, if any. + * + * @sa GettableSerialPortOption @n + * asio::serial_port_base::baud_rate @n + * asio::serial_port_base::flow_control @n + * asio::serial_port_base::parity @n + * asio::serial_port_base::stop_bits @n + * asio::serial_port_base::character_size + */ + template + asio::error_code get_option(GettableSerialPortOption& option, + asio::error_code& ec) + { + return this->service.get_option(this->implementation, option, ec); + } + + /// Write some data to the serial port. + /** + * This function is used to write data to the serial port. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the serial port. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * serial_port.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.write_some(this->implementation, buffers, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Write some data to the serial port. + /** + * This function is used to write data to the serial port. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the serial port. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return this->service.write_some(this->implementation, buffers, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the serial port. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the serial port. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * serial_port.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_write_some(const ConstBufferSequence& buffers, + WriteHandler handler) + { + this->service.async_write_some(this->implementation, buffers, handler); + } + + /// Read some data from the serial port. + /** + * This function is used to read data from the serial port. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * serial_port.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.read_some(this->implementation, buffers, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Read some data from the serial port. + /** + * This function is used to read data from the serial port. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return this->service.read_some(this->implementation, buffers, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the serial port. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * serial_port.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_read_some(const MutableBufferSequence& buffers, + ReadHandler handler) + { + this->service.async_read_some(this->implementation, buffers, handler); + } +}; + +} // namespace asio + +#endif // defined(ASIO_HAS_SERIAL_PORT) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SERIAL_PORT_HPP diff --git a/libtorrent/include/asio/basic_socket.hpp b/libtorrent/include/asio/basic_socket.hpp index 5335614fa..f494c1725 100644 --- a/libtorrent/include/asio/basic_socket.hpp +++ b/libtorrent/include/asio/basic_socket.hpp @@ -514,7 +514,7 @@ public: * * The socket is automatically opened if it is not already open. If the * connect fails, and the socket was automatically opened, the socket is - * returned to the closed state. + * not returned to the closed state. * * @param peer_endpoint The remote endpoint to which the socket will be * connected. @@ -549,7 +549,7 @@ public: * * The socket is automatically opened if it is not already open. If the * connect fails, and the socket was automatically opened, the socket is - * returned to the closed state. + * not returned to the closed state. * * @param peer_endpoint The remote endpoint to which the socket will be * connected. @@ -591,7 +591,7 @@ public: * * The socket is automatically opened if it is not already open. If the * connect fails, and the socket was automatically opened, the socket is - * returned to the closed state. + * not returned to the closed state. * * @param peer_endpoint The remote endpoint to which the socket will be * connected. Copies will be made of the endpoint object as required. diff --git a/libtorrent/include/asio/basic_streambuf.hpp b/libtorrent/include/asio/basic_streambuf.hpp index 5b14f5ef8..41649cc43 100644 --- a/libtorrent/include/asio/basic_streambuf.hpp +++ b/libtorrent/include/asio/basic_streambuf.hpp @@ -100,11 +100,9 @@ public: /// Move the start of the get area by the specified number of characters. void consume(std::size_t n) { - while (n > 0) - { - sbumpc(); - --n; - } + if (gptr() + n > pptr()) + n = pptr() - gptr(); + gbump(static_cast(n)); } protected: diff --git a/libtorrent/include/asio/buffer.hpp b/libtorrent/include/asio/buffer.hpp index 99fb1e2c8..7bd04a811 100644 --- a/libtorrent/include/asio/buffer.hpp +++ b/libtorrent/include/asio/buffer.hpp @@ -195,6 +195,12 @@ public: /// A random-access iterator type that may be used to read elements. typedef const mutable_buffer* const_iterator; + /// Construct to represent a given memory range. + mutable_buffers_1(void* data, std::size_t size) + : mutable_buffer(data, size) + { + } + /// Construct to represent a single modifiable buffer. explicit mutable_buffers_1(const mutable_buffer& b) : mutable_buffer(b) @@ -359,6 +365,12 @@ public: /// A random-access iterator type that may be used to read elements. typedef const const_buffer* const_iterator; + /// Construct to represent a given memory range. + const_buffers_1(const void* data, std::size_t size) + : const_buffer(data, size) + { + } + /// Construct to represent a single non-modifiable buffer. explicit const_buffers_1(const const_buffer& b) : const_buffer(b) @@ -378,6 +390,33 @@ public: } }; +/// An implementation of both the ConstBufferSequence and MutableBufferSequence +/// concepts to represent a null buffer sequence. +class null_buffers +{ +public: + /// The type for each element in the list of buffers. + typedef mutable_buffer value_type; + + /// A random-access iterator type that may be used to read elements. + typedef const mutable_buffer* const_iterator; + + /// Get a random-access iterator to the first element. + const_iterator begin() const + { + return &buf_; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const + { + return &buf_; + } + +private: + mutable_buffer buf_; +}; + #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) namespace detail { @@ -415,12 +454,22 @@ private: /** @defgroup buffer asio::buffer * * @brief The asio::buffer function is used to create a buffer object to - * represent raw memory, an array of POD elements, or a vector of POD elements. + * represent raw memory, an array of POD elements, a vector of POD elements, + * or a std::string. + * + * A buffer object represents a contiguous region of memory as a 2-tuple + * consisting of a pointer and size in bytes. A tuple of the form {void*, + * size_t} specifies a mutable (modifiable) region of memory. Similarly, a + * tuple of the form {const void*, size_t} specifies a const + * (non-modifiable) region of memory. These two forms correspond to the classes + * mutable_buffer and const_buffer, respectively. To mirror C++'s conversion + * rules, a mutable_buffer is implicitly convertible to a const_buffer, and the + * opposite conversion is not permitted. * * The simplest use case involves reading or writing a single buffer of a * specified size: * - * @code sock.write(asio::buffer(data, size)); @endcode + * @code sock.send(asio::buffer(data, size)); @endcode * * In the above example, the return value of asio::buffer meets the * requirements of the ConstBufferSequence concept so that it may be directly @@ -432,13 +481,90 @@ private: * automatically determining the size of the buffer: * * @code char d1[128]; - * size_t bytes_transferred = sock.read(asio::buffer(d1)); + * size_t bytes_transferred = sock.receive(asio::buffer(d1)); * * std::vector d2(128); - * bytes_transferred = sock.read(asio::buffer(d2)); + * bytes_transferred = sock.receive(asio::buffer(d2)); * * boost::array d3; - * bytes_transferred = sock.read(asio::buffer(d3)); @endcode + * bytes_transferred = sock.receive(asio::buffer(d3)); @endcode + * + * In all three cases above, the buffers created are exactly 128 bytes long. + * Note that a vector is @e never automatically resized when creating or using + * a buffer. The buffer size is determined using the vector's size() + * member function, and not its capacity. + * + * @par Accessing Buffer Contents + * + * The contents of a buffer may be accessed using the asio::buffer_size + * and asio::buffer_cast functions: + * + * @code asio::mutable_buffer b1 = ...; + * std::size_t s1 = asio::buffer_size(b1); + * unsigned char* p1 = asio::buffer_cast(b1); + * + * asio::const_buffer b2 = ...; + * std::size_t s2 = asio::buffer_size(b2); + * const void* p2 = asio::buffer_cast(b2); @endcode + * + * The asio::buffer_cast function permits violations of type safety, so + * uses of it in application code should be carefully considered. + * + * @par Buffer Invalidation + * + * A buffer object does not have any ownership of the memory it refers to. It + * is the responsibility of the application to ensure the memory region remains + * valid until it is no longer required for an I/O operation. When the memory + * is no longer available, the buffer is said to have been invalidated. + * + * For the asio::buffer overloads that accept an argument of type + * std::vector, the buffer objects returned are invalidated by any vector + * operation that also invalidates all references, pointers and iterators + * referring to the elements in the sequence (C++ Std, 23.2.4) + * + * For the asio::buffer overloads that accept an argument of type + * std::string, the buffer objects returned are invalidated according to the + * rules defined for invalidation of references, pointers and iterators + * referring to elements of the sequence (C++ Std, 21.3). + * + * @par Buffer Arithmetic + * + * Buffer objects may be manipulated using simple arithmetic in a safe way + * which helps prevent buffer overruns. Consider an array initialised as + * follows: + * + * @code boost::array a = { 'a', 'b', 'c', 'd', 'e' }; @endcode + * + * A buffer object @c b1 created using: + * + * @code b1 = asio::buffer(a); @endcode + * + * represents the entire array, { 'a', 'b', 'c', 'd', 'e' }. An + * optional second argument to the asio::buffer function may be used to + * limit the size, in bytes, of the buffer: + * + * @code b2 = asio::buffer(a, 3); @endcode + * + * such that @c b2 represents the data { 'a', 'b', 'c' }. Even if the + * size argument exceeds the actual size of the array, the size of the buffer + * object created will be limited to the array size. + * + * An offset may be applied to an existing buffer to create a new one: + * + * @code b3 = b1 + 2; @endcode + * + * where @c b3 will set to represent { 'c', 'd', 'e' }. If the offset + * exceeds the size of the existing buffer, the newly created buffer will be + * empty. + * + * Both an offset and size may be specified to create a buffer that corresponds + * to a specific range of bytes within an existing buffer: + * + * @code b4 = asio::buffer(b1 + 1, 3); @endcode + * + * so that @c b4 will refer to the bytes { 'b', 'c', 'd' }. + * + * @par Buffers and Scatter-Gather I/O * * To read or write using multiple buffers (i.e. scatter-gather I/O), multiple * buffer objects may be assigned into a container that supports the @@ -453,23 +579,32 @@ private: * asio::buffer(d1), * asio::buffer(d2), * asio::buffer(d3) }; - * bytes_transferred = sock.read(bufs1); + * bytes_transferred = sock.receive(bufs1); * * std::vector bufs2; * bufs2.push_back(asio::buffer(d1)); * bufs2.push_back(asio::buffer(d2)); * bufs2.push_back(asio::buffer(d3)); - * bytes_transferred = sock.write(bufs2); @endcode + * bytes_transferred = sock.send(bufs2); @endcode */ /*@{*/ /// Create a new modifiable buffer from an existing buffer. +/** + * @returns mutable_buffers_1(b). + */ inline mutable_buffers_1 buffer(const mutable_buffer& b) { return mutable_buffers_1(b); } /// Create a new modifiable buffer from an existing buffer. +/** + * @returns A mutable_buffers_1 value equivalent to: + * @code mutable_buffers_1( + * buffer_cast(b), + * min(buffer_size(b), max_size_in_bytes)); @endcode + */ inline mutable_buffers_1 buffer(const mutable_buffer& b, std::size_t max_size_in_bytes) { @@ -484,12 +619,21 @@ inline mutable_buffers_1 buffer(const mutable_buffer& b, } /// Create a new non-modifiable buffer from an existing buffer. +/** + * @returns const_buffers_1(b). + */ inline const_buffers_1 buffer(const const_buffer& b) { return const_buffers_1(b); } /// Create a new non-modifiable buffer from an existing buffer. +/** + * @returns A const_buffers_1 value equivalent to: + * @code const_buffers_1( + * buffer_cast(b), + * min(buffer_size(b), max_size_in_bytes)); @endcode + */ inline const_buffers_1 buffer(const const_buffer& b, std::size_t max_size_in_bytes) { @@ -504,12 +648,18 @@ inline const_buffers_1 buffer(const const_buffer& b, } /// Create a new modifiable buffer that represents the given memory range. +/** + * @returns mutable_buffers_1(data, size_in_bytes). + */ inline mutable_buffers_1 buffer(void* data, std::size_t size_in_bytes) { return mutable_buffers_1(mutable_buffer(data, size_in_bytes)); } /// Create a new non-modifiable buffer that represents the given memory range. +/** + * @returns const_buffers_1(data, size_in_bytes). + */ inline const_buffers_1 buffer(const void* data, std::size_t size_in_bytes) { @@ -517,6 +667,12 @@ inline const_buffers_1 buffer(const void* data, } /// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffers_1 value equivalent to: + * @code mutable_buffers_1( + * static_cast(data), + * N * sizeof(PodType)); @endcode + */ template inline mutable_buffers_1 buffer(PodType (&data)[N]) { @@ -524,6 +680,12 @@ inline mutable_buffers_1 buffer(PodType (&data)[N]) } /// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffers_1 value equivalent to: + * @code mutable_buffers_1( + * static_cast(data), + * min(N * sizeof(PodType), max_size_in_bytes)); @endcode + */ template inline mutable_buffers_1 buffer(PodType (&data)[N], std::size_t max_size_in_bytes) @@ -535,6 +697,12 @@ inline mutable_buffers_1 buffer(PodType (&data)[N], } /// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffers_1 value equivalent to: + * @code const_buffers_1( + * static_cast(data), + * N * sizeof(PodType)); @endcode + */ template inline const_buffers_1 buffer(const PodType (&data)[N]) { @@ -542,6 +710,12 @@ inline const_buffers_1 buffer(const PodType (&data)[N]) } /// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffers_1 value equivalent to: + * @code const_buffers_1( + * static_cast(data), + * min(N * sizeof(PodType), max_size_in_bytes)); @endcode + */ template inline const_buffers_1 buffer(const PodType (&data)[N], std::size_t max_size_in_bytes) @@ -624,6 +798,12 @@ buffer(boost::array& data, std::size_t max_size_in_bytes) // || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) /// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffers_1 value equivalent to: + * @code mutable_buffers_1( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ template inline mutable_buffers_1 buffer(boost::array& data) { @@ -632,6 +812,12 @@ inline mutable_buffers_1 buffer(boost::array& data) } /// Create a new modifiable buffer that represents the given POD array. +/** + * @returns A mutable_buffers_1 value equivalent to: + * @code mutable_buffers_1( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ template inline mutable_buffers_1 buffer(boost::array& data, std::size_t max_size_in_bytes) @@ -643,6 +829,12 @@ inline mutable_buffers_1 buffer(boost::array& data, } /// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffers_1 value equivalent to: + * @code const_buffers_1( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ template inline const_buffers_1 buffer(boost::array& data) { @@ -651,6 +843,12 @@ inline const_buffers_1 buffer(boost::array& data) } /// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffers_1 value equivalent to: + * @code const_buffers_1( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ template inline const_buffers_1 buffer(boost::array& data, std::size_t max_size_in_bytes) @@ -665,6 +863,12 @@ inline const_buffers_1 buffer(boost::array& data, // || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) /// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffers_1 value equivalent to: + * @code const_buffers_1( + * data.data(), + * data.size() * sizeof(PodType)); @endcode + */ template inline const_buffers_1 buffer(const boost::array& data) { @@ -673,6 +877,12 @@ inline const_buffers_1 buffer(const boost::array& data) } /// Create a new non-modifiable buffer that represents the given POD array. +/** + * @returns A const_buffers_1 value equivalent to: + * @code const_buffers_1( + * data.data(), + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + */ template inline const_buffers_1 buffer(const boost::array& data, std::size_t max_size_in_bytes) @@ -685,6 +895,11 @@ inline const_buffers_1 buffer(const boost::array& data, /// Create a new modifiable buffer that represents the given POD vector. /** + * @returns A mutable_buffers_1 value equivalent to: + * @code mutable_buffers_1( + * data.size() ? &data[0] : 0, + * data.size() * sizeof(PodType)); @endcode + * * @note The buffer is invalidated by any vector operation that would also * invalidate iterators. */ @@ -692,7 +907,7 @@ template inline mutable_buffers_1 buffer(std::vector& data) { return mutable_buffers_1( - mutable_buffer(&data[0], data.size() * sizeof(PodType) + mutable_buffer(data.size() ? &data[0] : 0, data.size() * sizeof(PodType) #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , detail::buffer_debug_check< typename std::vector::iterator @@ -703,6 +918,11 @@ inline mutable_buffers_1 buffer(std::vector& data) /// Create a new modifiable buffer that represents the given POD vector. /** + * @returns A mutable_buffers_1 value equivalent to: + * @code mutable_buffers_1( + * data.size() ? &data[0] : 0, + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + * * @note The buffer is invalidated by any vector operation that would also * invalidate iterators. */ @@ -711,7 +931,7 @@ inline mutable_buffers_1 buffer(std::vector& data, std::size_t max_size_in_bytes) { return mutable_buffers_1( - mutable_buffer(&data[0], + mutable_buffer(data.size() ? &data[0] : 0, data.size() * sizeof(PodType) < max_size_in_bytes ? data.size() * sizeof(PodType) : max_size_in_bytes #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) @@ -724,6 +944,11 @@ inline mutable_buffers_1 buffer(std::vector& data, /// Create a new non-modifiable buffer that represents the given POD vector. /** + * @returns A const_buffers_1 value equivalent to: + * @code const_buffers_1( + * data.size() ? &data[0] : 0, + * data.size() * sizeof(PodType)); @endcode + * * @note The buffer is invalidated by any vector operation that would also * invalidate iterators. */ @@ -732,7 +957,7 @@ inline const_buffers_1 buffer( const std::vector& data) { return const_buffers_1( - const_buffer(&data[0], data.size() * sizeof(PodType) + const_buffer(data.size() ? &data[0] : 0, data.size() * sizeof(PodType) #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) , detail::buffer_debug_check< typename std::vector::const_iterator @@ -743,6 +968,11 @@ inline const_buffers_1 buffer( /// Create a new non-modifiable buffer that represents the given POD vector. /** + * @returns A const_buffers_1 value equivalent to: + * @code const_buffers_1( + * data.size() ? &data[0] : 0, + * min(data.size() * sizeof(PodType), max_size_in_bytes)); @endcode + * * @note The buffer is invalidated by any vector operation that would also * invalidate iterators. */ @@ -751,7 +981,7 @@ inline const_buffers_1 buffer( const std::vector& data, std::size_t max_size_in_bytes) { return const_buffers_1( - const_buffer(&data[0], + const_buffer(data.size() ? &data[0] : 0, data.size() * sizeof(PodType) < max_size_in_bytes ? data.size() * sizeof(PodType) : max_size_in_bytes #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) @@ -764,6 +994,8 @@ inline const_buffers_1 buffer( /// Create a new non-modifiable buffer that represents the given string. /** + * @returns const_buffers_1(data.data(), data.size()). + * * @note The buffer is invalidated by any non-const operation called on the * given string object. */ @@ -778,6 +1010,11 @@ inline const_buffers_1 buffer(const std::string& data) /// Create a new non-modifiable buffer that represents the given string. /** + * @returns A const_buffers_1 value equivalent to: + * @code const_buffers_1( + * data.data(), + * min(data.size(), max_size_in_bytes)); @endcode + * * @note The buffer is invalidated by any non-const operation called on the * given string object. */ diff --git a/libtorrent/include/asio/buffers_iterator.hpp b/libtorrent/include/asio/buffers_iterator.hpp new file mode 100644 index 000000000..cc13bb5e2 --- /dev/null +++ b/libtorrent/include/asio/buffers_iterator.hpp @@ -0,0 +1,318 @@ +// +// buffers_iterator.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_BUFFERS_ITERATOR_HPP +#define ASIO_BUFFERS_ITERATOR_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 +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" + +namespace asio { + +namespace detail +{ + template + struct buffers_iterator_types_helper; + + template <> + struct buffers_iterator_types_helper + { + typedef const_buffer buffer_type; + template + struct byte_type + { + typedef typename boost::add_const::type type; + }; + }; + + template <> + struct buffers_iterator_types_helper + { + typedef mutable_buffer buffer_type; + template + struct byte_type + { + typedef ByteType type; + }; + }; + + template + struct buffers_iterator_types + { + enum + { + is_mutable = boost::is_convertible< + typename BufferSequence::value_type, mutable_buffer>::value + }; + typedef buffers_iterator_types_helper helper; + typedef typename helper::buffer_type buffer_type; + typedef typename helper::template byte_type::type byte_type; + }; +} + +/// A random access iterator over the bytes in a buffer sequence. +template +class buffers_iterator + : public boost::iterator_facade< + buffers_iterator, + typename detail::buffers_iterator_types< + BufferSequence, ByteType>::byte_type, + boost::random_access_traversal_tag> +{ +private: + typedef typename detail::buffers_iterator_types< + BufferSequence, ByteType>::buffer_type buffer_type; + typedef typename detail::buffers_iterator_types< + BufferSequence, ByteType>::byte_type byte_type; + +public: + /// Default constructor. Creates an iterator in an undefined state. + buffers_iterator() + : current_buffer_(), + current_buffer_position_(0), + begin_(), + current_(), + end_(), + position_(0) + { + } + + /// Construct an iterator representing the beginning of the buffers' data. + static buffers_iterator begin(const BufferSequence& buffers) + { + buffers_iterator new_iter; + new_iter.begin_ = buffers.begin(); + new_iter.current_ = buffers.begin(); + new_iter.end_ = buffers.end(); + while (new_iter.current_ != new_iter.end_) + { + new_iter.current_buffer_ = *new_iter.current_; + if (asio::buffer_size(new_iter.current_buffer_) > 0) + break; + ++new_iter.current_; + } + return new_iter; + } + + /// Construct an iterator representing the end of the buffers' data. + static buffers_iterator end(const BufferSequence& buffers) + { + buffers_iterator new_iter; + new_iter.begin_ = buffers.begin(); + new_iter.current_ = buffers.begin(); + new_iter.end_ = buffers.end(); + while (new_iter.current_ != new_iter.end_) + { + buffer_type buffer = *new_iter.current_; + new_iter.position_ += asio::buffer_size(buffer); + ++new_iter.current_; + } + return new_iter; + } + +private: + friend class boost::iterator_core_access; + + // Dereference the iterator. + byte_type& dereference() const + { + return buffer_cast(current_buffer_)[current_buffer_position_]; + } + + // Compare two iterators for equality. + bool equal(const buffers_iterator& other) const + { + return position_ == other.position_; + } + + // Increment the iterator. + void increment() + { + BOOST_ASSERT(current_ != end_ && "iterator out of bounds"); + ++position_; + + // Check if the increment can be satisfied by the current buffer. + ++current_buffer_position_; + if (current_buffer_position_ != asio::buffer_size(current_buffer_)) + return; + + // Find the next non-empty buffer. + ++current_; + current_buffer_position_ = 0; + while (current_ != end_) + { + current_buffer_ = *current_; + if (asio::buffer_size(current_buffer_) > 0) + return; + ++current_; + } + } + + // Decrement the iterator. + void decrement() + { + BOOST_ASSERT(position_ > 0 && "iterator out of bounds"); + --position_; + + // Check if the decrement can be satisfied by the current buffer. + if (current_buffer_position_ != 0) + { + --current_buffer_position_; + return; + } + + // Find the previous non-empty buffer. + typename BufferSequence::const_iterator iter = current_; + while (iter != begin_) + { + --iter; + buffer_type buffer = *iter; + std::size_t buffer_size = asio::buffer_size(buffer); + if (buffer_size > 0) + { + current_ = iter; + current_buffer_ = buffer; + current_buffer_position_ = buffer_size - 1; + return; + } + } + } + + // Advance the iterator by the specified distance. + void advance(std::ptrdiff_t n) + { + if (n > 0) + { + BOOST_ASSERT(current_ != end_ && "iterator out of bounds"); + for (;;) + { + std::ptrdiff_t current_buffer_balance + = asio::buffer_size(current_buffer_) + - current_buffer_position_; + + // Check if the advance can be satisfied by the current buffer. + if (current_buffer_balance > n) + { + position_ += n; + current_buffer_position_ += n; + return; + } + + // Update position. + n -= current_buffer_balance; + position_ += current_buffer_balance; + + // Move to next buffer. If it is empty then it will be skipped on the + // next iteration of this loop. + if (++current_ == end_) + { + BOOST_ASSERT(n == 0 && "iterator out of bounds"); + current_buffer_ = buffer_type(); + current_buffer_position_ = 0; + return; + } + current_buffer_ = *current_; + current_buffer_position_ = 0; + } + } + else if (n < 0) + { + std::size_t abs_n = -n; + BOOST_ASSERT(position_ >= abs_n && "iterator out of bounds"); + for (;;) + { + // Check if the advance can be satisfied by the current buffer. + if (current_buffer_position_ >= abs_n) + { + position_ -= abs_n; + current_buffer_position_ -= abs_n; + return; + } + + // Update position. + abs_n -= current_buffer_position_; + position_ -= current_buffer_position_; + + // Check if we've reached the beginning of the buffers. + if (current_ == begin_) + { + BOOST_ASSERT(abs_n == 0 && "iterator out of bounds"); + current_buffer_position_ = 0; + return; + } + + // Find the previous non-empty buffer. + typename BufferSequence::const_iterator iter = current_; + while (iter != begin_) + { + --iter; + buffer_type buffer = *iter; + std::size_t buffer_size = asio::buffer_size(buffer); + if (buffer_size > 0) + { + current_ = iter; + current_buffer_ = buffer; + current_buffer_position_ = buffer_size; + break; + } + } + } + } + } + + // Determine the distance between two iterators. + std::ptrdiff_t distance_to(const buffers_iterator& other) const + { + return other.position_ - position_; + } + + buffer_type current_buffer_; + std::size_t current_buffer_position_; + typename BufferSequence::const_iterator begin_; + typename BufferSequence::const_iterator current_; + typename BufferSequence::const_iterator end_; + std::size_t position_; +}; + +/// Construct an iterator representing the beginning of the buffers' data. +template +inline buffers_iterator buffers_begin( + const BufferSequence& buffers) +{ + return buffers_iterator::begin(buffers); +} + +/// Construct an iterator representing the end of the buffers' data. +template +inline buffers_iterator buffers_end( + const BufferSequence& buffers) +{ + return buffers_iterator::end(buffers); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERS_ITERATOR_HPP diff --git a/libtorrent/include/asio/detail/consuming_buffers.hpp b/libtorrent/include/asio/detail/consuming_buffers.hpp index d598b0129..8e88e361d 100644 --- a/libtorrent/include/asio/detail/consuming_buffers.hpp +++ b/libtorrent/include/asio/detail/consuming_buffers.hpp @@ -24,6 +24,8 @@ #include #include "asio/detail/pop_options.hpp" +#include "asio/buffer.hpp" + namespace asio { namespace detail { @@ -197,6 +199,24 @@ private: typename Buffers::const_iterator begin_remainder_; }; +// Specialisation for null_buffers to ensure that the null_buffers type is +// always passed through to the underlying read or write operation. +template +class consuming_buffers + : public asio::null_buffers +{ +public: + consuming_buffers(const asio::null_buffers&) + { + // No-op. + } + + void consume(std::size_t) + { + // No-op. + } +}; + } // namespace detail } // namespace asio diff --git a/libtorrent/include/asio/detail/deadline_timer_service.hpp b/libtorrent/include/asio/detail/deadline_timer_service.hpp index 1acbd15e4..6f30d7546 100644 --- a/libtorrent/include/asio/detail/deadline_timer_service.hpp +++ b/libtorrent/include/asio/detail/deadline_timer_service.hpp @@ -26,6 +26,7 @@ #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_base_from_member.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/service_base.hpp" #include "asio/detail/socket_ops.hpp" @@ -153,25 +154,25 @@ public: } template - class wait_handler + class wait_handler : + public handler_base_from_member { public: wait_handler(asio::io_service& io_service, Handler handler) - : io_service_(io_service), - work_(io_service), - handler_(handler) + : handler_base_from_member(handler), + io_service_(io_service), + work_(io_service) { } void operator()(const asio::error_code& result) { - io_service_.post(detail::bind_handler(handler_, result)); + io_service_.post(detail::bind_handler(this->handler_, result)); } private: asio::io_service& io_service_; asio::io_service::work work_; - Handler handler_; }; // Start an asynchronous wait on the timer. diff --git a/libtorrent/include/asio/detail/descriptor_ops.hpp b/libtorrent/include/asio/detail/descriptor_ops.hpp new file mode 100644 index 000000000..6de34f621 --- /dev/null +++ b/libtorrent/include/asio/detail/descriptor_ops.hpp @@ -0,0 +1,138 @@ +// +// descriptor_ops.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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 +#include +#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 +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); + return error_wrapper(::open(path, flags), ec); +} + +inline int close(int d, asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::close(d), ec); +} + +typedef iovec buf; + +inline void init_buf(buf& b, void* data, size_t size) +{ + b.iov_base = data; + b.iov_len = size; +} + +inline void init_buf(buf& b, const void* data, size_t size) +{ + b.iov_base = const_cast(data); + b.iov_len = size; +} + +inline int scatter_read(int d, buf* bufs, size_t count, + asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::readv(d, bufs, static_cast(count)), ec); +} + +inline int gather_write(int d, const buf* bufs, size_t count, + asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::writev(d, bufs, static_cast(count)), ec); +} + +inline int ioctl(int d, long cmd, ioctl_arg_type* arg, + asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::ioctl(d, cmd, arg), ec); +} + +inline int fcntl(int d, long cmd, asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::fcntl(d, cmd), ec); +} + +inline int fcntl(int d, long cmd, long arg, asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::fcntl(d, cmd, arg), ec); +} + +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); + return error_wrapper(::poll(&fds, 1, -1), ec); +} + +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); + return error_wrapper(::poll(&fds, 1, -1), ec); +} + +} // 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/libtorrent/include/asio/detail/dev_poll_reactor.hpp b/libtorrent/include/asio/detail/dev_poll_reactor.hpp index 1f24c5a1a..d68d8fe72 100644 --- a/libtorrent/include/asio/detail/dev_poll_reactor.hpp +++ b/libtorrent/include/asio/detail/dev_poll_reactor.hpp @@ -53,6 +53,11 @@ class dev_poll_reactor : public asio::detail::service_base > { public: + // Per-descriptor data. + struct per_descriptor_data + { + }; + // Constructor. dev_poll_reactor(asio::io_service& io_service) : asio::detail::service_base< @@ -115,11 +120,11 @@ public: for (std::size_t i = 0; i < timer_queues_.size(); ++i) timer_queues_[i]->destroy_timers(); timer_queues_.clear(); - } + } // Register a socket with the reactor. Returns 0 on success, system error // code on failure. - int register_descriptor(socket_type descriptor) + int register_descriptor(socket_type, per_descriptor_data&) { return 0; } @@ -127,16 +132,27 @@ public: // Start a new read operation. The handler object will be invoked when the // given descriptor is ready to be read, or an error has occurred. template - void start_read_op(socket_type descriptor, Handler handler) + void start_read_op(socket_type descriptor, per_descriptor_data&, + Handler handler, bool allow_speculative_read = true) { asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) return; - if (!read_op_queue_.has_operation(descriptor)) - if (handler(asio::error_code())) - return; + if (allow_speculative_read) + { + if (!read_op_queue_.has_operation(descriptor)) + { + asio::error_code ec; + std::size_t bytes_transferred = 0; + if (handler.perform(ec, bytes_transferred)) + { + handler.complete(ec, bytes_transferred); + return; + } + } + } if (read_op_queue_.enqueue_operation(descriptor, handler)) { @@ -153,16 +169,27 @@ public: // Start a new write operation. The handler object will be invoked when the // given descriptor is ready to be written, or an error has occurred. template - void start_write_op(socket_type descriptor, Handler handler) + void start_write_op(socket_type descriptor, per_descriptor_data&, + Handler handler, bool allow_speculative_write = true) { asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) return; - if (!write_op_queue_.has_operation(descriptor)) - if (handler(asio::error_code())) - return; + if (allow_speculative_write) + { + if (!write_op_queue_.has_operation(descriptor)) + { + asio::error_code ec; + std::size_t bytes_transferred = 0; + if (handler.perform(ec, bytes_transferred)) + { + handler.complete(ec, bytes_transferred); + return; + } + } + } if (write_op_queue_.enqueue_operation(descriptor, handler)) { @@ -179,7 +206,8 @@ public: // Start a new exception operation. The handler object will be invoked when // the given descriptor has exception information, or an error has occurred. template - void start_except_op(socket_type descriptor, Handler handler) + void start_except_op(socket_type descriptor, + per_descriptor_data&, Handler handler) { asio::detail::mutex::scoped_lock lock(mutex_); @@ -198,26 +226,25 @@ public: } } - // Start new write and exception operations. The handler object will be - // invoked when the given descriptor is ready for writing or has exception + // Start a new write operation. The handler object will be invoked when the // information available, or an error has occurred. template - void start_write_and_except_ops(socket_type descriptor, Handler handler) + void start_connect_op(socket_type descriptor, + per_descriptor_data&, Handler handler) { asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) return; - bool need_mod = write_op_queue_.enqueue_operation(descriptor, handler); - need_mod = except_op_queue_.enqueue_operation(descriptor, handler) - && need_mod; - if (need_mod) + if (write_op_queue_.enqueue_operation(descriptor, handler)) { ::pollfd& ev = add_pending_event_change(descriptor); - ev.events = POLLOUT | POLLPRI | POLLERR | POLLHUP; + ev.events = POLLOUT | POLLERR | POLLHUP; if (read_op_queue_.has_operation(descriptor)) ev.events |= POLLIN; + if (except_op_queue_.has_operation(descriptor)) + ev.events |= POLLPRI; interrupter_.interrupt(); } } @@ -225,25 +252,15 @@ public: // 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) + void cancel_ops(socket_type descriptor, per_descriptor_data&) { asio::detail::mutex::scoped_lock lock(mutex_); cancel_ops_unlocked(descriptor); } - // Enqueue cancellation of all operations associated with the given - // descriptor. The handlers associated with the descriptor will be invoked - // with the operation_aborted error. This function does not acquire the - // dev_poll_reactor's mutex, and so should only be used from within a reactor - // handler. - void enqueue_cancel_ops_unlocked(socket_type descriptor) - { - pending_cancellations_.push_back(descriptor); - } - // Cancel any operations that are running against the descriptor and remove // its registration from the reactor. - void close_descriptor(socket_type descriptor) + void close_descriptor(socket_type descriptor, per_descriptor_data&) { asio::detail::mutex::scoped_lock lock(mutex_); @@ -313,16 +330,16 @@ private: // Dispatch any operation cancellations that were made while the select // loop was not running. - read_op_queue_.dispatch_cancellations(); - write_op_queue_.dispatch_cancellations(); - except_op_queue_.dispatch_cancellations(); + read_op_queue_.perform_cancellations(); + write_op_queue_.perform_cancellations(); + except_op_queue_.perform_cancellations(); for (std::size_t i = 0; i < timer_queues_.size(); ++i) timer_queues_[i]->dispatch_cancellations(); // Check if the thread is supposed to stop. if (stop_thread_) { - cleanup_operations_and_timers(lock); + complete_operations_and_timers(lock); return; } @@ -331,7 +348,7 @@ private: if (!block && read_op_queue_.empty() && write_op_queue_.empty() && except_op_queue_.empty() && all_timer_queues_are_empty()) { - cleanup_operations_and_timers(lock); + complete_operations_and_timers(lock); return; } @@ -347,9 +364,9 @@ private: int descriptor = pending_event_changes_[i].fd; asio::error_code ec = asio::error_code( errno, asio::error::get_system_category()); - read_op_queue_.dispatch_all_operations(descriptor, ec); - write_op_queue_.dispatch_all_operations(descriptor, ec); - except_op_queue_.dispatch_all_operations(descriptor, ec); + read_op_queue_.perform_all_operations(descriptor, ec); + write_op_queue_.perform_all_operations(descriptor, ec); + except_op_queue_.perform_all_operations(descriptor, ec); } } pending_event_changes_.clear(); @@ -370,7 +387,7 @@ private: lock.lock(); wait_in_progress_ = false; - // Block signals while dispatching operations. + // Block signals while performing operations. asio::detail::signal_blocker sb; // Dispatch the waiting events. @@ -391,17 +408,17 @@ private: // Exception operations must be processed first to ensure that any // out-of-band data is read before normal data. if (events[i].events & (POLLPRI | POLLERR | POLLHUP)) - more_except = except_op_queue_.dispatch_operation(descriptor, ec); + more_except = except_op_queue_.perform_operation(descriptor, ec); else more_except = except_op_queue_.has_operation(descriptor); if (events[i].events & (POLLIN | POLLERR | POLLHUP)) - more_reads = read_op_queue_.dispatch_operation(descriptor, ec); + more_reads = read_op_queue_.perform_operation(descriptor, ec); else more_reads = read_op_queue_.has_operation(descriptor); if (events[i].events & (POLLOUT | POLLERR | POLLHUP)) - more_writes = write_op_queue_.dispatch_operation(descriptor, ec); + more_writes = write_op_queue_.perform_operation(descriptor, ec); else more_writes = write_op_queue_.has_operation(descriptor); @@ -436,16 +453,16 @@ private: { ec = asio::error_code(errno, asio::error::get_system_category()); - read_op_queue_.dispatch_all_operations(descriptor, ec); - write_op_queue_.dispatch_all_operations(descriptor, ec); - except_op_queue_.dispatch_all_operations(descriptor, ec); + read_op_queue_.perform_all_operations(descriptor, ec); + write_op_queue_.perform_all_operations(descriptor, ec); + except_op_queue_.perform_all_operations(descriptor, ec); } } } } - read_op_queue_.dispatch_cancellations(); - write_op_queue_.dispatch_cancellations(); - except_op_queue_.dispatch_cancellations(); + read_op_queue_.perform_cancellations(); + write_op_queue_.perform_cancellations(); + except_op_queue_.perform_cancellations(); for (std::size_t i = 0; i < timer_queues_.size(); ++i) { timer_queues_[i]->dispatch_timers(); @@ -457,7 +474,7 @@ private: cancel_ops_unlocked(pending_cancellations_[i]); pending_cancellations_.clear(); - cleanup_operations_and_timers(lock); + complete_operations_and_timers(lock); } // Run the select loop in the thread. @@ -557,16 +574,16 @@ private: // destructors may make calls back into this reactor. We make a copy of the // vector of timer queues since the original may be modified while the lock // is not held. - void cleanup_operations_and_timers( + void complete_operations_and_timers( asio::detail::mutex::scoped_lock& lock) { timer_queues_for_cleanup_ = timer_queues_; lock.unlock(); - read_op_queue_.cleanup_operations(); - write_op_queue_.cleanup_operations(); - except_op_queue_.cleanup_operations(); + read_op_queue_.complete_operations(); + write_op_queue_.complete_operations(); + except_op_queue_.complete_operations(); for (std::size_t i = 0; i < timer_queues_for_cleanup_.size(); ++i) - timer_queues_for_cleanup_[i]->cleanup_timers(); + timer_queues_for_cleanup_[i]->complete_timers(); } // Add a pending event entry for the given descriptor. diff --git a/libtorrent/include/asio/detail/epoll_reactor.hpp b/libtorrent/include/asio/detail/epoll_reactor.hpp index e318ed666..b8fd6afb8 100644 --- a/libtorrent/include/asio/detail/epoll_reactor.hpp +++ b/libtorrent/include/asio/detail/epoll_reactor.hpp @@ -53,6 +53,13 @@ class epoll_reactor : public asio::detail::service_base > { public: + // Per-descriptor data. + struct per_descriptor_data + { + bool allow_speculative_read; + bool allow_speculative_write; + }; + // Constructor. epoll_reactor(asio::io_service& io_service) : asio::detail::service_base >(io_service), @@ -118,10 +125,14 @@ public: // Register a socket with the reactor. Returns 0 on success, system error // code on failure. - int register_descriptor(socket_type descriptor) + int register_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data) { // No need to lock according to epoll documentation. + descriptor_data.allow_speculative_read = true; + descriptor_data.allow_speculative_write = true; + epoll_event ev = { 0, { 0 } }; ev.events = 0; ev.data.fd = descriptor; @@ -134,16 +145,47 @@ public: // Start a new read operation. The handler object will be invoked when the // given descriptor is ready to be read, or an error has occurred. template - void start_read_op(socket_type descriptor, Handler handler) + void start_read_op(socket_type descriptor, + per_descriptor_data& descriptor_data, + Handler handler, bool allow_speculative_read = true) { + if (allow_speculative_read && descriptor_data.allow_speculative_read) + { + asio::error_code ec; + std::size_t bytes_transferred = 0; + if (handler.perform(ec, bytes_transferred)) + { + handler.complete(ec, bytes_transferred); + return; + } + + // We only get one shot at a speculative read in this function. + allow_speculative_read = false; + } + asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) return; - if (!read_op_queue_.has_operation(descriptor)) - if (handler(asio::error_code())) + if (!allow_speculative_read) + need_epoll_wait_ = true; + else if (!read_op_queue_.has_operation(descriptor)) + { + // Speculative reads are ok as there are no queued read operations. + descriptor_data.allow_speculative_read = true; + + asio::error_code ec; + std::size_t bytes_transferred = 0; + if (handler.perform(ec, bytes_transferred)) + { + handler.complete(ec, bytes_transferred); return; + } + } + + // Speculative reads are not ok as there will be queued read operations. + descriptor_data.allow_speculative_read = false; if (read_op_queue_.enqueue_operation(descriptor, handler)) { @@ -162,7 +204,7 @@ public: { asio::error_code ec(errno, asio::error::get_system_category()); - read_op_queue_.dispatch_all_operations(descriptor, ec); + read_op_queue_.perform_all_operations(descriptor, ec); } } } @@ -170,16 +212,47 @@ public: // Start a new write operation. The handler object will be invoked when the // given descriptor is ready to be written, or an error has occurred. template - void start_write_op(socket_type descriptor, Handler handler) + void start_write_op(socket_type descriptor, + per_descriptor_data& descriptor_data, + Handler handler, bool allow_speculative_write = true) { + if (allow_speculative_write && descriptor_data.allow_speculative_write) + { + asio::error_code ec; + std::size_t bytes_transferred = 0; + if (handler.perform(ec, bytes_transferred)) + { + handler.complete(ec, bytes_transferred); + return; + } + + // We only get one shot at a speculative write in this function. + allow_speculative_write = false; + } + asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) return; - if (!write_op_queue_.has_operation(descriptor)) - if (handler(asio::error_code())) + if (!allow_speculative_write) + need_epoll_wait_ = true; + else if (!write_op_queue_.has_operation(descriptor)) + { + // Speculative writes are ok as there are no queued write operations. + descriptor_data.allow_speculative_write = true; + + asio::error_code ec; + std::size_t bytes_transferred = 0; + if (handler.perform(ec, bytes_transferred)) + { + handler.complete(ec, bytes_transferred); return; + } + } + + // Speculative writes are not ok as there will be queued write operations. + descriptor_data.allow_speculative_write = false; if (write_op_queue_.enqueue_operation(descriptor, handler)) { @@ -198,7 +271,7 @@ public: { asio::error_code ec(errno, asio::error::get_system_category()); - write_op_queue_.dispatch_all_operations(descriptor, ec); + write_op_queue_.perform_all_operations(descriptor, ec); } } } @@ -206,7 +279,8 @@ public: // Start a new exception operation. The handler object will be invoked when // the given descriptor has exception information, or an error has occurred. template - void start_except_op(socket_type descriptor, Handler handler) + void start_except_op(socket_type descriptor, + per_descriptor_data&, Handler handler) { asio::detail::mutex::scoped_lock lock(mutex_); @@ -230,31 +304,34 @@ public: { asio::error_code ec(errno, asio::error::get_system_category()); - except_op_queue_.dispatch_all_operations(descriptor, ec); + except_op_queue_.perform_all_operations(descriptor, ec); } } } - // Start new write and exception operations. The handler object will be - // invoked when the given descriptor is ready for writing or has exception - // information available, or an error has occurred. + // Start a new write operation. The handler object will be invoked when the + // given descriptor is ready for writing or an error has occurred. Speculative + // writes are not allowed. template - void start_write_and_except_ops(socket_type descriptor, Handler handler) + void start_connect_op(socket_type descriptor, + per_descriptor_data& descriptor_data, Handler handler) { asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) return; - bool need_mod = write_op_queue_.enqueue_operation(descriptor, handler); - need_mod = except_op_queue_.enqueue_operation(descriptor, handler) - && need_mod; - if (need_mod) + // Speculative writes are not ok as there will be queued write operations. + descriptor_data.allow_speculative_write = false; + + if (write_op_queue_.enqueue_operation(descriptor, handler)) { epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP; + ev.events = EPOLLOUT | EPOLLERR | EPOLLHUP; if (read_op_queue_.has_operation(descriptor)) ev.events |= EPOLLIN; + if (except_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLPRI; ev.data.fd = descriptor; int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); @@ -264,8 +341,7 @@ public: { asio::error_code ec(errno, asio::error::get_system_category()); - write_op_queue_.dispatch_all_operations(descriptor, ec); - except_op_queue_.dispatch_all_operations(descriptor, ec); + write_op_queue_.perform_all_operations(descriptor, ec); } } } @@ -273,25 +349,15 @@ public: // 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) + void cancel_ops(socket_type descriptor, per_descriptor_data&) { asio::detail::mutex::scoped_lock lock(mutex_); cancel_ops_unlocked(descriptor); } - // Enqueue cancellation of all operations associated with the given - // descriptor. The handlers associated with the descriptor will be invoked - // with the operation_aborted error. This function does not acquire the - // epoll_reactor's mutex, and so should only be used from within a reactor - // handler. - void enqueue_cancel_ops_unlocked(socket_type descriptor) - { - pending_cancellations_.push_back(descriptor); - } - // Cancel any operations that are running against the descriptor and remove // its registration from the reactor. - void close_descriptor(socket_type descriptor) + void close_descriptor(socket_type descriptor, per_descriptor_data&) { asio::detail::mutex::scoped_lock lock(mutex_); @@ -360,16 +426,16 @@ private: // Dispatch any operation cancellations that were made while the select // loop was not running. - read_op_queue_.dispatch_cancellations(); - write_op_queue_.dispatch_cancellations(); - except_op_queue_.dispatch_cancellations(); + read_op_queue_.perform_cancellations(); + write_op_queue_.perform_cancellations(); + except_op_queue_.perform_cancellations(); for (std::size_t i = 0; i < timer_queues_.size(); ++i) timer_queues_[i]->dispatch_cancellations(); // Check if the thread is supposed to stop. if (stop_thread_) { - cleanup_operations_and_timers(lock); + complete_operations_and_timers(lock); return; } @@ -378,7 +444,7 @@ private: if (!block && read_op_queue_.empty() && write_op_queue_.empty() && except_op_queue_.empty() && all_timer_queues_are_empty()) { - cleanup_operations_and_timers(lock); + complete_operations_and_timers(lock); return; } @@ -395,7 +461,7 @@ private: lock.lock(); wait_in_progress_ = false; - // Block signals while dispatching operations. + // Block signals while performing operations. asio::detail::signal_blocker sb; // Dispatch the waiting events. @@ -416,17 +482,17 @@ private: // Exception operations must be processed first to ensure that any // out-of-band data is read before normal data. if (events[i].events & (EPOLLPRI | EPOLLERR | EPOLLHUP)) - more_except = except_op_queue_.dispatch_operation(descriptor, ec); + more_except = except_op_queue_.perform_operation(descriptor, ec); else more_except = except_op_queue_.has_operation(descriptor); if (events[i].events & (EPOLLIN | EPOLLERR | EPOLLHUP)) - more_reads = read_op_queue_.dispatch_operation(descriptor, ec); + more_reads = read_op_queue_.perform_operation(descriptor, ec); else more_reads = read_op_queue_.has_operation(descriptor); if (events[i].events & (EPOLLOUT | EPOLLERR | EPOLLHUP)) - more_writes = write_op_queue_.dispatch_operation(descriptor, ec); + more_writes = write_op_queue_.perform_operation(descriptor, ec); else more_writes = write_op_queue_.has_operation(descriptor); @@ -460,16 +526,16 @@ private: { ec = asio::error_code(errno, asio::error::get_system_category()); - read_op_queue_.dispatch_all_operations(descriptor, ec); - write_op_queue_.dispatch_all_operations(descriptor, ec); - except_op_queue_.dispatch_all_operations(descriptor, ec); + read_op_queue_.perform_all_operations(descriptor, ec); + write_op_queue_.perform_all_operations(descriptor, ec); + except_op_queue_.perform_all_operations(descriptor, ec); } } } } - read_op_queue_.dispatch_cancellations(); - write_op_queue_.dispatch_cancellations(); - except_op_queue_.dispatch_cancellations(); + read_op_queue_.perform_cancellations(); + write_op_queue_.perform_cancellations(); + except_op_queue_.perform_cancellations(); for (std::size_t i = 0; i < timer_queues_.size(); ++i) { timer_queues_[i]->dispatch_timers(); @@ -485,7 +551,7 @@ private: need_epoll_wait_ = !read_op_queue_.empty() || !write_op_queue_.empty() || !except_op_queue_.empty(); - cleanup_operations_and_timers(lock); + complete_operations_and_timers(lock); } // Run the select loop in the thread. @@ -588,16 +654,16 @@ private: // destructors may make calls back into this reactor. We make a copy of the // vector of timer queues since the original may be modified while the lock // is not held. - void cleanup_operations_and_timers( + void complete_operations_and_timers( asio::detail::mutex::scoped_lock& lock) { timer_queues_for_cleanup_ = timer_queues_; lock.unlock(); - read_op_queue_.cleanup_operations(); - write_op_queue_.cleanup_operations(); - except_op_queue_.cleanup_operations(); + read_op_queue_.complete_operations(); + write_op_queue_.complete_operations(); + except_op_queue_.complete_operations(); for (std::size_t i = 0; i < timer_queues_for_cleanup_.size(); ++i) - timer_queues_for_cleanup_[i]->cleanup_timers(); + timer_queues_for_cleanup_[i]->complete_timers(); } // Mutex to protect access to internal data. diff --git a/libtorrent/include/asio/detail/handler_base_from_member.hpp b/libtorrent/include/asio/detail/handler_base_from_member.hpp new file mode 100644 index 000000000..87e16c14b --- /dev/null +++ b/libtorrent/include/asio/detail/handler_base_from_member.hpp @@ -0,0 +1,76 @@ +// +// handler_base_from_member.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_BASE_FROM_MEMBER_HPP +#define ASIO_DETAIL_HANDLER_BASE_FROM_MEMBER_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 { + +// Base class for classes that need a handler data member. Forwards the custom +// allocation and invocation hooks to the contained handler. +template +class handler_base_from_member +{ +public: + handler_base_from_member(Handler handler) + : handler_(handler) + { + } + +//protected: + Handler handler_; + +protected: + // Protected destructor to prevent deletion through this type. + ~handler_base_from_member() + { + } +}; + +template +inline void* asio_handler_allocate(std::size_t size, + handler_base_from_member* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + handler_base_from_member* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + handler_base_from_member* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_BASE_FROM_MEMBER_HPP diff --git a/libtorrent/include/asio/detail/handler_queue.hpp b/libtorrent/include/asio/detail/handler_queue.hpp index 63c78a7d0..989f976b2 100644 --- a/libtorrent/include/asio/detail/handler_queue.hpp +++ b/libtorrent/include/asio/detail/handler_queue.hpp @@ -198,6 +198,16 @@ private: this_type* h(static_cast(base)); typedef handler_alloc_traits alloc_traits; handler_ptr ptr(h->handler_, h); + + // A sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Handler handler(h->handler_); + (void)handler; + + // Free the memory associated with the handler. + ptr.reset(); } private: diff --git a/libtorrent/include/asio/detail/hash_map.hpp b/libtorrent/include/asio/detail/hash_map.hpp index 8560e5879..bbbb406f8 100644 --- a/libtorrent/include/asio/detail/hash_map.hpp +++ b/libtorrent/include/asio/detail/hash_map.hpp @@ -43,6 +43,7 @@ inline std::size_t calculate_hash_value(SOCKET s) } #endif // defined(_WIN64) +// Note: assumes K and V are POD types. template class hash_map : private noncopyable @@ -139,7 +140,7 @@ public: if (it == values_.end()) { buckets_[bucket].first = buckets_[bucket].last = - values_.insert(values_.end(), v); + values_insert(values_.end(), v); return std::pair(buckets_[bucket].last, true); } iterator end = buckets_[bucket].last; @@ -150,7 +151,7 @@ public: return std::pair(it, false); ++it; } - buckets_[bucket].last = values_.insert(end, v); + buckets_[bucket].last = values_insert(end, v); return std::pair(buckets_[bucket].last, true); } @@ -169,7 +170,7 @@ public: else if (is_last) --buckets_[bucket].last; - values_.erase(it); + values_erase(it); } // Remove all entries from the map. @@ -184,9 +185,36 @@ public: } private: + // 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 list of all values in the hash map. std::list values_; + // The list of spare nodes waiting to be recycled. Assumes that POD types only + // are stored in the hash map. + std::list spares_; + // The type for a bucket in the hash table. struct bucket_type { diff --git a/libtorrent/include/asio/detail/indirect_handler_queue.hpp b/libtorrent/include/asio/detail/indirect_handler_queue.hpp new file mode 100644 index 000000000..d08a7df4d --- /dev/null +++ b/libtorrent/include/asio/detail/indirect_handler_queue.hpp @@ -0,0 +1,291 @@ +// +// indirect_handler_queue.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_INDIRECT_HANDLER_QUEUE_HPP +#define ASIO_DETAIL_INDIRECT_HANDLER_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/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/noncopyable.hpp" + +#if defined(_MSC_VER) && (_MSC_VER >= 1310) +extern "C" void _ReadWriteBarrier(); +# pragma intrinsic(_ReadWriteBarrier) +#endif // defined(_MSC_VER) && (_MSC_VER >= 1310) + +namespace asio { +namespace detail { + +class indirect_handler_queue + : private noncopyable +{ +public: + class handler; + + // Element for a node in the queue. + class node + { + public: + node() + : version_(0), + handler_(0), + next_(0) + { + } + + private: + friend class indirect_handler_queue; + unsigned long version_; + handler* handler_; + node* next_; + }; + + // Base class for handlers in the queue. + class handler + : private noncopyable + { + public: + void invoke() + { + invoke_func_(this); + } + + void destroy() + { + destroy_func_(this); + } + + protected: + typedef void (*invoke_func_type)(handler*); + typedef void (*destroy_func_type)(handler*); + + handler(invoke_func_type invoke_func, + destroy_func_type destroy_func) + : node_(new node), + invoke_func_(invoke_func), + destroy_func_(destroy_func) + { + } + + ~handler() + { + if (node_) + delete node_; + } + + private: + friend class indirect_handler_queue; + node* node_; + invoke_func_type invoke_func_; + destroy_func_type destroy_func_; + }; + + // Smart point to manager handler lifetimes. + class scoped_ptr + : private noncopyable + { + public: + explicit scoped_ptr(handler* h) + : handler_(h) + { + } + + ~scoped_ptr() + { + if (handler_) + handler_->destroy(); + } + + handler* get() const + { + return handler_; + } + + handler* release() + { + handler* tmp = handler_; + handler_ = 0; + return tmp; + } + + private: + handler* handler_; + }; + + // Constructor. + indirect_handler_queue() + : front_(new node), + back_(front_), + next_version_(1) + { + } + + // Destructor. + ~indirect_handler_queue() + { + while (front_) + { + node* tmp = front_; + front_ = front_->next_; + delete tmp; + } + } + + // Wrap a handler to be pushed into the queue. + template + static handler* wrap(Handler h) + { + // Allocate and construct an object to wrap the handler. + typedef handler_wrapper value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(h); + handler_ptr ptr(raw_ptr, h); + return ptr.release(); + } + + // Determine whether the queue has something ready to pop. + bool poppable() + { + return front_->next_ != 0; + } + + // The version number at the front of the queue. + unsigned long front_version() + { + return front_->version_; + } + + // The version number at the back of the queue. + unsigned long back_version() + { + return back_->version_; + } + + // Pop a handler from the front of the queue. + handler* pop() + { + node* n = front_; + node* new_front = n->next_; + if (new_front) + { + handler* h = new_front->handler_; + h->node_ = n; + new_front->handler_ = 0; + front_ = new_front; + return h; + } + return 0; + } + + // Push a handler on to the back of the queue. + void push(handler* h) + { + node* n = h->node_; + h->node_ = 0; + n->version_ = next_version_; + next_version_ += 2; + n->handler_ = h; + n->next_ = 0; + memory_barrier(); + back_->next_ = n; + back_ = n; + } + +private: + // Template wrapper for handlers. + template + class handler_wrapper + : public handler + { + public: + handler_wrapper(Handler h) + : handler( + &handler_wrapper::do_call, + &handler_wrapper::do_destroy), + handler_(h) + { + } + + static void do_call(handler* base) + { + // Take ownership of the handler object. + typedef handler_wrapper this_type; + this_type* h(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(h->handler_, h); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(h->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Make the upcall. + asio_handler_invoke_helpers::invoke(handler, &handler); + } + + static void do_destroy(handler* base) + { + // Take ownership of the handler object. + typedef handler_wrapper this_type; + this_type* h(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(h->handler_, h); + + // A sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Handler handler(h->handler_); + (void)handler; + + // Free the memory associated with the handler. + ptr.reset(); + } + + private: + Handler handler_; + }; + + // Helper function to create a memory barrier. + static void memory_barrier() + { +#if defined(_GLIBCXX_WRITE_MEM_BARRIER) + _GLIBCXX_WRITE_MEM_BARRIER; +#elif defined(_MSC_VER) && (_MSC_VER >= 1310) + _ReadWriteBarrier(); +#else +# error memory barrier required +#endif + } + + // The front of the queue. + node* front_; + + // The back of the queue. + node* back_; + + // The next version counter to be assigned to a node. + unsigned long next_version_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_INDIRECT_HANDLER_QUEUE_HPP diff --git a/libtorrent/include/asio/detail/kqueue_reactor.hpp b/libtorrent/include/asio/detail/kqueue_reactor.hpp index 20163181b..1d0d21aba 100644 --- a/libtorrent/include/asio/detail/kqueue_reactor.hpp +++ b/libtorrent/include/asio/detail/kqueue_reactor.hpp @@ -60,6 +60,13 @@ class kqueue_reactor : public asio::detail::service_base > { public: + // Per-descriptor data. + struct per_descriptor_data + { + bool allow_speculative_read; + bool allow_speculative_write; + }; + // Constructor. kqueue_reactor(asio::io_service& io_service) : asio::detail::service_base< @@ -126,24 +133,58 @@ public: // Register a socket with the reactor. Returns 0 on success, system error // code on failure. - int register_descriptor(socket_type) + int register_descriptor(socket_type, per_descriptor_data& descriptor_data) { + descriptor_data.allow_speculative_read = true; + descriptor_data.allow_speculative_write = true; + return 0; } // Start a new read operation. The handler object will be invoked when the // given descriptor is ready to be read, or an error has occurred. template - void start_read_op(socket_type descriptor, Handler handler) + void start_read_op(socket_type descriptor, + per_descriptor_data& descriptor_data, Handler handler, + bool allow_speculative_read = true) { + if (allow_speculative_read && descriptor_data.allow_speculative_read) + { + asio::error_code ec; + std::size_t bytes_transferred = 0; + if (handler.perform(ec, bytes_transferred)) + { + handler.complete(ec, bytes_transferred); + return; + } + + // We only get one shot at a speculative read in this function. + allow_speculative_read = false; + } + asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) return; - if (!read_op_queue_.has_operation(descriptor)) - if (handler(asio::error_code())) + if (!allow_speculative_read) + need_kqueue_wait_ = true; + else if (!read_op_queue_.has_operation(descriptor)) + { + // Speculative reads are ok as there are no queued read operations. + descriptor_data.allow_speculative_read = true; + + asio::error_code ec; + std::size_t bytes_transferred = 0; + if (handler.perform(ec, bytes_transferred)) + { + handler.complete(ec, bytes_transferred); return; + } + } + + // Speculative reads are not ok as there will be queued read operations. + descriptor_data.allow_speculative_read = false; if (read_op_queue_.enqueue_operation(descriptor, handler)) { @@ -153,7 +194,7 @@ public: { asio::error_code ec(errno, asio::error::get_system_category()); - read_op_queue_.dispatch_all_operations(descriptor, ec); + read_op_queue_.perform_all_operations(descriptor, ec); } } } @@ -161,16 +202,47 @@ public: // Start a new write operation. The handler object will be invoked when the // given descriptor is ready to be written, or an error has occurred. template - void start_write_op(socket_type descriptor, Handler handler) + void start_write_op(socket_type descriptor, + per_descriptor_data& descriptor_data, Handler handler, + bool allow_speculative_write = true) { + if (allow_speculative_write && descriptor_data.allow_speculative_write) + { + asio::error_code ec; + std::size_t bytes_transferred = 0; + if (handler.perform(ec, bytes_transferred)) + { + handler.complete(ec, bytes_transferred); + return; + } + + // We only get one shot at a speculative write in this function. + allow_speculative_write = false; + } + asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) return; - if (!write_op_queue_.has_operation(descriptor)) - if (handler(asio::error_code())) + if (!allow_speculative_write) + need_kqueue_wait_ = true; + else if (!write_op_queue_.has_operation(descriptor)) + { + // Speculative writes are ok as there are no queued write operations. + descriptor_data.allow_speculative_write = true; + + asio::error_code ec; + std::size_t bytes_transferred = 0; + if (handler.perform(ec, bytes_transferred)) + { + handler.complete(ec, bytes_transferred); return; + } + } + + // Speculative writes are not ok as there will be queued write operations. + descriptor_data.allow_speculative_write = false; if (write_op_queue_.enqueue_operation(descriptor, handler)) { @@ -180,7 +252,7 @@ public: { asio::error_code ec(errno, asio::error::get_system_category()); - write_op_queue_.dispatch_all_operations(descriptor, ec); + write_op_queue_.perform_all_operations(descriptor, ec); } } } @@ -188,7 +260,8 @@ public: // Start a new exception operation. The handler object will be invoked when // the given descriptor has exception information, or an error has occurred. template - void start_except_op(socket_type descriptor, Handler handler) + void start_except_op(socket_type descriptor, + per_descriptor_data&, Handler handler) { asio::detail::mutex::scoped_lock lock(mutex_); @@ -206,22 +279,25 @@ public: { asio::error_code ec(errno, asio::error::get_system_category()); - except_op_queue_.dispatch_all_operations(descriptor, ec); + except_op_queue_.perform_all_operations(descriptor, ec); } } } - // Start new write and exception operations. The handler object will be - // invoked when the given descriptor is ready for writing or has exception - // information available, or an error has occurred. + // Start a new write operation. The handler object will be invoked when the + // given descriptor is ready to be written, or an error has occurred. template - void start_write_and_except_ops(socket_type descriptor, Handler handler) + void start_connect_op(socket_type descriptor, + per_descriptor_data& descriptor_data, Handler handler) { asio::detail::mutex::scoped_lock lock(mutex_); if (shutdown_) return; + // Speculative writes are not ok as there will be queued write operations. + descriptor_data.allow_speculative_write = false; + if (write_op_queue_.enqueue_operation(descriptor, handler)) { struct kevent event; @@ -230,23 +306,7 @@ public: { asio::error_code ec(errno, asio::error::get_system_category()); - write_op_queue_.dispatch_all_operations(descriptor, ec); - } - } - - if (except_op_queue_.enqueue_operation(descriptor, handler)) - { - struct kevent event; - if (read_op_queue_.has_operation(descriptor)) - EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); - else - EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0); - if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) - { - asio::error_code ec(errno, - asio::error::get_system_category()); - except_op_queue_.dispatch_all_operations(descriptor, ec); - write_op_queue_.dispatch_all_operations(descriptor, ec); + write_op_queue_.perform_all_operations(descriptor, ec); } } } @@ -254,25 +314,15 @@ public: // 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) + void cancel_ops(socket_type descriptor, per_descriptor_data&) { asio::detail::mutex::scoped_lock lock(mutex_); cancel_ops_unlocked(descriptor); } - // Enqueue cancellation of all operations associated with the given - // descriptor. The handlers associated with the descriptor will be invoked - // with the operation_aborted error. This function does not acquire the - // kqueue_reactor's mutex, and so should only be used from within a reactor - // handler. - void enqueue_cancel_ops_unlocked(socket_type descriptor) - { - pending_cancellations_.push_back(descriptor); - } - // Cancel any operations that are running against the descriptor and remove // its registration from the reactor. - void close_descriptor(socket_type descriptor) + void close_descriptor(socket_type descriptor, per_descriptor_data&) { asio::detail::mutex::scoped_lock lock(mutex_); @@ -343,16 +393,16 @@ private: // Dispatch any operation cancellations that were made while the select // loop was not running. - read_op_queue_.dispatch_cancellations(); - write_op_queue_.dispatch_cancellations(); - except_op_queue_.dispatch_cancellations(); + read_op_queue_.perform_cancellations(); + write_op_queue_.perform_cancellations(); + except_op_queue_.perform_cancellations(); for (std::size_t i = 0; i < timer_queues_.size(); ++i) timer_queues_[i]->dispatch_cancellations(); // Check if the thread is supposed to stop. if (stop_thread_) { - cleanup_operations_and_timers(lock); + complete_operations_and_timers(lock); return; } @@ -361,7 +411,7 @@ private: if (!block && read_op_queue_.empty() && write_op_queue_.empty() && except_op_queue_.empty() && all_timer_queues_are_empty()) { - cleanup_operations_and_timers(lock); + complete_operations_and_timers(lock); return; } @@ -381,7 +431,7 @@ private: lock.lock(); wait_in_progress_ = false; - // Block signals while dispatching operations. + // Block signals while performing operations. asio::detail::signal_blocker sb; // Dispatch the waiting events. @@ -401,22 +451,22 @@ private: { asio::error_code error( events[i].data, asio::error::get_system_category()); - except_op_queue_.dispatch_all_operations(descriptor, error); - read_op_queue_.dispatch_all_operations(descriptor, error); + except_op_queue_.perform_all_operations(descriptor, error); + read_op_queue_.perform_all_operations(descriptor, error); } else if (events[i].flags & EV_OOBAND) { asio::error_code error; - more_except = except_op_queue_.dispatch_operation(descriptor, error); + more_except = except_op_queue_.perform_operation(descriptor, error); if (events[i].data > 0) - more_reads = read_op_queue_.dispatch_operation(descriptor, error); + more_reads = read_op_queue_.perform_operation(descriptor, error); else more_reads = read_op_queue_.has_operation(descriptor); } else { asio::error_code error; - more_reads = read_op_queue_.dispatch_operation(descriptor, error); + more_reads = read_op_queue_.perform_operation(descriptor, error); more_except = except_op_queue_.has_operation(descriptor); } @@ -432,8 +482,8 @@ private: { asio::error_code error(errno, asio::error::get_system_category()); - except_op_queue_.dispatch_all_operations(descriptor, error); - read_op_queue_.dispatch_all_operations(descriptor, error); + except_op_queue_.perform_all_operations(descriptor, error); + read_op_queue_.perform_all_operations(descriptor, error); } } else if (events[i].filter == EVFILT_WRITE) @@ -444,12 +494,12 @@ private: { asio::error_code error( events[i].data, asio::error::get_system_category()); - write_op_queue_.dispatch_all_operations(descriptor, error); + write_op_queue_.perform_all_operations(descriptor, error); } else { asio::error_code error; - more_writes = write_op_queue_.dispatch_operation(descriptor, error); + more_writes = write_op_queue_.perform_operation(descriptor, error); } // Update the descriptor in the kqueue. @@ -462,14 +512,14 @@ private: { asio::error_code error(errno, asio::error::get_system_category()); - write_op_queue_.dispatch_all_operations(descriptor, error); + write_op_queue_.perform_all_operations(descriptor, error); } } } - read_op_queue_.dispatch_cancellations(); - write_op_queue_.dispatch_cancellations(); - except_op_queue_.dispatch_cancellations(); + read_op_queue_.perform_cancellations(); + write_op_queue_.perform_cancellations(); + except_op_queue_.perform_cancellations(); for (std::size_t i = 0; i < timer_queues_.size(); ++i) { timer_queues_[i]->dispatch_timers(); @@ -485,7 +535,7 @@ private: need_kqueue_wait_ = !read_op_queue_.empty() || !write_op_queue_.empty() || !except_op_queue_.empty(); - cleanup_operations_and_timers(lock); + complete_operations_and_timers(lock); } // Run the select loop in the thread. @@ -586,16 +636,16 @@ private: // destructors may make calls back into this reactor. We make a copy of the // vector of timer queues since the original may be modified while the lock // is not held. - void cleanup_operations_and_timers( + void complete_operations_and_timers( asio::detail::mutex::scoped_lock& lock) { timer_queues_for_cleanup_ = timer_queues_; lock.unlock(); - read_op_queue_.cleanup_operations(); - write_op_queue_.cleanup_operations(); - except_op_queue_.cleanup_operations(); + read_op_queue_.complete_operations(); + write_op_queue_.complete_operations(); + except_op_queue_.complete_operations(); for (std::size_t i = 0; i < timer_queues_for_cleanup_.size(); ++i) - timer_queues_for_cleanup_[i]->cleanup_timers(); + timer_queues_for_cleanup_[i]->complete_timers(); } // Mutex to protect access to internal data. diff --git a/libtorrent/include/asio/detail/null_thread.hpp b/libtorrent/include/asio/detail/null_thread.hpp index f91ca53e0..f08b7b378 100644 --- a/libtorrent/include/asio/detail/null_thread.hpp +++ b/libtorrent/include/asio/detail/null_thread.hpp @@ -38,9 +38,12 @@ class null_thread : private noncopyable { public: + // The purpose of the thread. + enum purpose { internal, external }; + // Constructor. template - null_thread(Function f) + null_thread(Function f, purpose = internal) { asio::system_error e( asio::error::operation_not_supported, "thread"); diff --git a/libtorrent/include/asio/detail/old_win_sdk_compat.hpp b/libtorrent/include/asio/detail/old_win_sdk_compat.hpp index a935677e1..b73a09012 100644 --- a/libtorrent/include/asio/detail/old_win_sdk_compat.hpp +++ b/libtorrent/include/asio/detail/old_win_sdk_compat.hpp @@ -328,6 +328,11 @@ inline int IN6_IS_ADDR_MC_GLOBAL(const in6_addr_emulation* a) # 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" diff --git a/libtorrent/include/asio/detail/posix_thread.hpp b/libtorrent/include/asio/detail/posix_thread.hpp index 892ffe076..fe5f82905 100644 --- a/libtorrent/include/asio/detail/posix_thread.hpp +++ b/libtorrent/include/asio/detail/posix_thread.hpp @@ -42,9 +42,12 @@ class posix_thread : private noncopyable { public: + // The purpose of the thread. + enum purpose { internal, external }; + // Constructor. template - posix_thread(Function f) + posix_thread(Function f, purpose = internal) : joined_(false) { std::auto_ptr arg(new func(f)); diff --git a/libtorrent/include/asio/detail/push_options.hpp b/libtorrent/include/asio/detail/push_options.hpp index 4e0524dc5..47524b255 100644 --- a/libtorrent/include/asio/detail/push_options.hpp +++ b/libtorrent/include/asio/detail/push_options.hpp @@ -87,8 +87,10 @@ # 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 diff --git a/libtorrent/include/asio/detail/reactive_descriptor_service.hpp b/libtorrent/include/asio/detail/reactive_descriptor_service.hpp new file mode 100644 index 000000000..12a0c36b4 --- /dev/null +++ b/libtorrent/include/asio/detail/reactive_descriptor_service.hpp @@ -0,0 +1,709 @@ +// +// reactive_descriptor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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/handler_base_from_member.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/descriptor_ops.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +namespace asio { +namespace detail { + +template +class reactive_descriptor_service + : public asio::detail::service_base< + 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 + { + user_set_non_blocking = 1, // The user wants a non-blocking descriptor. + internal_non_blocking = 2 // The descriptor has been set non-blocking. + }; + + // Flags indicating the current state of the descriptor. + unsigned char flags_; + + // Per-descriptor data used by the reactor. + typename Reactor::per_descriptor_data reactor_data_; + }; + + // 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) + : asio::detail::service_base< + reactive_descriptor_service >(io_service), + reactor_(asio::use_service(io_service)) + { + } + + // 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 + 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; + } + + if (command.name() == static_cast(FIONBIO)) + { + if (command.get()) + impl.flags_ |= implementation_type::user_set_non_blocking; + else + impl.flags_ &= ~implementation_type::user_set_non_blocking; + ec = asio::error_code(); + } + else + { + descriptor_ops::ioctl(impl.descriptor_, command.name(), + static_cast(command.data()), ec); + } + return ec; + } + + // Write some data to the descriptor. + template + 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; + } + + // Copy buffers into array. + descriptor_ops::buf bufs[max_buffers]; + typename ConstBufferSequence::const_iterator iter = buffers.begin(); + typename ConstBufferSequence::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + descriptor_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to read_some 0 bytes on a stream is a no-op. + if (total_buffer_size == 0) + { + ec = asio::error_code(); + return 0; + } + + // Make descriptor non-blocking if user wants non-blocking. + if (impl.flags_ & implementation_type::user_set_non_blocking) + { + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (descriptor_ops::ioctl(impl.descriptor_, + FIONBIO, &non_blocking, ec)) + return 0; + impl.flags_ |= implementation_type::internal_non_blocking; + } + } + + // Send the data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes_sent = descriptor_ops::gather_write( + impl.descriptor_, bufs, i, ec); + + // 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 + class write_operation : + public handler_base_from_member + { + public: + write_operation(int descriptor, asio::io_service& io_service, + const ConstBufferSequence& buffers, Handler handler) + : handler_base_from_member(handler), + descriptor_(descriptor), + io_service_(io_service), + work_(io_service), + buffers_(buffers) + { + } + + bool perform(asio::error_code& ec, + std::size_t& bytes_transferred) + { + // Check whether the operation was successful. + if (ec) + { + bytes_transferred = 0; + return true; + } + + // Copy buffers into array. + descriptor_ops::buf bufs[max_buffers]; + typename ConstBufferSequence::const_iterator iter = buffers_.begin(); + typename ConstBufferSequence::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + descriptor_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Write the data. + int bytes = descriptor_ops::gather_write(descriptor_, bufs, i, ec); + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + bytes_transferred = (bytes < 0 ? 0 : bytes); + return true; + } + + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + io_service_.post(bind_handler(this->handler_, ec, bytes_transferred)); + } + + private: + int descriptor_; + asio::io_service& io_service_; + asio::io_service::work work_; + ConstBufferSequence buffers_; + }; + + // Start an asynchronous write. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + // Determine total size of buffers. + typename ConstBufferSequence::const_iterator iter = buffers.begin(); + typename ConstBufferSequence::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to read_some 0 bytes on a stream is a no-op. + if (total_buffer_size == 0) + { + this->get_io_service().post(bind_handler(handler, + asio::error_code(), 0)); + return; + } + + // Make descriptor non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + asio::error_code ec; + if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec)) + { + this->get_io_service().post(bind_handler(handler, ec, 0)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + reactor_.start_write_op(impl.descriptor_, impl.reactor_data_, + write_operation( + impl.descriptor_, this->get_io_service(), buffers, handler)); + } + } + + template + class null_buffers_operation : + public handler_base_from_member + { + public: + null_buffers_operation(asio::io_service& io_service, Handler handler) + : handler_base_from_member(handler), + work_(io_service) + { + } + + bool perform(asio::error_code&, + std::size_t& bytes_transferred) + { + bytes_transferred = 0; + return true; + } + + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + work_.get_io_service().post(bind_handler( + this->handler_, ec, bytes_transferred)); + } + + private: + asio::io_service::work work_; + }; + + // Start an asynchronous wait until data can be written without blocking. + template + void async_write_some(implementation_type& impl, + const null_buffers&, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + reactor_.start_write_op(impl.descriptor_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler), + false); + } + } + + // Read some data from the stream. Returns the number of bytes read. + template + 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; + } + + // Copy buffers into array. + descriptor_ops::buf bufs[max_buffers]; + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + descriptor_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to read_some 0 bytes on a stream is a no-op. + if (total_buffer_size == 0) + { + ec = asio::error_code(); + return 0; + } + + // Make descriptor non-blocking if user wants non-blocking. + if (impl.flags_ & implementation_type::user_set_non_blocking) + { + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec)) + return 0; + impl.flags_ |= implementation_type::internal_non_blocking; + } + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes_read = descriptor_ops::scatter_read( + impl.descriptor_, bufs, i, ec); + + // 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 + class read_operation : + public handler_base_from_member + { + public: + read_operation(int descriptor, asio::io_service& io_service, + const MutableBufferSequence& buffers, Handler handler) + : handler_base_from_member(handler), + descriptor_(descriptor), + io_service_(io_service), + work_(io_service), + buffers_(buffers) + { + } + + bool perform(asio::error_code& ec, + std::size_t& bytes_transferred) + { + // Check whether the operation was successful. + if (ec) + { + bytes_transferred = 0; + return true; + } + + // Copy buffers into array. + descriptor_ops::buf bufs[max_buffers]; + typename MutableBufferSequence::const_iterator iter = buffers_.begin(); + typename MutableBufferSequence::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + descriptor_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Read some data. + int bytes = descriptor_ops::scatter_read(descriptor_, bufs, i, ec); + if (bytes == 0) + ec = asio::error::eof; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + bytes_transferred = (bytes < 0 ? 0 : bytes); + return true; + } + + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + io_service_.post(bind_handler(this->handler_, ec, bytes_transferred)); + } + + private: + int descriptor_; + asio::io_service& io_service_; + asio::io_service::work work_; + MutableBufferSequence buffers_; + }; + + // Start an asynchronous read. The buffer for the data being read must be + // valid for the lifetime of the asynchronous operation. + template + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + // Determine total size of buffers. + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to read_some 0 bytes on a stream is a no-op. + if (total_buffer_size == 0) + { + this->get_io_service().post(bind_handler(handler, + asio::error_code(), 0)); + return; + } + + // Make descriptor non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + asio::error_code ec; + if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec)) + { + this->get_io_service().post(bind_handler(handler, ec, 0)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + reactor_.start_read_op(impl.descriptor_, impl.reactor_data_, + read_operation( + impl.descriptor_, this->get_io_service(), buffers, handler)); + } + } + + // Wait until data can be read without blocking. + template + void async_read_some(implementation_type& impl, + const null_buffers&, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + reactor_.start_read_op(impl.descriptor_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler), + false); + } + } + +private: + // 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/libtorrent/include/asio/detail/reactive_serial_port_service.hpp b/libtorrent/include/asio/detail/reactive_serial_port_service.hpp new file mode 100644 index 000000000..5db94a2dd --- /dev/null +++ b/libtorrent/include/asio/detail/reactive_serial_port_service.hpp @@ -0,0 +1,267 @@ +// +// reactive_serial_port_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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 +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#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. +template +class reactive_serial_port_service + : public asio::detail::service_base< + reactive_serial_port_service > +{ +public: + // The native type of a stream handle. + typedef typename reactive_descriptor_service::native_type + native_type; + + // The implementation type of the stream handle. + typedef typename reactive_descriptor_service::implementation_type + implementation_type; + + reactive_serial_port_service(asio::io_service& io_service) + : asio::detail::service_base< + reactive_serial_port_service>(io_service), + descriptor_service_(asio::use_service< + reactive_descriptor_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) + { + 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; + 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 + 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 + 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 + 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 + 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 + 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 + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, Handler handler) + { + descriptor_service_.async_read_some(impl, buffers, handler); + } + +private: + // The handle service used for initiating asynchronous operations. + reactive_descriptor_service& descriptor_service_; +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP diff --git a/libtorrent/include/asio/detail/reactive_socket_service.hpp b/libtorrent/include/asio/detail/reactive_socket_service.hpp index 0ecabaa0e..c7aedf88e 100644 --- a/libtorrent/include/asio/detail/reactive_socket_service.hpp +++ b/libtorrent/include/asio/detail/reactive_socket_service.hpp @@ -26,6 +26,7 @@ #include "asio/io_service.hpp" #include "asio/socket_base.hpp" #include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_base_from_member.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/service_base.hpp" #include "asio/detail/socket_holder.hpp" @@ -83,6 +84,9 @@ public: // The protocol associated with the socket. protocol_type protocol_; + + // Per-descriptor data used by the reactor. + typename Reactor::per_descriptor_data reactor_data_; }; // The maximum number of buffers to support in a single operation. @@ -113,7 +117,7 @@ public: { if (impl.socket_ != invalid_socket) { - reactor_.close_descriptor(impl.socket_); + reactor_.close_descriptor(impl.socket_, impl.reactor_data_); if (impl.flags_ & implementation_type::internal_non_blocking) { @@ -155,7 +159,7 @@ public: if (sock.get() == invalid_socket) return ec; - if (int err = reactor_.register_descriptor(sock.get())) + if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_)) { ec = asio::error_code(err, asio::error::get_system_category()); @@ -180,7 +184,8 @@ public: return ec; } - if (int err = reactor_.register_descriptor(native_socket)) + if (int err = reactor_.register_descriptor( + native_socket, impl.reactor_data_)) { ec = asio::error_code(err, asio::error::get_system_category()); @@ -206,7 +211,7 @@ public: { if (is_open(impl)) { - reactor_.close_descriptor(impl.socket_); + reactor_.close_descriptor(impl.socket_, impl.reactor_data_); if (impl.flags_ & implementation_type::internal_non_blocking) { @@ -242,7 +247,7 @@ public: return ec; } - reactor_.cancel_ops(impl.socket_); + reactor_.cancel_ops(impl.socket_, impl.reactor_data_); ec = asio::error_code(); return ec; } @@ -558,28 +563,46 @@ public: } } + // 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 - class send_handler + class send_operation : + public handler_base_from_member { public: - send_handler(socket_type socket, asio::io_service& io_service, + send_operation(socket_type socket, asio::io_service& io_service, const ConstBufferSequence& buffers, socket_base::message_flags flags, Handler handler) - : socket_(socket), + : handler_base_from_member(handler), + socket_(socket), io_service_(io_service), work_(io_service), buffers_(buffers), - flags_(flags), - handler_(handler) + flags_(flags) { } - bool operator()(const asio::error_code& result) + bool perform(asio::error_code& ec, + std::size_t& bytes_transferred) { // Check whether the operation was successful. - if (result) + if (ec) { - io_service_.post(bind_handler(handler_, result, 0)); + bytes_transferred = 0; return true; } @@ -597,7 +620,6 @@ public: } // Send the data. - asio::error_code ec; int bytes = socket_ops::send(socket_, bufs, i, flags_, ec); // Check if we need to run the operation again. @@ -605,17 +627,22 @@ public: || ec == asio::error::try_again) return false; - io_service_.post(bind_handler(handler_, ec, bytes < 0 ? 0 : bytes)); + bytes_transferred = (bytes < 0 ? 0 : bytes); return true; } + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + io_service_.post(bind_handler(this->handler_, ec, bytes_transferred)); + } + private: socket_type socket_; asio::io_service& io_service_; asio::io_service::work work_; ConstBufferSequence buffers_; socket_base::message_flags flags_; - Handler handler_; }; // Start an asynchronous send. The data being sent must be valid for the @@ -666,12 +693,59 @@ public: impl.flags_ |= implementation_type::internal_non_blocking; } - reactor_.start_write_op(impl.socket_, - send_handler( + reactor_.start_write_op(impl.socket_, impl.reactor_data_, + send_operation( impl.socket_, this->get_io_service(), buffers, flags, handler)); } } + template + class null_buffers_operation : + public handler_base_from_member + { + public: + null_buffers_operation(asio::io_service& io_service, Handler handler) + : handler_base_from_member(handler), + work_(io_service) + { + } + + bool perform(asio::error_code&, + std::size_t& bytes_transferred) + { + bytes_transferred = 0; + return true; + } + + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + work_.get_io_service().post(bind_handler( + this->handler_, ec, bytes_transferred)); + } + + private: + asio::io_service::work work_; + }; + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send(implementation_type& impl, const null_buffers&, + socket_base::message_flags, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + reactor_.start_write_op(impl.socket_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler), + false); + } + } + // Send a datagram to the specified endpoint. Returns the number of bytes // sent. template @@ -733,29 +807,48 @@ public: } } + // 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 - class send_to_handler + class send_to_operation : + public handler_base_from_member { public: - send_to_handler(socket_type socket, asio::io_service& io_service, + send_to_operation(socket_type socket, asio::io_service& io_service, const ConstBufferSequence& buffers, const endpoint_type& endpoint, socket_base::message_flags flags, Handler handler) - : socket_(socket), + : handler_base_from_member(handler), + socket_(socket), io_service_(io_service), work_(io_service), buffers_(buffers), destination_(endpoint), - flags_(flags), - handler_(handler) + flags_(flags) { } - bool operator()(const asio::error_code& result) + bool perform(asio::error_code& ec, + std::size_t& bytes_transferred) { // Check whether the operation was successful. - if (result) + if (ec) { - io_service_.post(bind_handler(handler_, result, 0)); + bytes_transferred = 0; return true; } @@ -773,7 +866,6 @@ public: } // Send the data. - asio::error_code ec; int bytes = socket_ops::sendto(socket_, bufs, i, flags_, destination_.data(), destination_.size(), ec); @@ -782,10 +874,16 @@ public: || ec == asio::error::try_again) return false; - io_service_.post(bind_handler(handler_, ec, bytes < 0 ? 0 : bytes)); + bytes_transferred = (bytes < 0 ? 0 : bytes); return true; } + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + io_service_.post(bind_handler(this->handler_, ec, bytes_transferred)); + } + private: socket_type socket_; asio::io_service& io_service_; @@ -793,7 +891,6 @@ public: ConstBufferSequence buffers_; endpoint_type destination_; socket_base::message_flags flags_; - Handler handler_; }; // Start an asynchronous send. The data being sent must be valid for the @@ -824,13 +921,31 @@ public: impl.flags_ |= implementation_type::internal_non_blocking; } - reactor_.start_write_op(impl.socket_, - send_to_handler( + reactor_.start_write_op(impl.socket_, impl.reactor_data_, + send_to_operation( impl.socket_, this->get_io_service(), buffers, destination, flags, handler)); } } + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send_to(implementation_type& impl, const null_buffers&, + socket_base::message_flags, const endpoint_type&, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + reactor_.start_write_op(impl.socket_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler), + false); + } + } + // Receive some data from the peer. Returns the number of bytes received. template size_t receive(implementation_type& impl, @@ -888,7 +1003,7 @@ public: return bytes_recvd; // Check for EOF. - if (bytes_recvd == 0) + if (bytes_recvd == 0 && impl.protocol_.type() == SOCK_STREAM) { ec = asio::error::eof; return 0; @@ -906,28 +1021,48 @@ public: } } + // 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 - class receive_handler + class receive_operation : + public handler_base_from_member { public: - receive_handler(socket_type socket, asio::io_service& io_service, - const MutableBufferSequence& buffers, socket_base::message_flags flags, - Handler handler) - : socket_(socket), + receive_operation(socket_type socket, int protocol_type, + asio::io_service& io_service, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + : handler_base_from_member(handler), + socket_(socket), + protocol_type_(protocol_type), io_service_(io_service), work_(io_service), buffers_(buffers), - flags_(flags), - handler_(handler) + flags_(flags) { } - bool operator()(const asio::error_code& result) + bool perform(asio::error_code& ec, + std::size_t& bytes_transferred) { // Check whether the operation was successful. - if (result) + if (ec) { - io_service_.post(bind_handler(handler_, result, 0)); + bytes_transferred = 0; return true; } @@ -945,9 +1080,8 @@ public: } // Receive some data. - asio::error_code ec; int bytes = socket_ops::recv(socket_, bufs, i, flags_, ec); - if (bytes == 0) + if (bytes == 0 && protocol_type_ == SOCK_STREAM) ec = asio::error::eof; // Check if we need to run the operation again. @@ -955,17 +1089,23 @@ public: || ec == asio::error::try_again) return false; - io_service_.post(bind_handler(handler_, ec, bytes < 0 ? 0 : bytes)); + bytes_transferred = (bytes < 0 ? 0 : bytes); return true; } + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + io_service_.post(bind_handler(this->handler_, ec, bytes_transferred)); + } + private: socket_type socket_; + int protocol_type_; asio::io_service& io_service_; asio::io_service::work work_; MutableBufferSequence buffers_; socket_base::message_flags flags_; - Handler handler_; }; // Start an asynchronous receive. The buffer for the data being received @@ -1019,19 +1159,44 @@ public: if (flags & socket_base::message_out_of_band) { - reactor_.start_except_op(impl.socket_, - receive_handler( - impl.socket_, this->get_io_service(), buffers, flags, handler)); + reactor_.start_except_op(impl.socket_, impl.reactor_data_, + receive_operation( + impl.socket_, impl.protocol_.type(), + this->get_io_service(), buffers, flags, handler)); } else { - reactor_.start_read_op(impl.socket_, - receive_handler( - impl.socket_, this->get_io_service(), buffers, flags, handler)); + reactor_.start_read_op(impl.socket_, impl.reactor_data_, + receive_operation( + impl.socket_, impl.protocol_.type(), + this->get_io_service(), buffers, flags, handler)); } } } + // Wait until data can be received without blocking. + template + void async_receive(implementation_type& impl, const null_buffers&, + socket_base::message_flags flags, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else if (flags & socket_base::message_out_of_band) + { + reactor_.start_except_op(impl.socket_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler)); + } + else + { + reactor_.start_read_op(impl.socket_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler), + false); + } + } + // Receive a datagram with the endpoint of the sender. Returns the number of // bytes received. template @@ -1087,7 +1252,7 @@ public: } // Check for EOF. - if (bytes_recvd == 0) + if (bytes_recvd == 0 && impl.protocol_.type() == SOCK_STREAM) { ec = asio::error::eof; return 0; @@ -1105,30 +1270,53 @@ public: } } + // 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 - class receive_from_handler + class receive_from_operation : + public handler_base_from_member { public: - receive_from_handler(socket_type socket, + receive_from_operation(socket_type socket, int protocol_type, asio::io_service& io_service, const MutableBufferSequence& buffers, endpoint_type& endpoint, socket_base::message_flags flags, Handler handler) - : socket_(socket), + : handler_base_from_member(handler), + socket_(socket), + protocol_type_(protocol_type), io_service_(io_service), work_(io_service), buffers_(buffers), sender_endpoint_(endpoint), - flags_(flags), - handler_(handler) + flags_(flags) { } - bool operator()(const asio::error_code& result) + bool perform(asio::error_code& ec, + std::size_t& bytes_transferred) { // Check whether the operation was successful. - if (result) + if (ec) { - io_service_.post(bind_handler(handler_, result, 0)); + bytes_transferred = 0; return true; } @@ -1147,10 +1335,9 @@ public: // Receive some data. std::size_t addr_len = sender_endpoint_.capacity(); - asio::error_code ec; int bytes = socket_ops::recvfrom(socket_, bufs, i, flags_, sender_endpoint_.data(), &addr_len, ec); - if (bytes == 0) + if (bytes == 0 && protocol_type_ == SOCK_STREAM) ec = asio::error::eof; // Check if we need to run the operation again. @@ -1159,18 +1346,24 @@ public: return false; sender_endpoint_.resize(addr_len); - io_service_.post(bind_handler(handler_, ec, bytes < 0 ? 0 : bytes)); + bytes_transferred = (bytes < 0 ? 0 : bytes); return true; } + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + io_service_.post(bind_handler(this->handler_, ec, bytes_transferred)); + } + private: socket_type socket_; + int protocol_type_; asio::io_service& io_service_; asio::io_service::work work_; MutableBufferSequence buffers_; endpoint_type& sender_endpoint_; socket_base::message_flags flags_; - Handler handler_; }; // Start an asynchronous receive. The buffer for the data being received and @@ -1201,10 +1394,40 @@ public: impl.flags_ |= implementation_type::internal_non_blocking; } - reactor_.start_read_op(impl.socket_, - receive_from_handler( - impl.socket_, this->get_io_service(), buffers, - sender_endpoint, flags, handler)); + reactor_.start_read_op(impl.socket_, impl.reactor_data_, + receive_from_operation( + impl.socket_, impl.protocol_.type(), this->get_io_service(), + buffers, sender_endpoint, flags, handler)); + } + } + + // Wait until data can be received without blocking. + template + void async_receive_from(implementation_type& impl, + const null_buffers&, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + if (flags & socket_base::message_out_of_band) + { + reactor_.start_except_op(impl.socket_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler)); + } + else + { + reactor_.start_read_op(impl.socket_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler), + false); + } } } @@ -1299,35 +1522,32 @@ public: } template - class accept_handler + class accept_operation : + public handler_base_from_member { public: - accept_handler(socket_type socket, asio::io_service& io_service, + accept_operation(socket_type socket, asio::io_service& io_service, Socket& peer, const protocol_type& protocol, endpoint_type* peer_endpoint, bool enable_connection_aborted, Handler handler) - : socket_(socket), + : handler_base_from_member(handler), + socket_(socket), io_service_(io_service), work_(io_service), peer_(peer), protocol_(protocol), peer_endpoint_(peer_endpoint), - enable_connection_aborted_(enable_connection_aborted), - handler_(handler) + enable_connection_aborted_(enable_connection_aborted) { } - bool operator()(const asio::error_code& result) + bool perform(asio::error_code& ec, std::size_t&) { // Check whether the operation was successful. - if (result) - { - io_service_.post(bind_handler(handler_, result)); + if (ec) return true; - } // Accept the waiting connection. - asio::error_code ec; socket_holder new_socket; std::size_t addr_len = 0; if (peer_endpoint_) @@ -1363,10 +1583,14 @@ public: new_socket.release(); } - io_service_.post(bind_handler(handler_, ec)); return true; } + void complete(const asio::error_code& ec, std::size_t) + { + io_service_.post(bind_handler(this->handler_, ec)); + } + private: socket_type socket_; asio::io_service& io_service_; @@ -1375,7 +1599,6 @@ public: protocol_type protocol_; endpoint_type* peer_endpoint_; bool enable_connection_aborted_; - Handler handler_; }; // Start an asynchronous accept. The peer and peer_endpoint objects @@ -1409,8 +1632,8 @@ public: impl.flags_ |= implementation_type::internal_non_blocking; } - reactor_.start_read_op(impl.socket_, - accept_handler( + reactor_.start_read_op(impl.socket_, impl.reactor_data_, + accept_operation( impl.socket_, this->get_io_service(), peer, impl.protocol_, peer_endpoint, (impl.flags_ & implementation_type::enable_connection_aborted) != 0, @@ -1444,70 +1667,52 @@ public: } template - class connect_handler + class connect_operation : + public handler_base_from_member { public: - connect_handler(socket_type socket, boost::shared_ptr completed, - asio::io_service& io_service, Reactor& reactor, Handler handler) - : socket_(socket), - completed_(completed), + connect_operation(socket_type socket, + asio::io_service& io_service, Handler handler) + : handler_base_from_member(handler), + socket_(socket), io_service_(io_service), - work_(io_service), - reactor_(reactor), - handler_(handler) + work_(io_service) { } - bool operator()(const asio::error_code& result) + bool perform(asio::error_code& ec, std::size_t&) { - // Check whether a handler has already been called for the connection. - // If it has, then we don't want to do anything in this handler. - if (*completed_) - return true; - - // Cancel the other reactor operation for the connection. - *completed_ = true; - reactor_.enqueue_cancel_ops_unlocked(socket_); - // Check whether the operation was successful. - if (result) - { - io_service_.post(bind_handler(handler_, result)); + if (ec) return true; - } // Get the error code from the connect operation. int connect_error = 0; size_t connect_error_len = sizeof(connect_error); - asio::error_code ec; if (socket_ops::getsockopt(socket_, SOL_SOCKET, SO_ERROR, &connect_error, &connect_error_len, ec) == socket_error_retval) - { - io_service_.post(bind_handler(handler_, ec)); return true; - } - // If connection failed then post the handler with the error code. + // The connection failed so the handler will be posted with an error code. if (connect_error) { ec = asio::error_code(connect_error, asio::error::get_system_category()); - io_service_.post(bind_handler(handler_, ec)); return true; } - // Post the result of the successful connection operation. - io_service_.post(bind_handler(handler_, ec)); return true; } + void complete(const asio::error_code& ec, std::size_t) + { + io_service_.post(bind_handler(this->handler_, ec)); + } + private: socket_type socket_; - boost::shared_ptr completed_; asio::io_service& io_service_; asio::io_service::work work_; - Reactor& reactor_; - Handler handler_; }; // Start an asynchronous connect. @@ -1551,10 +1756,9 @@ public: { // The connection is happening in the background, and we need to wait // until the socket becomes writeable. - boost::shared_ptr completed(new bool(false)); - reactor_.start_write_and_except_ops(impl.socket_, - connect_handler(impl.socket_, completed, - this->get_io_service(), reactor_, handler)); + reactor_.start_connect_op(impl.socket_, impl.reactor_data_, + connect_operation(impl.socket_, + this->get_io_service(), handler)); } else { diff --git a/libtorrent/include/asio/detail/reactor_op_queue.hpp b/libtorrent/include/asio/detail/reactor_op_queue.hpp index b419c7c3f..034591d0d 100644 --- a/libtorrent/include/asio/detail/reactor_op_queue.hpp +++ b/libtorrent/include/asio/detail/reactor_op_queue.hpp @@ -22,6 +22,7 @@ #include "asio/detail/pop_options.hpp" #include "asio/error.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/hash_map.hpp" #include "asio/detail/noncopyable.hpp" @@ -37,36 +38,42 @@ public: reactor_op_queue() : operations_(), cancelled_operations_(0), - cleanup_operations_(0) + complete_operations_(0) { } // Add a new operation to the queue. Returns true if this is the only // operation for the given descriptor, in which case the reactor's event // demultiplexing function call may need to be interrupted and restarted. - template - bool enqueue_operation(Descriptor descriptor, Handler handler) + template + bool enqueue_operation(Descriptor descriptor, Operation operation) { - op_base* new_op = new op(descriptor, handler); + // Allocate and construct an object to wrap the handler. + typedef handler_alloc_traits > alloc_traits; + raw_handler_ptr raw_ptr(operation); + handler_ptr ptr(raw_ptr, descriptor, operation); typedef typename operation_map::iterator iterator; typedef typename operation_map::value_type value_type; std::pair entry = - operations_.insert(value_type(descriptor, new_op)); + operations_.insert(value_type(descriptor, ptr.get())); if (entry.second) + { + ptr.release(); return true; + } op_base* current_op = entry.first->second; while (current_op->next_) current_op = current_op->next_; - current_op->next_ = new_op; + current_op->next_ = ptr.release(); return false; } // Cancel all operations associated with the descriptor. Any operations // pending for the descriptor will be notified that they have been cancelled - // next time dispatch_cancellations is called. Returns true if any operations + // 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) @@ -98,9 +105,9 @@ public: return operations_.find(descriptor) != operations_.end(); } - // Dispatch the first operation corresponding to the descriptor. Returns true + // Perform the first operation corresponding to the descriptor. Returns true // if there are more operations queued for the descriptor. - bool dispatch_operation(Descriptor descriptor, + bool perform_operation(Descriptor descriptor, const asio::error_code& result) { typename operation_map::iterator i = operations_.find(descriptor); @@ -108,9 +115,9 @@ public: { op_base* this_op = i->second; i->second = this_op->next_; - this_op->next_ = cleanup_operations_; - cleanup_operations_ = this_op; - bool done = this_op->invoke(result); + this_op->next_ = complete_operations_; + complete_operations_ = this_op; + bool done = this_op->perform(result); if (done) { // Operation has finished. @@ -127,8 +134,8 @@ public: else { // Operation wants to be called again. Leave it at the front of the - // queue for this descriptor, and remove from the cleanup list. - cleanup_operations_ = this_op->next_; + // queue for this descriptor, and remove from the completed list. + complete_operations_ = this_op->next_; this_op->next_ = i->second; i->second = this_op; return true; @@ -137,8 +144,8 @@ public: return false; } - // Dispatch all operations corresponding to the descriptor. - void dispatch_all_operations(Descriptor descriptor, + // Perform all operations corresponding to the descriptor. + void perform_all_operations(Descriptor descriptor, const asio::error_code& result) { typename operation_map::iterator i = operations_.find(descriptor); @@ -148,14 +155,14 @@ public: { op_base* this_op = i->second; i->second = this_op->next_; - this_op->next_ = cleanup_operations_; - cleanup_operations_ = this_op; - bool done = this_op->invoke(result); + this_op->next_ = complete_operations_; + complete_operations_ = this_op; + bool done = this_op->perform(result); if (!done) { // Operation has not finished yet, so leave at front of queue, and - // remove from the cleanup list. - cleanup_operations_ = this_op->next_; + // remove from the completed list. + complete_operations_ = this_op->next_; this_op->next_ = i->second; i->second = this_op; return; @@ -178,15 +185,15 @@ public: if (!descriptors.set(descriptor)) { asio::error_code ec(error::fd_set_failure); - dispatch_all_operations(descriptor, ec); + perform_all_operations(descriptor, ec); } } } - // Dispatch the operations corresponding to the ready file descriptors + // Perform the operations corresponding to the ready file descriptors // contained in the given descriptor set. template - void dispatch_descriptors(const Descriptor_Set& descriptors, + void perform_operations_for_descriptors(const Descriptor_Set& descriptors, const asio::error_code& result) { typename operation_map::iterator i = operations_.begin(); @@ -197,9 +204,9 @@ public: { op_base* this_op = op_iter->second; op_iter->second = this_op->next_; - this_op->next_ = cleanup_operations_; - cleanup_operations_ = this_op; - bool done = this_op->invoke(result); + this_op->next_ = complete_operations_; + complete_operations_ = this_op; + bool done = this_op->perform(result); if (done) { if (!op_iter->second) @@ -208,8 +215,8 @@ public: else { // Operation has not finished yet, so leave at front of queue, and - // remove from the cleanup list. - cleanup_operations_ = this_op->next_; + // remove from the completed list. + complete_operations_ = this_op->next_; this_op->next_ = op_iter->second; op_iter->second = this_op; } @@ -217,28 +224,28 @@ public: } } - // Dispatch any pending cancels for operations. - void dispatch_cancellations() + // Perform any pending cancels for operations. + void perform_cancellations() { while (cancelled_operations_) { op_base* this_op = cancelled_operations_; cancelled_operations_ = this_op->next_; - this_op->next_ = cleanup_operations_; - cleanup_operations_ = this_op; - this_op->invoke(asio::error::operation_aborted); + this_op->next_ = complete_operations_; + complete_operations_ = this_op; + this_op->perform(asio::error::operation_aborted); } } - // Destroy operations that are waiting to be cleaned up. - void cleanup_operations() + // Complete all operations that are waiting to be completed. + void complete_operations() { - while (cleanup_operations_) + while (complete_operations_) { - op_base* next_op = cleanup_operations_->next_; - cleanup_operations_->next_ = 0; - cleanup_operations_->destroy(); - cleanup_operations_ = next_op; + op_base* next_op = complete_operations_->next_; + complete_operations_->next_ = 0; + complete_operations_->complete(); + complete_operations_ = next_op; } } @@ -253,12 +260,12 @@ public: cancelled_operations_ = next_op; } - while (cleanup_operations_) + while (complete_operations_) { - op_base* next_op = cleanup_operations_->next_; - cleanup_operations_->next_ = 0; - cleanup_operations_->destroy(); - cleanup_operations_ = next_op; + op_base* next_op = complete_operations_->next_; + complete_operations_->next_ = 0; + complete_operations_->destroy(); + complete_operations_ = next_op; } typename operation_map::iterator i = operations_.begin(); @@ -290,28 +297,40 @@ private: } // Perform the operation. - bool invoke(const asio::error_code& result) + bool perform(const asio::error_code& result) { - return invoke_func_(this, result); + result_ = result; + return perform_func_(this, result_, bytes_transferred_); + } + + // Destroy the operation and post the handler. + void complete() + { + complete_func_(this, result_, bytes_transferred_); } // Destroy the operation. void destroy() { - return destroy_func_(this); + destroy_func_(this); } protected: - typedef bool (*invoke_func_type)(op_base*, - const asio::error_code&); + typedef bool (*perform_func_type)(op_base*, + asio::error_code&, std::size_t&); + typedef void (*complete_func_type)(op_base*, + const asio::error_code&, std::size_t); typedef void (*destroy_func_type)(op_base*); // Construct an operation for the given descriptor. - op_base(invoke_func_type invoke_func, + op_base(perform_func_type perform_func, complete_func_type complete_func, destroy_func_type destroy_func, Descriptor descriptor) - : invoke_func_(invoke_func), + : perform_func_(perform_func), + complete_func_(complete_func), destroy_func_(destroy_func), descriptor_(descriptor), + result_(), + bytes_transferred_(0), next_(0) { } @@ -324,48 +343,94 @@ private: private: friend class reactor_op_queue; - // The function to be called to dispatch the handler. - invoke_func_type invoke_func_; + // The function to be called to perform the operation. + perform_func_type perform_func_; - // The function to be called to delete the handler. + // The function to be called to delete the operation and post the handler. + complete_func_type complete_func_; + + // The function to be called to delete the operation. destroy_func_type destroy_func_; // The descriptor associated with the operation. Descriptor descriptor_; + // The result of the operation. + asio::error_code result_; + + // The number of bytes transferred in the operation. + std::size_t bytes_transferred_; + // The next operation for the same file descriptor. op_base* next_; }; - // Adaptor class template for using handlers in operations. - template + // Adaptor class template for operations. + template class op : public op_base { public: // Constructor. - op(Descriptor descriptor, Handler handler) - : op_base(&op::invoke_handler, - &op::destroy_handler, descriptor), - handler_(handler) + op(Descriptor descriptor, Operation operation) + : op_base(&op::do_perform, &op::do_complete, + &op::do_destroy, descriptor), + operation_(operation) { } - // Invoke the handler. - static bool invoke_handler(op_base* base, - const asio::error_code& result) + // Perform the operation. + static bool do_perform(op_base* base, + asio::error_code& result, std::size_t& bytes_transferred) { - return static_cast*>(base)->handler_(result); + return static_cast*>(base)->operation_.perform( + result, bytes_transferred); } - // Delete the handler. - static void destroy_handler(op_base* base) + // Destroy the operation and post the handler. + static void do_complete(op_base* base, + const asio::error_code& result, std::size_t bytes_transferred) { - delete static_cast*>(base); + // Take ownership of the operation object. + typedef op this_type; + this_type* this_op(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(this_op->operation_, this_op); + + // Make a copy of the error_code and the operation so that the memory can + // be deallocated before the upcall is made. + asio::error_code ec(result); + Operation operation(this_op->operation_); + + // Free the memory associated with the operation. + ptr.reset(); + + // Make the upcall. + operation.complete(ec, bytes_transferred); + } + + // Destroy the operation. + static void do_destroy(op_base* base) + { + // Take ownership of the operation object. + typedef op this_type; + this_type* this_op(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(this_op->operation_, this_op); + + // A sub-object of the operation may be the true owner of the memory + // associated with the operation. Consequently, a local copy of the + // operation is required to ensure that any owning sub-object remains + // valid until after we have deallocated the memory here. + Operation operation(this_op->operation_); + (void)operation; + + // Free the memory associated with the operation. + ptr.reset(); } private: - Handler handler_; + Operation operation_; }; // The type for a map of operations. @@ -377,8 +442,8 @@ private: // The list of operations that have been cancelled. op_base* cancelled_operations_; - // The list of operations to be destroyed. - op_base* cleanup_operations_; + // The list of operations waiting to be completed. + op_base* complete_operations_; }; } // namespace detail diff --git a/libtorrent/include/asio/detail/select_reactor.hpp b/libtorrent/include/asio/detail/select_reactor.hpp index a21cf93f5..78a364ee0 100644 --- a/libtorrent/include/asio/detail/select_reactor.hpp +++ b/libtorrent/include/asio/detail/select_reactor.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "asio/detail/pop_options.hpp" @@ -50,6 +51,11 @@ class select_reactor : public asio::detail::service_base > { public: + // Per-descriptor data. + struct per_descriptor_data + { + }; + // Constructor. select_reactor(asio::io_service& io_service) : asio::detail::service_base< @@ -106,7 +112,7 @@ public: // Register a socket with the reactor. Returns 0 on success, system error // code on failure. - int register_descriptor(socket_type descriptor) + int register_descriptor(socket_type, per_descriptor_data&) { return 0; } @@ -114,7 +120,8 @@ public: // Start a new read operation. The handler object will be invoked when the // given descriptor is ready to be read, or an error has occurred. template - void start_read_op(socket_type descriptor, Handler handler) + void start_read_op(socket_type descriptor, per_descriptor_data&, + Handler handler, bool /*allow_speculative_read*/ = true) { asio::detail::mutex::scoped_lock lock(mutex_); if (!shutdown_) @@ -125,7 +132,8 @@ public: // Start a new write operation. The handler object will be invoked when the // given descriptor is ready to be written, or an error has occurred. template - void start_write_op(socket_type descriptor, Handler handler) + void start_write_op(socket_type descriptor, per_descriptor_data&, + Handler handler, bool /*allow_speculative_write*/ = true) { asio::detail::mutex::scoped_lock lock(mutex_); if (!shutdown_) @@ -136,7 +144,8 @@ public: // Start a new exception operation. The handler object will be invoked when // the given descriptor has exception information, or an error has occurred. template - void start_except_op(socket_type descriptor, Handler handler) + void start_except_op(socket_type descriptor, + per_descriptor_data&, Handler handler) { asio::detail::mutex::scoped_lock lock(mutex_); if (!shutdown_) @@ -144,18 +153,74 @@ public: interrupter_.interrupt(); } + // Wrapper for connect handlers to enable the handler object to be placed + // in both the write and the except operation queues, but ensure that only + // one of the handlers is called. + template + class connect_handler_wrapper + { + public: + connect_handler_wrapper(socket_type descriptor, + boost::shared_ptr completed, + select_reactor& reactor, Handler handler) + : descriptor_(descriptor), + completed_(completed), + reactor_(reactor), + handler_(handler) + { + } + + bool perform(asio::error_code& ec, + std::size_t& bytes_transferred) + { + // Check whether one of the handlers has already been called. If it has, + // then we don't want to do anything in this handler. + if (*completed_) + { + completed_.reset(); // Indicate that this handler should not complete. + return true; + } + + // Cancel the other reactor operation for the connection. + *completed_ = true; + reactor_.enqueue_cancel_ops_unlocked(descriptor_); + + // Call the contained handler. + return handler_.perform(ec, bytes_transferred); + } + + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + if (completed_.get()) + handler_.complete(ec, bytes_transferred); + } + + private: + socket_type descriptor_; + boost::shared_ptr completed_; + select_reactor& reactor_; + Handler handler_; + }; + // Start new write and exception operations. The handler object will be // invoked when the given descriptor is ready for writing or has exception - // information available, or an error has occurred. + // information available, or an error has occurred. The handler will be called + // only once. template - void start_write_and_except_ops(socket_type descriptor, Handler handler) + void start_connect_op(socket_type descriptor, + per_descriptor_data&, Handler handler) { asio::detail::mutex::scoped_lock lock(mutex_); if (!shutdown_) { - bool interrupt = write_op_queue_.enqueue_operation(descriptor, handler); - interrupt = except_op_queue_.enqueue_operation(descriptor, handler) - || interrupt; + boost::shared_ptr completed(new bool(false)); + connect_handler_wrapper wrapped_handler( + descriptor, completed, *this, handler); + bool interrupt = write_op_queue_.enqueue_operation( + descriptor, wrapped_handler); + interrupt = except_op_queue_.enqueue_operation( + descriptor, wrapped_handler) || interrupt; if (interrupt) interrupter_.interrupt(); } @@ -164,7 +229,7 @@ public: // 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) + void cancel_ops(socket_type descriptor, per_descriptor_data&) { asio::detail::mutex::scoped_lock lock(mutex_); cancel_ops_unlocked(descriptor); @@ -173,8 +238,8 @@ public: // Enqueue cancellation of all operations associated with the given // descriptor. The handlers associated with the descriptor will be invoked // with the operation_aborted error. This function does not acquire the - // select_reactor's mutex, and so should only be used from within a reactor - // handler. + // select_reactor's mutex, and so should only be used when the reactor lock is + // already held. void enqueue_cancel_ops_unlocked(socket_type descriptor) { pending_cancellations_.push_back(descriptor); @@ -182,7 +247,7 @@ public: // Cancel any operations that are running against the descriptor and remove // its registration from the reactor. - void close_descriptor(socket_type descriptor) + void close_descriptor(socket_type descriptor, per_descriptor_data&) { asio::detail::mutex::scoped_lock lock(mutex_); cancel_ops_unlocked(descriptor); @@ -245,16 +310,16 @@ private: // Dispatch any operation cancellations that were made while the select // loop was not running. - read_op_queue_.dispatch_cancellations(); - write_op_queue_.dispatch_cancellations(); - except_op_queue_.dispatch_cancellations(); + read_op_queue_.perform_cancellations(); + write_op_queue_.perform_cancellations(); + except_op_queue_.perform_cancellations(); for (std::size_t i = 0; i < timer_queues_.size(); ++i) timer_queues_[i]->dispatch_cancellations(); // Check if the thread is supposed to stop. if (stop_thread_) { - cleanup_operations_and_timers(lock); + complete_operations_and_timers(lock); return; } @@ -263,7 +328,7 @@ private: if (!block && read_op_queue_.empty() && write_op_queue_.empty() && except_op_queue_.empty() && all_timer_queues_are_empty()) { - cleanup_operations_and_timers(lock); + complete_operations_and_timers(lock); return; } @@ -305,15 +370,15 @@ private: { // Exception operations must be processed first to ensure that any // out-of-band data is read before normal data. - except_op_queue_.dispatch_descriptors(except_fds, - asio::error_code()); - read_op_queue_.dispatch_descriptors(read_fds, - asio::error_code()); - write_op_queue_.dispatch_descriptors(write_fds, - asio::error_code()); - except_op_queue_.dispatch_cancellations(); - read_op_queue_.dispatch_cancellations(); - write_op_queue_.dispatch_cancellations(); + except_op_queue_.perform_operations_for_descriptors( + except_fds, asio::error_code()); + read_op_queue_.perform_operations_for_descriptors( + read_fds, asio::error_code()); + write_op_queue_.perform_operations_for_descriptors( + write_fds, asio::error_code()); + except_op_queue_.perform_cancellations(); + read_op_queue_.perform_cancellations(); + write_op_queue_.perform_cancellations(); } for (std::size_t i = 0; i < timer_queues_.size(); ++i) { @@ -326,7 +391,7 @@ private: cancel_ops_unlocked(pending_cancellations_[i]); pending_cancellations_.clear(); - cleanup_operations_and_timers(lock); + complete_operations_and_timers(lock); } // Run the select loop in the thread. @@ -411,16 +476,16 @@ private: // destructors may make calls back into this reactor. We make a copy of the // vector of timer queues since the original may be modified while the lock // is not held. - void cleanup_operations_and_timers( + void complete_operations_and_timers( asio::detail::mutex::scoped_lock& lock) { timer_queues_for_cleanup_ = timer_queues_; lock.unlock(); - read_op_queue_.cleanup_operations(); - write_op_queue_.cleanup_operations(); - except_op_queue_.cleanup_operations(); + read_op_queue_.complete_operations(); + write_op_queue_.complete_operations(); + except_op_queue_.complete_operations(); for (std::size_t i = 0; i < timer_queues_for_cleanup_.size(); ++i) - timer_queues_for_cleanup_[i]->cleanup_timers(); + timer_queues_for_cleanup_[i]->complete_timers(); } // Mutex to protect access to internal data. diff --git a/libtorrent/include/asio/detail/service_registry.hpp b/libtorrent/include/asio/detail/service_registry.hpp index dadcd4bf6..f517cbbf1 100644 --- a/libtorrent/include/asio/detail/service_registry.hpp +++ b/libtorrent/include/asio/detail/service_registry.hpp @@ -27,6 +27,12 @@ #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 { @@ -156,6 +162,7 @@ private: service.id_ = &id; } +#if !defined(ASIO_NO_TYPEID) // Set a service's id. template void init_service_id(asio::io_service::service& service, @@ -164,6 +171,7 @@ private: service.type_info_ = &typeid(Service); service.id_ = 0; } +#endif // !defined(ASIO_NO_TYPEID) // Check if a service matches the given id. static bool service_id_matches( @@ -173,6 +181,7 @@ private: return service.id_ == &id; } +#if !defined(ASIO_NO_TYPEID) // Check if a service matches the given id. template static bool service_id_matches( @@ -181,6 +190,7 @@ private: { return service.type_info_ != 0 && *service.type_info_ == typeid(Service); } +#endif // !defined(ASIO_NO_TYPEID) // Mutex to protect access to internal data. mutable asio::detail::mutex mutex_; diff --git a/libtorrent/include/asio/detail/socket_ops.hpp b/libtorrent/include/asio/detail/socket_ops.hpp index 3d471b643..231bc61c5 100644 --- a/libtorrent/include/asio/detail/socket_ops.hpp +++ b/libtorrent/include/asio/detail/socket_ops.hpp @@ -175,6 +175,22 @@ inline int connect(socket_type s, const socket_addr_type* addr, 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); + return error_wrapper(::socketpair(af, type, protocol, sv), ec); +#endif +} + inline int listen(socket_type s, int backlog, asio::error_code& ec) { clear_error(ec); diff --git a/libtorrent/include/asio/detail/socket_types.hpp b/libtorrent/include/asio/detail/socket_types.hpp index b1e6e6781..b2acb6931 100644 --- a/libtorrent/include/asio/detail/socket_types.hpp +++ b/libtorrent/include/asio/detail/socket_types.hpp @@ -99,6 +99,7 @@ # endif # include # include +# include # include # include # include @@ -175,6 +176,7 @@ 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; diff --git a/libtorrent/include/asio/detail/strand_service.hpp b/libtorrent/include/asio/detail/strand_service.hpp index 42a5752f9..6deb040f6 100644 --- a/libtorrent/include/asio/detail/strand_service.hpp +++ b/libtorrent/include/asio/detail/strand_service.hpp @@ -20,6 +20,7 @@ #include "asio/detail/push_options.hpp" #include #include +#include #include #include "asio/detail/pop_options.hpp" @@ -54,19 +55,13 @@ public: #endif void add_ref() { - asio::detail::mutex::scoped_lock lock(mutex_); ++ref_count_; } void release() { - asio::detail::mutex::scoped_lock lock(mutex_); - --ref_count_; - if (ref_count_ == 0) - { - lock.unlock(); + if (--ref_count_ == 0) delete this; - } } private: @@ -147,7 +142,7 @@ public: strand_impl* prev_; // The reference count on the strand implementation. - size_t ref_count_; + boost::detail::atomic_count ref_count_; #if !defined(__BORLANDC__) friend void intrusive_ptr_add_ref(strand_impl* p) @@ -345,6 +340,16 @@ public: this_type* h(static_cast(base)); typedef handler_alloc_traits alloc_traits; handler_ptr ptr(h->handler_, h); + + // A sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Handler handler(h->handler_); + (void)handler; + + // Free the memory associated with the handler. + ptr.reset(); } private: diff --git a/libtorrent/include/asio/detail/task_io_service.hpp b/libtorrent/include/asio/detail/task_io_service.hpp index 6889845ca..beba1f251 100644 --- a/libtorrent/include/asio/detail/task_io_service.hpp +++ b/libtorrent/include/asio/detail/task_io_service.hpp @@ -15,6 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) +#if defined(ASIO_ENABLE_TWO_LOCK_QUEUE) +#include "asio/detail/task_io_service_2lock.hpp" +#else // defined(ASIO_ENABLE_TWO_LOCK_QUEUE) + #include "asio/detail/push_options.hpp" #include "asio/error_code.hpp" @@ -416,4 +420,6 @@ private: #include "asio/detail/pop_options.hpp" +#endif // defined(ASIO_ENABLE_TWO_LOCK_QUEUE) + #endif // ASIO_DETAIL_TASK_IO_SERVICE_HPP diff --git a/libtorrent/include/asio/detail/task_io_service_2lock.hpp b/libtorrent/include/asio/detail/task_io_service_2lock.hpp new file mode 100644 index 000000000..9b0221bbb --- /dev/null +++ b/libtorrent/include/asio/detail/task_io_service_2lock.hpp @@ -0,0 +1,462 @@ +// +// task_io_service_2lock.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_2LOCK_HPP +#define ASIO_DETAIL_TASK_IO_SERVICE_2LOCK_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/event.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/indirect_handler_queue.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/task_io_service_fwd.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +// An alternative task_io_service implementation based on a two-lock queue. + +template +class task_io_service + : public asio::detail::service_base > +{ +public: + typedef indirect_handler_queue handler_queue; + + // Constructor. + task_io_service(asio::io_service& io_service) + : asio::detail::service_base >(io_service), + front_mutex_(), + back_mutex_(), + task_(use_service(io_service)), + outstanding_work_(0), + front_stopped_(false), + back_stopped_(false), + back_shutdown_(false), + back_first_idle_thread_(0), + back_task_thread_(0) + { + handler_queue_.push(&task_handler_); + } + + void init(size_t /*concurrency_hint*/) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + asio::detail::mutex::scoped_lock back_lock(back_mutex_); + back_shutdown_ = true; + back_lock.unlock(); + + // Destroy handler objects. + while (handler_queue::handler* h = handler_queue_.pop()) + if (h != &task_handler_) + h->destroy(); + + // Reset handler queue to initial state. + handler_queue_.push(&task_handler_); + } + + // Run the event loop until interrupted or no more work. + size_t run(asio::error_code& ec) + { + if (outstanding_work_ == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + typename call_stack::context ctx(this); + + idle_thread_info this_idle_thread; + this_idle_thread.next = 0; + + size_t n = 0; + while (do_one(&this_idle_thread, ec)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } + + // Run until interrupted or one operation is performed. + size_t run_one(asio::error_code& ec) + { + if (outstanding_work_ == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + typename call_stack::context ctx(this); + + idle_thread_info this_idle_thread; + this_idle_thread.next = 0; + + return do_one(&this_idle_thread, ec); + } + + // Poll for operations without blocking. + size_t poll(asio::error_code& ec) + { + if (outstanding_work_ == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + typename call_stack::context ctx(this); + + size_t n = 0; + while (do_one(0, ec)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } + + // Poll for one operation without blocking. + size_t poll_one(asio::error_code& ec) + { + if (outstanding_work_ == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + typename call_stack::context ctx(this); + + return do_one(0, ec); + } + + // Interrupt the event processing loop. + void stop() + { + asio::detail::mutex::scoped_lock front_lock(front_mutex_); + front_stopped_ = true; + front_lock.unlock(); + + asio::detail::mutex::scoped_lock back_lock(back_mutex_); + back_stopped_ = true; + interrupt_all_idle_threads(back_lock); + } + + // Reset in preparation for a subsequent run invocation. + void reset() + { + asio::detail::mutex::scoped_lock front_lock(front_mutex_); + front_stopped_ = false; + front_lock.unlock(); + + asio::detail::mutex::scoped_lock back_lock(back_mutex_); + back_stopped_ = false; + } + + // Notify that some work has started. + void work_started() + { + ++outstanding_work_; + } + + // Notify that some work has finished. + void work_finished() + { + if (--outstanding_work_ == 0) + stop(); + } + + // Request invocation of the given handler. + template + void dispatch(Handler handler) + { + if (call_stack::contains(this)) + asio_handler_invoke_helpers::invoke(handler, &handler); + else + post(handler); + } + + // Request invocation of the given handler and return immediately. + template + void post(Handler handler) + { + // Allocate and construct an operation to wrap the handler. + handler_queue::scoped_ptr ptr(handler_queue::wrap(handler)); + + asio::detail::mutex::scoped_lock back_lock(back_mutex_); + + // If the service has been shut down we silently discard the handler. + if (back_shutdown_) + return; + + // Add the handler to the end of the queue. + handler_queue_.push(ptr.get()); + ptr.release(); + + // An undelivered handler is treated as unfinished work. + ++outstanding_work_; + + // Wake up a thread to execute the handler. + interrupt_one_idle_thread(back_lock); + } + +private: + struct idle_thread_info; + + size_t do_one(idle_thread_info* this_idle_thread, + asio::error_code& ec) + { + bool task_has_run = false; + for (;;) + { + // The front lock must be held before we can pop items from the queue. + asio::detail::mutex::scoped_lock front_lock(front_mutex_); + if (front_stopped_) + { + ec = asio::error_code(); + return 0; + } + + if (handler_queue::handler* h = handler_queue_.pop()) + { + if (h == &task_handler_) + { + bool more_handlers = handler_queue_.poppable(); + unsigned long front_version = handler_queue_.front_version(); + front_lock.unlock(); + + // The task is always added to the back of the queue when we exit + // this block. + task_cleanup c(*this); + + // If we're polling and the task has already run then we're done. + bool polling = !this_idle_thread; + if (task_has_run && polling) + { + ec = asio::error_code(); + return 0; + } + + // If we're considering going idle we need to check whether the queue + // is still empty. If it is, add the thread to the list of idle + // threads. + if (!more_handlers && !polling) + { + asio::detail::mutex::scoped_lock back_lock(back_mutex_); + if (back_stopped_) + { + ec = asio::error_code(); + return 0; + } + else if (front_version == handler_queue_.back_version()) + { + back_task_thread_ = this_idle_thread; + } + else + { + more_handlers = true; + } + } + + // Run the task. May throw an exception. Only block if the handler + // queue is empty and we're not polling, otherwise we want to return + // as soon as possible. + task_has_run = true; + task_.run(!more_handlers && !polling); + } + else + { + front_lock.unlock(); + handler_cleanup c(*this); + + // Invoke the handler. May throw an exception. + h->invoke(); // invoke() deletes the handler object + + ec = asio::error_code(); + return 1; + } + } + else if (this_idle_thread) + { + unsigned long front_version = handler_queue_.front_version(); + front_lock.unlock(); + + // If we're considering going idle we need to check whether the queue + // is still empty. If it is, add the thread to the list of idle + // threads. + asio::detail::mutex::scoped_lock back_lock(back_mutex_); + if (back_stopped_) + { + ec = asio::error_code(); + return 0; + } + else if (front_version == handler_queue_.back_version()) + { + this_idle_thread->next = back_first_idle_thread_; + back_first_idle_thread_ = this_idle_thread; + this_idle_thread->wakeup_event.clear(back_lock); + this_idle_thread->wakeup_event.wait(back_lock); + } + } + else + { + ec = asio::error_code(); + return 0; + } + } + } + + // Interrupt a single idle thread. + void interrupt_one_idle_thread( + asio::detail::mutex::scoped_lock& back_lock) + { + if (back_first_idle_thread_) + { + idle_thread_info* idle_thread = back_first_idle_thread_; + back_first_idle_thread_ = idle_thread->next; + idle_thread->next = 0; + idle_thread->wakeup_event.signal(back_lock); + } + else if (back_task_thread_) + { + back_task_thread_ = 0; + task_.interrupt(); + } + } + + // Interrupt all idle threads. + void interrupt_all_idle_threads( + asio::detail::mutex::scoped_lock& back_lock) + { + while (back_first_idle_thread_) + { + idle_thread_info* idle_thread = back_first_idle_thread_; + back_first_idle_thread_ = idle_thread->next; + idle_thread->next = 0; + idle_thread->wakeup_event.signal(back_lock); + } + + if (back_task_thread_) + { + back_task_thread_ = 0; + task_.interrupt(); + } + } + + // Helper class to perform task-related operations on block exit. + class task_cleanup; + friend class task_cleanup; + class task_cleanup + { + public: + task_cleanup(task_io_service& task_io_svc) + : task_io_service_(task_io_svc) + { + } + + ~task_cleanup() + { + // Reinsert the task at the end of the handler queue. + asio::detail::mutex::scoped_lock back_lock( + task_io_service_.back_mutex_); + task_io_service_.back_task_thread_ = 0; + task_io_service_.handler_queue_.push(&task_io_service_.task_handler_); + } + + private: + task_io_service& task_io_service_; + }; + + // Helper class to perform handler-related operations on block exit. + class handler_cleanup + { + public: + handler_cleanup(task_io_service& task_io_svc) + : task_io_service_(task_io_svc) + { + } + + ~handler_cleanup() + { + task_io_service_.work_finished(); + } + + private: + task_io_service& task_io_service_; + }; + + // Mutexes to protect access to internal data. + asio::detail::mutex front_mutex_; + asio::detail::mutex back_mutex_; + + // The task to be run by this service. + Task& task_; + + // Handler object to represent the position of the task in the queue. + class task_handler + : public handler_queue::handler + { + public: + task_handler() + : handler_queue::handler(0, 0) + { + } + } task_handler_; + + // The count of unfinished work. + boost::detail::atomic_count outstanding_work_; + + // The queue of handlers that are ready to be delivered. + handler_queue handler_queue_; + + // Flag to indicate that the dispatcher has been stopped. + bool front_stopped_; + bool back_stopped_; + + // Flag to indicate that the dispatcher has been shut down. + bool back_shutdown_; + + // Structure containing information about an idle thread. + struct idle_thread_info + { + event wakeup_event; + idle_thread_info* next; + }; + + // The number of threads that are currently idle. + idle_thread_info* back_first_idle_thread_; + + // The thread that is currently blocked on the task. + idle_thread_info* back_task_thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TASK_IO_SERVICE_2LOCK_HPP diff --git a/libtorrent/include/asio/detail/timer_queue.hpp b/libtorrent/include/asio/detail/timer_queue.hpp index ed12ddaa9..cdc94ab7a 100644 --- a/libtorrent/include/asio/detail/timer_queue.hpp +++ b/libtorrent/include/asio/detail/timer_queue.hpp @@ -27,6 +27,7 @@ #include "asio/detail/pop_options.hpp" #include "asio/error.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/hash_map.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/timer_queue_base.hpp" @@ -50,7 +51,7 @@ public: : timers_(), heap_(), cancelled_timers_(0), - cleanup_timers_(0) + complete_timers_(0) { } @@ -101,6 +102,8 @@ public: // Get the time for the timer that is earliest in the queue. virtual boost::posix_time::time_duration wait_duration() const { + if (heap_.empty()) + return boost::posix_time::pos_infin; return Time_Traits::to_posix_duration( Time_Traits::subtract(heap_[0]->time_, Time_Traits::now())); } @@ -113,10 +116,10 @@ public: { timer_base* t = heap_[0]; remove_timer(t); + t->result_ = asio::error_code(); t->prev_ = 0; - t->next_ = cleanup_timers_; - cleanup_timers_ = t; - t->invoke(asio::error_code()); + t->next_ = complete_timers_; + complete_timers_ = t; } } @@ -152,17 +155,23 @@ public: while (cancelled_timers_) { timer_base* this_timer = cancelled_timers_; + this_timer->result_ = asio::error::operation_aborted; cancelled_timers_ = this_timer->next_; - this_timer->next_ = cleanup_timers_; - cleanup_timers_ = this_timer; - this_timer->invoke(asio::error::operation_aborted); + this_timer->next_ = complete_timers_; + complete_timers_ = this_timer; } } - // Destroy timers that are waiting to be cleaned up. - virtual void cleanup_timers() + // Complete any timers that are waiting to be completed. + virtual void complete_timers() { - destroy_timer_list(cleanup_timers_); + while (complete_timers_) + { + timer_base* this_timer = complete_timers_; + complete_timers_ = this_timer->next_; + this_timer->next_ = 0; + this_timer->complete(); + } } // Destroy all timers. @@ -180,7 +189,7 @@ public: heap_.clear(); timers_.clear(); destroy_timer_list(cancelled_timers_); - destroy_timer_list(cleanup_timers_); + destroy_timer_list(complete_timers_); } private: @@ -189,27 +198,27 @@ private: class timer_base { public: - // Perform the timer operation and then destroy. - void invoke(const asio::error_code& result) + // Delete the timer and post the handler. + void complete() { - invoke_func_(this, result); + complete_func_(this, result_); } - // Destroy the timer operation. + // Delete the timer. void destroy() { destroy_func_(this); } protected: - typedef void (*invoke_func_type)(timer_base*, + typedef void (*complete_func_type)(timer_base*, const asio::error_code&); typedef void (*destroy_func_type)(timer_base*); // Constructor. - timer_base(invoke_func_type invoke_func, destroy_func_type destroy_func, + timer_base(complete_func_type complete_func, destroy_func_type destroy_func, const time_type& time, void* token) - : invoke_func_(invoke_func), + : complete_func_(complete_func), destroy_func_(destroy_func), time_(time), token_(token), @@ -228,13 +237,16 @@ private: private: friend class timer_queue; - // The function to be called to dispatch the handler. - invoke_func_type invoke_func_; + // The function to be called to delete the timer and post the handler. + complete_func_type complete_func_; - // The function to be called to destroy the handler. + // The function to be called to delete the timer. destroy_func_type destroy_func_; - // The time when the operation should fire. + // The result of the timer operation. + asio::error_code result_; + + // The time when the timer should fire. time_type time_; // The token associated with the timer. @@ -258,23 +270,52 @@ private: public: // Constructor. timer(const time_type& time, Handler handler, void* token) - : timer_base(&timer::invoke_handler, + : timer_base(&timer::complete_handler, &timer::destroy_handler, time, token), handler_(handler) { } - // Invoke the handler and then destroy it. - static void invoke_handler(timer_base* base, + // Delete the timer and post the handler. + static void complete_handler(timer_base* base, const asio::error_code& result) { - static_cast*>(base)->handler_(result); + // Take ownership of the timer object. + typedef timer this_type; + this_type* this_timer(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(this_timer->handler_, this_timer); + + // Make a copy of the error_code and the handler so that the memory can + // be deallocated before the upcall is made. + asio::error_code ec(result); + Handler handler(this_timer->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Make the upcall. + handler(ec); } - // Destroy the handler. + // Delete the timer. static void destroy_handler(timer_base* base) { - delete static_cast*>(base); + // Take ownership of the timer object. + typedef timer this_type; + this_type* this_timer(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(this_timer->handler_, this_timer); + + // A sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Handler handler(this_timer->handler_); + (void)handler; + + // Free the memory associated with the handler. + ptr.reset(); } private: @@ -383,8 +424,8 @@ private: // The list of timers to be cancelled. timer_base* cancelled_timers_; - // The list of timers to be destroyed. - timer_base* cleanup_timers_; + // The list of timers waiting to be completed. + timer_base* complete_timers_; }; } // namespace detail diff --git a/libtorrent/include/asio/detail/timer_queue_base.hpp b/libtorrent/include/asio/detail/timer_queue_base.hpp index 30a2a216e..57f212b05 100644 --- a/libtorrent/include/asio/detail/timer_queue_base.hpp +++ b/libtorrent/include/asio/detail/timer_queue_base.hpp @@ -47,8 +47,8 @@ public: // Dispatch any pending cancels for timers. virtual void dispatch_cancellations() = 0; - // Destroy timers that are waiting to be cleaned up. - virtual void cleanup_timers() = 0; + // Complete all timers that are waiting to be completed. + virtual void complete_timers() = 0; // Destroy all timers. virtual void destroy_timers() = 0; diff --git a/libtorrent/include/asio/detail/win_iocp_handle_service.hpp b/libtorrent/include/asio/detail/win_iocp_handle_service.hpp new file mode 100644 index 000000000..99c472c34 --- /dev/null +++ b/libtorrent/include/asio/detail/win_iocp_handle_service.hpp @@ -0,0 +1,832 @@ +// +// win_iocp_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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 +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/win_iocp_io_service.hpp" + +namespace asio { +namespace detail { + +class win_iocp_handle_service + : public asio::detail::service_base +{ +public: + // Base class for all operations. + typedef win_iocp_io_service::operation operation; + + // 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) + : asio::detail::service_base(io_service), + iocp_service_(asio::use_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(&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 + 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 + 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; + } + + // Find first buffer of non-zero length. + asio::const_buffer buffer; + typename ConstBufferSequence::const_iterator iter = buffers.begin(); + typename ConstBufferSequence::const_iterator end = buffers.end(); + for (DWORD i = 0; iter != end; ++iter, ++i) + { + buffer = asio::const_buffer(*iter); + if (asio::buffer_size(buffer) != 0) + break; + } + + // 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(buffer), + static_cast(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()); + } + + ec = asio::error_code(); + return bytes_transferred; + } + + template + class write_operation + : public operation + { + public: + write_operation(win_iocp_io_service& io_service, + const ConstBufferSequence& buffers, Handler handler) + : operation(io_service, + &write_operation::do_completion_impl, + &write_operation::destroy_impl), + work_(io_service.get_io_service()), + buffers_(buffers), + handler_(handler) + { + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef write_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + typename ConstBufferSequence::const_iterator iter + = handler_op->buffers_.begin(); + typename ConstBufferSequence::const_iterator end + = handler_op->buffers_.end(); + while (iter != end) + { + asio::const_buffer buffer(*iter); + asio::buffer_cast(buffer); + ++iter; + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio_handler_invoke_helpers::invoke( + bind_handler(handler, ec, bytes_transferred), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef write_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + + // A sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Handler handler(handler_op->handler_); + (void)handler; + + // Free the memory associated with the handler. + ptr.reset(); + } + + asio::io_service::work work_; + ConstBufferSequence buffers_; + Handler handler_; + }; + + // Start an asynchronous write. The data being written must be valid for the + // lifetime of the asynchronous operation. + template + 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 + void async_write_some_at(implementation_type& impl, boost::uint64_t offset, + const ConstBufferSequence& buffers, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + return; + } + + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Allocate and construct an operation to wrap the handler. + typedef write_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, iocp_service_, buffers, handler); + + // Find first buffer of non-zero length. + asio::const_buffer buffer; + typename ConstBufferSequence::const_iterator iter = buffers.begin(); + typename ConstBufferSequence::const_iterator end = buffers.end(); + for (DWORD i = 0; iter != end; ++iter, ++i) + { + buffer = asio::const_buffer(*iter); + if (asio::buffer_size(buffer) != 0) + break; + } + + // A request to write 0 bytes on a handle is a no-op. + if (asio::buffer_size(buffer) == 0) + { + asio::io_service::work work(this->get_io_service()); + ptr.reset(); + asio::error_code error; + iocp_service_.post(bind_handler(handler, error, 0)); + return; + } + + // Write the data. + DWORD bytes_transferred = 0; + ptr.get()->Offset = offset & 0xFFFFFFFF; + ptr.get()->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::WriteFile(impl.handle_, + asio::buffer_cast(buffer), + static_cast(asio::buffer_size(buffer)), + &bytes_transferred, ptr.get()); + DWORD last_error = ::GetLastError(); + + // Check if the operation completed immediately. + if (!ok && last_error != ERROR_IO_PENDING) + { + asio::io_service::work work(this->get_io_service()); + ptr.reset(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + iocp_service_.post(bind_handler(handler, ec, bytes_transferred)); + } + else + { + ptr.release(); + } + } + + // Read some data. Returns the number of bytes received. + template + 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 + 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; + } + + // Find first buffer of non-zero length. + asio::mutable_buffer buffer; + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + for (DWORD i = 0; iter != end; ++iter, ++i) + { + buffer = asio::mutable_buffer(*iter); + if (asio::buffer_size(buffer) != 0) + break; + } + + // 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(buffer), + static_cast(asio::buffer_size(buffer)), 0, &overlapped); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error != ERROR_IO_PENDING) + { + 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()); + } + } + + ec = asio::error_code(); + return bytes_transferred; + } + + template + class read_operation + : public operation + { + public: + read_operation(win_iocp_io_service& io_service, + const MutableBufferSequence& buffers, Handler handler) + : operation(io_service, + &read_operation< + MutableBufferSequence, Handler>::do_completion_impl, + &read_operation< + MutableBufferSequence, Handler>::destroy_impl), + work_(io_service.get_io_service()), + buffers_(buffers), + handler_(handler) + { + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef read_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + typename MutableBufferSequence::const_iterator iter + = handler_op->buffers_.begin(); + typename MutableBufferSequence::const_iterator end + = handler_op->buffers_.end(); + while (iter != end) + { + asio::mutable_buffer buffer(*iter); + asio::buffer_cast(buffer); + ++iter; + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Check for the end-of-file condition. + asio::error_code ec(last_error, + asio::error::get_system_category()); + if (!ec && bytes_transferred == 0 || last_error == 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. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio_handler_invoke_helpers::invoke( + bind_handler(handler, ec, bytes_transferred), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef read_operation op_type; + op_type* handler_op(static_cast(op)); + typedef asio::detail::handler_alloc_traits< + Handler, op_type> alloc_traits; + asio::detail::handler_ptr ptr( + handler_op->handler_, handler_op); + + // A sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Handler handler(handler_op->handler_); + (void)handler; + + // Free the memory associated with the handler. + ptr.reset(); + } + + asio::io_service::work work_; + 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 + 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 + void async_read_some_at(implementation_type& impl, boost::uint64_t offset, + const MutableBufferSequence& buffers, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + return; + } + + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Allocate and construct an operation to wrap the handler. + typedef read_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, iocp_service_, buffers, handler); + + // Find first buffer of non-zero length. + asio::mutable_buffer buffer; + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + for (DWORD i = 0; iter != end; ++iter, ++i) + { + buffer = asio::mutable_buffer(*iter); + if (asio::buffer_size(buffer) != 0) + break; + } + + // A request to receive 0 bytes on a stream handle is a no-op. + if (asio::buffer_size(buffer) == 0) + { + asio::io_service::work work(this->get_io_service()); + ptr.reset(); + asio::error_code error; + iocp_service_.post(bind_handler(handler, error, 0)); + return; + } + + // Read some data. + DWORD bytes_transferred = 0; + ptr.get()->Offset = offset & 0xFFFFFFFF; + ptr.get()->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::ReadFile(impl.handle_, + asio::buffer_cast(buffer), + static_cast(asio::buffer_size(buffer)), + &bytes_transferred, ptr.get()); + DWORD last_error = ::GetLastError(); + if (!ok && last_error != ERROR_IO_PENDING) + { + asio::io_service::work work(this->get_io_service()); + ptr.reset(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + iocp_service_.post(bind_handler(handler, ec, bytes_transferred)); + } + else + { + 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 + void async_write_some(implementation_type& impl, + const null_buffers& buffers, Handler handler); + template + 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 + void async_read_some(implementation_type& impl, + const null_buffers& buffers, Handler handler); + template + void async_read_some_at(implementation_type& impl, boost::uint64_t offset, + const null_buffers& buffers, Handler handler); + + // 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/libtorrent/include/asio/detail/win_iocp_io_service.hpp b/libtorrent/include/asio/detail/win_iocp_io_service.hpp index 6dec9c9b4..7f6d631c0 100644 --- a/libtorrent/include/asio/detail/win_iocp_io_service.hpp +++ b/libtorrent/include/asio/detail/win_iocp_io_service.hpp @@ -149,9 +149,20 @@ public: } // Register a handle with the IO completion port. - void register_handle(HANDLE handle) + asio::error_code register_handle( + HANDLE handle, asio::error_code& ec) { - ::CreateIoCompletionPort(handle, iocp_.handle, 0, 0); + 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. @@ -424,7 +435,7 @@ private: { timer_queues_copy_[i]->dispatch_timers(); timer_queues_copy_[i]->dispatch_cancellations(); - timer_queues_copy_[i]->cleanup_timers(); + timer_queues_copy_[i]->complete_timers(); } } catch (...) @@ -504,16 +515,18 @@ private: } 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) { - // Relinquish responsibility for dispatching timers. - if (dispatching_timers) - { - ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id); - } - // Wake up next thread that is blocked on GetQueuedCompletionStatus. if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) { @@ -636,6 +649,16 @@ private: op_type* handler_op(static_cast(op)); typedef handler_alloc_traits alloc_traits; handler_ptr ptr(handler_op->handler_, handler_op); + + // A sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Handler handler(handler_op->handler_); + (void)handler; + + // Free the memory associated with the handler. + ptr.reset(); } win_iocp_io_service& io_service_; diff --git a/libtorrent/include/asio/detail/win_iocp_serial_port_service.hpp b/libtorrent/include/asio/detail/win_iocp_serial_port_service.hpp new file mode 100644 index 000000000..55f32ae18 --- /dev/null +++ b/libtorrent/include/asio/detail/win_iocp_serial_port_service.hpp @@ -0,0 +1,292 @@ +// +// win_iocp_serial_port_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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 +#include +#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 asio::detail::service_base +{ +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) + : asio::detail::service_base< + win_iocp_serial_port_service>(io_service), + handle_service_( + asio::use_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 + 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 + 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& impl, + asio::error_code& ec) + { + ec = asio::error::operation_not_supported; + return ec; + } + + // Write the given data. Returns the number of bytes sent. + template + 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 + 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 + 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 + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, Handler handler) + { + handle_service_.async_read_some(impl, buffers, handler); + } + +private: + // The handle service 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/libtorrent/include/asio/detail/win_iocp_socket_service.hpp b/libtorrent/include/asio/detail/win_iocp_socket_service.hpp index 770ca4899..13943bab0 100644 --- a/libtorrent/include/asio/detail/win_iocp_socket_service.hpp +++ b/libtorrent/include/asio/detail/win_iocp_socket_service.hpp @@ -24,6 +24,7 @@ #include "asio/detail/push_options.hpp" #include #include +#include #include #include "asio/detail/pop_options.hpp" @@ -112,6 +113,9 @@ public: endpoint_type remote_endpoint_; }; + // The type of the reactor used for connect operations. + typedef detail::select_reactor reactor_type; + // The implementation type of the socket. class implementation_type { @@ -155,6 +159,9 @@ public: // The protocol associated with the socket. protocol_type protocol_; + // Per-descriptor data used by the reactor. + reactor_type::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. @@ -168,9 +175,6 @@ public: implementation_type* prev_; }; - // The type of the reactor used for connect operations. - typedef detail::select_reactor reactor_type; - // The maximum number of buffers to support in a single operation. enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; @@ -251,7 +255,8 @@ public: return ec; HANDLE sock_as_handle = reinterpret_cast(sock.get()); - iocp_service_.register_handle(sock_as_handle); + if (iocp_service_.register_handle(sock_as_handle, ec)) + return ec; impl.socket_ = sock.release(); impl.flags_ = 0; @@ -272,7 +277,8 @@ public: return ec; } - iocp_service_.register_handle(native_socket.as_handle()); + if (iocp_service_.register_handle(native_socket.as_handle(), ec)) + return ec; impl.socket_ = native_socket; impl.flags_ = 0; @@ -301,7 +307,7 @@ public: interlocked_compare_exchange_pointer( reinterpret_cast(&reactor_), 0, 0)); if (reactor) - reactor->close_descriptor(impl.socket_); + reactor->close_descriptor(impl.socket_, impl.reactor_data_); if (socket_ops::close(impl.socket_, ec) == socket_error_retval) return ec; @@ -331,6 +337,7 @@ public: if (!is_open(impl)) { ec = asio::error::bad_descriptor; + return ec; } else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) @@ -699,6 +706,22 @@ public: 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 class send_operation : public operation @@ -775,6 +798,16 @@ public: op_type* handler_op(static_cast(op)); typedef handler_alloc_traits alloc_traits; handler_ptr ptr(handler_op->handler_, handler_op); + + // A sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Handler handler(handler_op->handler_); + (void)handler; + + // Free the memory associated with the handler. + ptr.reset(); } asio::io_service::work work_; @@ -857,6 +890,65 @@ public: } } + template + class null_buffers_operation + { + public: + null_buffers_operation(asio::io_service& io_service, Handler handler) + : work_(io_service), + handler_(handler) + { + } + + bool perform(asio::error_code&, + std::size_t& bytes_transferred) + { + bytes_transferred = 0; + return true; + } + + void complete(const asio::error_code& ec, + std::size_t bytes_transferred) + { + work_.get_io_service().post(bind_handler( + handler_, ec, bytes_transferred)); + } + + private: + asio::io_service::work work_; + Handler handler_; + }; + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send(implementation_type& impl, const null_buffers&, + socket_base::message_flags, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + // Check if the reactor was already obtained from the io_service. + reactor_type* reactor = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (!reactor) + { + reactor = &(asio::use_service( + this->get_io_service())); + interlocked_exchange_pointer( + reinterpret_cast(&reactor_), reactor); + } + + reactor->start_write_op(impl.socket_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler), + false); + } + } + // Send a datagram to the specified endpoint. Returns the number of bytes // sent. template @@ -901,6 +993,23 @@ public: 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 class send_to_operation : public operation @@ -968,6 +1077,16 @@ public: op_type* handler_op(static_cast(op)); typedef handler_alloc_traits alloc_traits; handler_ptr ptr(handler_op->handler_, handler_op); + + // A sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Handler handler(handler_op->handler_); + (void)handler; + + // Free the memory associated with the handler. + ptr.reset(); } asio::io_service::work work_; @@ -1037,6 +1156,36 @@ public: } } + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send_to(implementation_type& impl, const null_buffers&, + socket_base::message_flags, const endpoint_type&, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + // Check if the reactor was already obtained from the io_service. + reactor_type* reactor = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (!reactor) + { + reactor = &(asio::use_service( + this->get_io_service())); + interlocked_exchange_pointer( + reinterpret_cast(&reactor_), reactor); + } + + reactor->start_write_op(impl.socket_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler), + false); + } + } + // Receive some data from the peer. Returns the number of bytes received. template size_t receive(implementation_type& impl, @@ -1086,7 +1235,7 @@ public: asio::error::get_system_category()); return 0; } - if (bytes_transferred == 0) + if (bytes_transferred == 0 && impl.protocol_.type() == SOCK_STREAM) { ec = asio::error::eof; return 0; @@ -1096,12 +1245,28 @@ public: 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 class receive_operation : public operation { public: - receive_operation(win_iocp_io_service& io_service, + receive_operation(int protocol_type, win_iocp_io_service& io_service, weak_cancel_token_type cancel_token, const MutableBufferSequence& buffers, Handler handler) : operation(io_service, @@ -1109,6 +1274,7 @@ public: MutableBufferSequence, Handler>::do_completion_impl, &receive_operation< MutableBufferSequence, Handler>::destroy_impl), + protocol_type_(protocol_type), work_(io_service.get_io_service()), cancel_token_(cancel_token), buffers_(buffers), @@ -1156,7 +1322,9 @@ public: } // Check for connection closed. - else if (!ec && bytes_transferred == 0) + else if (!ec && bytes_transferred == 0 + && handler_op->protocol_type_ == SOCK_STREAM + && !boost::is_same::value) { ec = asio::error::eof; } @@ -1180,8 +1348,19 @@ public: op_type* handler_op(static_cast(op)); typedef handler_alloc_traits alloc_traits; handler_ptr ptr(handler_op->handler_, handler_op); + + // A sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Handler handler(handler_op->handler_); + (void)handler; + + // Free the memory associated with the handler. + ptr.reset(); } + int protocol_type_; asio::io_service::work work_; weak_cancel_token_type cancel_token_; MutableBufferSequence buffers_; @@ -1214,8 +1393,9 @@ public: typedef receive_operation value_type; typedef handler_alloc_traits alloc_traits; raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, iocp_service_, - impl.cancel_token_, buffers, handler); + int protocol_type = impl.protocol_.type(); + handler_ptr ptr(raw_ptr, protocol_type, + iocp_service_, impl.cancel_token_, buffers, handler); // Copy buffers into WSABUF array. ::WSABUF bufs[max_buffers]; @@ -1261,6 +1441,85 @@ public: } } + // Wait until data can be received without blocking. + template + void async_receive(implementation_type& impl, const null_buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else if (impl.protocol_.type() == SOCK_STREAM) + { + // For stream sockets on Windows, we may issue a 0-byte overlapped + // WSARecv to wait until there is data available on the socket. + +#if defined(ASIO_ENABLE_CANCELIO) + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Allocate and construct an operation to wrap the handler. + typedef receive_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + int protocol_type = impl.protocol_.type(); + handler_ptr ptr(raw_ptr, protocol_type, + iocp_service_, impl.cancel_token_, buffers, handler); + + // Issue a receive operation with an empty buffer. + ::WSABUF buf = { 0, 0 }; + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(impl.socket_, &buf, 1, + &bytes_transferred, &recv_flags, ptr.get(), 0); + DWORD last_error = ::WSAGetLastError(); + if (result != 0 && last_error != WSA_IO_PENDING) + { + asio::io_service::work work(this->get_io_service()); + ptr.reset(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + iocp_service_.post(bind_handler(handler, ec, bytes_transferred)); + } + else + { + ptr.release(); + } + } + else + { + // Check if the reactor was already obtained from the io_service. + reactor_type* reactor = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (!reactor) + { + reactor = &(asio::use_service( + this->get_io_service())); + interlocked_exchange_pointer( + reinterpret_cast(&reactor_), reactor); + } + + if (flags & socket_base::message_out_of_band) + { + reactor->start_except_op(impl.socket_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler)); + } + else + { + reactor->start_read_op(impl.socket_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler), + false); + } + } + } + // Receive a datagram with the endpoint of the sender. Returns the number of // bytes received. template @@ -1302,7 +1561,7 @@ public: asio::error::get_system_category()); return 0; } - if (bytes_transferred == 0) + if (bytes_transferred == 0 && impl.protocol_.type() == SOCK_STREAM) { ec = asio::error::eof; return 0; @@ -1314,12 +1573,32 @@ public: 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 class receive_from_operation : public operation { public: - receive_from_operation(win_iocp_io_service& io_service, + receive_from_operation(int protocol_type, win_iocp_io_service& io_service, endpoint_type& endpoint, const MutableBufferSequence& buffers, Handler handler) : operation(io_service, @@ -1327,6 +1606,7 @@ public: MutableBufferSequence, Handler>::do_completion_impl, &receive_from_operation< MutableBufferSequence, Handler>::destroy_impl), + protocol_type_(protocol_type), endpoint_(endpoint), endpoint_size_(static_cast(endpoint.capacity())), work_(io_service.get_io_service()), @@ -1373,7 +1653,8 @@ public: } // Check for connection closed. - if (!ec && bytes_transferred == 0) + if (!ec && bytes_transferred == 0 + && handler_op->protocol_type_ == SOCK_STREAM) { ec = asio::error::eof; } @@ -1400,8 +1681,19 @@ public: op_type* handler_op(static_cast(op)); typedef handler_alloc_traits alloc_traits; handler_ptr ptr(handler_op->handler_, handler_op); + + // A sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Handler handler(handler_op->handler_); + (void)handler; + + // Free the memory associated with the handler. + ptr.reset(); } + int protocol_type_; endpoint_type& endpoint_; int endpoint_size_; asio::io_service::work work_; @@ -1436,8 +1728,9 @@ public: typedef receive_from_operation value_type; typedef handler_alloc_traits alloc_traits; raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, iocp_service_, - sender_endp, buffers, handler); + int protocol_type = impl.protocol_.type(); + handler_ptr ptr(raw_ptr, protocol_type, + iocp_service_, sender_endp, buffers, handler); // Copy buffers into WSABUF array. ::WSABUF bufs[max_buffers]; @@ -1472,6 +1765,48 @@ public: } } + // Wait until data can be received without blocking. + template + void async_receive_from(implementation_type& impl, + const null_buffers&, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler handler) + { + if (!is_open(impl)) + { + this->get_io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + // Check if the reactor was already obtained from the io_service. + reactor_type* reactor = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (!reactor) + { + reactor = &(asio::use_service( + this->get_io_service())); + interlocked_exchange_pointer( + reinterpret_cast(&reactor_), reactor); + } + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + if (flags & socket_base::message_out_of_band) + { + reactor->start_except_op(impl.socket_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler)); + } + else + { + reactor->start_read_op(impl.socket_, impl.reactor_data_, + null_buffers_operation(this->get_io_service(), handler), + false); + } + } + } + // Accept a new connection. template asio::error_code accept(implementation_type& impl, Socket& peer, @@ -1492,7 +1827,6 @@ public: for (;;) { - asio::error_code ec; socket_holder new_socket; std::size_t addr_len = 0; if (peer_endpoint) @@ -1570,8 +1904,7 @@ public: } private: - static void do_completion_impl(operation* op, - DWORD last_error, size_t bytes_transferred) + static void do_completion_impl(operation* op, DWORD last_error, size_t) { // Take ownership of the operation object. typedef accept_operation op_type; @@ -1714,6 +2047,16 @@ public: op_type* handler_op(static_cast(op)); typedef handler_alloc_traits alloc_traits; handler_ptr ptr(handler_op->handler_, handler_op); + + // A sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + Handler handler(handler_op->handler_); + (void)handler; + + // Free the memory associated with the handler. + ptr.reset(); } win_iocp_io_service& io_service_; @@ -1832,58 +2175,38 @@ public: } template - class connect_handler + class connect_operation { public: - connect_handler(socket_type socket, bool user_set_non_blocking, - boost::shared_ptr completed, - asio::io_service& io_service, - reactor_type& reactor, Handler handler) + connect_operation(socket_type socket, bool user_set_non_blocking, + asio::io_service& io_service, Handler handler) : socket_(socket), user_set_non_blocking_(user_set_non_blocking), - completed_(completed), io_service_(io_service), - reactor_(reactor), work_(io_service), handler_(handler) { } - bool operator()(const asio::error_code& result) + bool perform(asio::error_code& ec, + std::size_t& bytes_transferred) { - // Check whether a handler has already been called for the connection. - // If it has, then we don't want to do anything in this handler. - if (*completed_) - return true; - - // Cancel the other reactor operation for the connection. - *completed_ = true; - reactor_.enqueue_cancel_ops_unlocked(socket_); - // Check whether the operation was successful. - if (result) - { - io_service_.post(bind_handler(handler_, result)); + if (ec) return true; - } // Get the error code from the connect operation. int connect_error = 0; size_t connect_error_len = sizeof(connect_error); - asio::error_code ec; if (socket_ops::getsockopt(socket_, SOL_SOCKET, SO_ERROR, &connect_error, &connect_error_len, ec) == socket_error_retval) - { - io_service_.post(bind_handler(handler_, ec)); return true; - } // If connection failed then post the handler with the error code. if (connect_error) { ec = asio::error_code(connect_error, asio::error::get_system_category()); - io_service_.post(bind_handler(handler_, ec)); return true; } @@ -1892,24 +2215,23 @@ public: { ioctl_arg_type non_blocking = 0; if (socket_ops::ioctl(socket_, FIONBIO, &non_blocking, ec)) - { - io_service_.post(bind_handler(handler_, ec)); return true; - } } // Post the result of the successful connection operation. ec = asio::error_code(); - io_service_.post(bind_handler(handler_, ec)); return true; } + void complete(const asio::error_code& ec, std::size_t) + { + io_service_.post(bind_handler(handler_, ec)); + } + private: socket_type socket_; bool user_set_non_blocking_; - boost::shared_ptr completed_; asio::io_service& io_service_; - reactor_type& reactor_; asio::io_service::work work_; Handler handler_; }; @@ -1977,11 +2299,11 @@ public: // The connection is happening in the background, and we need to wait // until the socket becomes writeable. boost::shared_ptr completed(new bool(false)); - reactor->start_write_and_except_ops(impl.socket_, - connect_handler( + reactor->start_connect_op(impl.socket_, impl.reactor_data_, + connect_operation( impl.socket_, (impl.flags_ & implementation_type::user_set_non_blocking) != 0, - completed, this->get_io_service(), *reactor, handler)); + this->get_io_service(), handler)); } else { @@ -2012,7 +2334,7 @@ private: interlocked_compare_exchange_pointer( reinterpret_cast(&reactor_), 0, 0)); if (reactor) - reactor->close_descriptor(impl.socket_); + reactor->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 diff --git a/libtorrent/include/asio/detail/win_thread.hpp b/libtorrent/include/asio/detail/win_thread.hpp index acd8b51e3..17d912565 100644 --- a/libtorrent/include/asio/detail/win_thread.hpp +++ b/libtorrent/include/asio/detail/win_thread.hpp @@ -43,17 +43,53 @@ class win_thread : private noncopyable { public: + // The purpose of the thread. + enum purpose { internal, external }; + // Constructor. template - win_thread(Function f) + win_thread(Function f, purpose p = internal) + : exit_event_(0) { std::auto_ptr arg(new func(f)); + + ::HANDLE entry_event = 0; + if (p == internal) + { + 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(::_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()), @@ -61,18 +97,36 @@ public: 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(thread_, INFINITE); + if (exit_event_) + { + ::WaitForSingleObject(exit_event_, INFINITE); + ::CloseHandle(exit_event_); + ::TerminateThread(thread_, 0); + } + else + { + ::WaitForSingleObject(thread_, INFINITE); + } } private: @@ -83,6 +137,8 @@ private: public: virtual ~func_base() {} virtual void run() = 0; + ::HANDLE entry_event_; + ::HANDLE exit_event_; }; template @@ -105,13 +161,26 @@ private: }; ::HANDLE thread_; + ::HANDLE exit_event_; }; inline unsigned int __stdcall win_thread_function(void* arg) { std::auto_ptr func( static_cast(arg)); + + if (func->entry_event_) + ::SetEvent(func->entry_event_); + func->run(); + + if (HANDLE exit_event = func->exit_event_) + { + func.reset(); + ::SetEvent(exit_event); + ::Sleep(INFINITE); + } + return 0; } diff --git a/libtorrent/include/asio/detail/wince_thread.hpp b/libtorrent/include/asio/detail/wince_thread.hpp index d0b4a9f51..5a558b9ef 100644 --- a/libtorrent/include/asio/detail/wince_thread.hpp +++ b/libtorrent/include/asio/detail/wince_thread.hpp @@ -42,9 +42,12 @@ class wince_thread : private noncopyable { public: + // The purpose of the thread. + enum purpose { internal, external }; + // Constructor. template - wince_thread(Function f) + wince_thread(Function f, purpose = internal) { std::auto_ptr arg(new func(f)); DWORD thread_id = 0; diff --git a/libtorrent/include/asio/detail/wrapped_handler.hpp b/libtorrent/include/asio/detail/wrapped_handler.hpp index 0d8d03590..1b2e4dafc 100644 --- a/libtorrent/include/asio/detail/wrapped_handler.hpp +++ b/libtorrent/include/asio/detail/wrapped_handler.hpp @@ -127,22 +127,6 @@ public: Handler handler_; }; -template -inline void* asio_handler_allocate(std::size_t size, - wrapped_handler* this_handler) -{ - return asio_handler_alloc_helpers::allocate( - size, &this_handler->handler_); -} - -template -inline void asio_handler_deallocate(void* pointer, std::size_t size, - wrapped_handler* this_handler) -{ - asio_handler_alloc_helpers::deallocate( - pointer, size, &this_handler->handler_); -} - template class rewrapped_handler { @@ -168,6 +152,22 @@ public: Context context_; }; +template +inline void* asio_handler_allocate(std::size_t size, + wrapped_handler* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + wrapped_handler* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + template inline void asio_handler_invoke(const Function& function, wrapped_handler* this_handler) @@ -177,6 +177,22 @@ inline void asio_handler_invoke(const Function& function, function, this_handler->handler_)); } +template +inline void* asio_handler_allocate(std::size_t size, + rewrapped_handler* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->context_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + rewrapped_handler* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->context_); +} + template inline void asio_handler_invoke(const Function& function, rewrapped_handler* this_handler) diff --git a/libtorrent/include/asio/error.hpp b/libtorrent/include/asio/error.hpp index 1487ed3d7..7a82a8849 100644 --- a/libtorrent/include/asio/error.hpp +++ b/libtorrent/include/asio/error.hpp @@ -105,6 +105,9 @@ enum basic_errors /// Message too long. message_size = ASIO_SOCKET_ERROR(EMSGSIZE), + /// The name was too long. + name_too_long = ASIO_SOCKET_ERROR(ENAMETOOLONG), + /// Network is down. network_down = ASIO_SOCKET_ERROR(ENETDOWN), diff --git a/libtorrent/include/asio/handler_alloc_hook.hpp b/libtorrent/include/asio/handler_alloc_hook.hpp index f49a78da9..8666cfb31 100644 --- a/libtorrent/include/asio/handler_alloc_hook.hpp +++ b/libtorrent/include/asio/handler_alloc_hook.hpp @@ -35,7 +35,7 @@ namespace asio { * * This default implementation is simply: * @code - * return ::operator new(bytes); + * return ::operator new(size); * @endcode * * @note All temporary objects associated with a handler will be deallocated diff --git a/libtorrent/include/asio/impl/read_at.ipp b/libtorrent/include/asio/impl/read_at.ipp new file mode 100644 index 000000000..2412487b5 --- /dev/null +++ b/libtorrent/include/asio/impl/read_at.ipp @@ -0,0 +1,337 @@ +// +// read_at.ipp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_READ_AT_IPP +#define ASIO_READ_AT_IPP + +#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 +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/error.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { + +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec) +{ + asio::detail::consuming_buffers< + mutable_buffer, MutableBufferSequence> tmp(buffers); + std::size_t total_transferred = 0; + while (tmp.begin() != tmp.end()) + { + std::size_t bytes_transferred = d.read_some_at( + offset + total_transferred, tmp, ec); + tmp.consume(bytes_transferred); + total_transferred += bytes_transferred; + if (completion_condition(ec, total_transferred)) + return total_transferred; + } + ec = asio::error_code(); + return total_transferred; +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_at( + d, offset, buffers, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_at( + d, offset, buffers, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec) +{ + std::size_t total_transferred = 0; + for (;;) + { + std::size_t bytes_available = + std::min(512, b.max_size() - b.size()); + std::size_t bytes_transferred = d.read_some_at( + offset + total_transferred, b.prepare(bytes_available), ec); + b.commit(bytes_transferred); + total_transferred += bytes_transferred; + if (b.size() == b.max_size() + || completion_condition(ec, total_transferred)) + return total_transferred; + } +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_at( + d, offset, b, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_at( + d, offset, b, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +namespace detail +{ + template + class read_at_handler + { + public: + typedef asio::detail::consuming_buffers< + mutable_buffer, MutableBufferSequence> buffers_type; + + read_at_handler(AsyncRandomAccessReadDevice& stream, + boost::uint64_t offset, const buffers_type& buffers, + CompletionCondition completion_condition, ReadHandler handler) + : stream_(stream), + offset_(offset), + buffers_(buffers), + total_transferred_(0), + completion_condition_(completion_condition), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + total_transferred_ += bytes_transferred; + buffers_.consume(bytes_transferred); + if (completion_condition_(ec, total_transferred_) + || buffers_.begin() == buffers_.end()) + { + handler_(ec, total_transferred_); + } + else + { + stream_.async_read_some_at( + offset_ + total_transferred_, buffers_, *this); + } + } + + //private: + AsyncRandomAccessReadDevice& stream_; + boost::uint64_t offset_; + buffers_type buffers_; + std::size_t total_transferred_; + CompletionCondition completion_condition_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_at_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_at_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_at_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +inline void async_read_at(AsyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, ReadHandler handler) +{ + asio::detail::consuming_buffers< + mutable_buffer, MutableBufferSequence> tmp(buffers); + d.async_read_some_at(offset, tmp, + detail::read_at_handler( + d, offset, tmp, completion_condition, handler)); +} + +template +inline void async_read_at(AsyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers, + ReadHandler handler) +{ + async_read_at(d, offset, buffers, transfer_all(), handler); +} + +namespace detail +{ + template + class read_at_streambuf_handler + { + public: + read_at_streambuf_handler(AsyncRandomAccessReadDevice& stream, + boost::uint64_t offset, basic_streambuf& streambuf, + CompletionCondition completion_condition, ReadHandler handler) + : stream_(stream), + offset_(offset), + streambuf_(streambuf), + total_transferred_(0), + completion_condition_(completion_condition), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + total_transferred_ += bytes_transferred; + streambuf_.commit(bytes_transferred); + if (streambuf_.size() == streambuf_.max_size() + || completion_condition_(ec, total_transferred_)) + { + handler_(ec, total_transferred_); + } + else + { + std::size_t bytes_available = + std::min(512, streambuf_.max_size() - streambuf_.size()); + stream_.async_read_some_at(offset_ + total_transferred_, + streambuf_.prepare(bytes_available), *this); + } + } + + //private: + AsyncRandomAccessReadDevice& stream_; + boost::uint64_t offset_; + asio::basic_streambuf& streambuf_; + std::size_t total_transferred_; + CompletionCondition completion_condition_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_at_streambuf_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_at_streambuf_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_at_streambuf_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +inline void async_read_at(AsyncRandomAccessReadDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition, ReadHandler handler) +{ + std::size_t bytes_available = + std::min(512, b.max_size() - b.size()); + d.async_read_some_at(offset, b.prepare(bytes_available), + detail::read_at_streambuf_handler( + d, offset, b, completion_condition, handler)); +} + +template +inline void async_read_at(AsyncRandomAccessReadDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + ReadHandler handler) +{ + async_read_at(d, offset, b, transfer_all(), handler); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_READ_AT_IPP diff --git a/libtorrent/include/asio/impl/read_until.ipp b/libtorrent/include/asio/impl/read_until.ipp index fa549fe88..b88b14194 100644 --- a/libtorrent/include/asio/impl/read_until.ipp +++ b/libtorrent/include/asio/impl/read_until.ipp @@ -25,8 +25,8 @@ #include "asio/detail/pop_options.hpp" #include "asio/buffer.hpp" +#include "asio/buffers_iterator.hpp" #include "asio/detail/bind_handler.hpp" -#include "asio/detail/const_buffers_iterator.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/throw_error.hpp" @@ -54,24 +54,24 @@ std::size_t read_until(SyncReadStream& s, // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; - typedef asio::detail::const_buffers_iterator< - const_buffers_type> iterator; + typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); - iterator begin(buffers, next_search_start); - iterator end(buffers, (std::numeric_limits::max)()); + iterator begin = iterator::begin(buffers); + iterator start = begin + next_search_start; + iterator end = iterator::end(buffers); // Look for a match. - iterator iter = std::find(begin, end, delim); + iterator iter = std::find(start, end, delim); if (iter != end) { // Found a match. We're done. ec = asio::error_code(); - return iter.position() + 1; + return iter - begin + 1; } else { // No match. Next search can start with the new data. - next_search_start = end.position(); + next_search_start = end - begin; } // Check if buffer is full. @@ -146,33 +146,33 @@ std::size_t read_until(SyncReadStream& s, // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; - typedef asio::detail::const_buffers_iterator< - const_buffers_type> iterator; + typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); - iterator begin(buffers, next_search_start); - iterator end(buffers, (std::numeric_limits::max)()); + iterator begin = iterator::begin(buffers); + iterator start = begin + next_search_start; + iterator end = iterator::end(buffers); // Look for a match. std::pair result = asio::detail::partial_search( - begin, end, delim.begin(), delim.end()); + start, end, delim.begin(), delim.end()); if (result.first != end) { if (result.second) { // Full match. We're done. ec = asio::error_code(); - return result.first.position() + delim.length(); + return result.first - begin + delim.length(); } else { // Partial match. Next search needs to start from beginning of match. - next_search_start = result.first.position(); + next_search_start = result.first - begin; } } else { // No match. Next search can start with the new data. - next_search_start = end.position(); + next_search_start = end - begin; } // Check if buffer is full. @@ -212,33 +212,33 @@ std::size_t read_until(SyncReadStream& s, // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; - typedef asio::detail::const_buffers_iterator< - const_buffers_type> iterator; + typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); - iterator begin(buffers, next_search_start); - iterator end(buffers, (std::numeric_limits::max)()); + iterator begin = iterator::begin(buffers); + iterator start = begin + next_search_start; + iterator end = iterator::end(buffers); // Look for a match. boost::match_results match_results; - if (boost::regex_search(begin, end, match_results, expr, + if (boost::regex_search(start, end, match_results, expr, boost::match_default | boost::match_partial)) { if (match_results[0].matched) { // Full match. We're done. ec = asio::error_code(); - return match_results[0].second.position(); + return match_results[0].second - begin; } else { // Partial match. Next search needs to start from beginning of match. - next_search_start = match_results[0].first.position(); + next_search_start = match_results[0].first - begin; } } else { // No match. Next search can start with the new data. - next_search_start = end.position(); + next_search_start = end - begin; } // Check if buffer is full. @@ -257,6 +257,73 @@ std::size_t read_until(SyncReadStream& s, } } +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, + MatchCondition match_condition, asio::error_code& ec, + typename boost::enable_if >::type*) +{ + std::size_t next_search_start = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::buffers_iterator iterator; + const_buffers_type buffers = b.data(); + iterator begin = iterator::begin(buffers); + iterator start = begin + next_search_start; + iterator end = iterator::end(buffers); + + // Look for a match. + std::pair result = match_condition(start, end); + if (result.first != end) + { + if (result.second) + { + // Full match. We're done. + ec = asio::error_code(); + return result.first - begin; + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start = result.first - begin; + } + } + else + { + // No match. Next search can start with the new data. + next_search_start = end - begin; + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + ec = error::not_found; + return 0; + } + + // Need more data. + std::size_t bytes_available = + std::min(512, b.max_size() - b.size()); + b.commit(s.read_some(b.prepare(bytes_available), ec)); + if (ec) + return 0; + } +} + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, MatchCondition match_condition, + typename boost::enable_if >::type*) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_until(s, b, match_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + namespace detail { template @@ -291,18 +358,18 @@ namespace detail // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; - typedef asio::detail::const_buffers_iterator< - const_buffers_type> iterator; + typedef asio::buffers_iterator iterator; const_buffers_type buffers = streambuf_.data(); - iterator begin(buffers, next_search_start_); - iterator end(buffers, (std::numeric_limits::max)()); + iterator begin = iterator::begin(buffers); + iterator start = begin + next_search_start_; + iterator end = iterator::end(buffers); // Look for a match. - iterator iter = std::find(begin, end, delim_); + iterator iter = std::find(start, end, delim_); if (iter != end) { // Found a match. We're done. - std::size_t bytes = iter.position() + 1; + std::size_t bytes = iter - begin + 1; handler_(ec, bytes); return; } @@ -317,7 +384,7 @@ namespace detail } // Next search can start with the new data. - next_search_start_ = end.position(); + next_search_start_ = end - begin; // Start a new asynchronous read operation to obtain more data. std::size_t bytes_available = @@ -369,11 +436,10 @@ void async_read_until(AsyncReadStream& s, // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; - typedef asio::detail::const_buffers_iterator< - const_buffers_type> iterator; + typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); - iterator begin(buffers, 0); - iterator end(buffers, (std::numeric_limits::max)()); + iterator begin = iterator::begin(buffers); + iterator end = iterator::end(buffers); // Look for a match. iterator iter = std::find(begin, end, delim); @@ -381,7 +447,7 @@ void async_read_until(AsyncReadStream& s, { // Found a match. We're done. asio::error_code ec; - std::size_t bytes = iter.position() + 1; + std::size_t bytes = iter - begin + 1; s.io_service().post(detail::bind_handler(handler, ec, bytes)); return; } @@ -399,7 +465,7 @@ void async_read_until(AsyncReadStream& s, std::min(512, b.max_size() - b.size()); s.async_read_some(b.prepare(bytes_available), detail::read_until_delim_handler( - s, b, delim, end.position(), handler)); + s, b, delim, end - begin, handler)); } namespace detail @@ -437,34 +503,34 @@ namespace detail // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; - typedef asio::detail::const_buffers_iterator< - const_buffers_type> iterator; + typedef asio::buffers_iterator iterator; const_buffers_type buffers = streambuf_.data(); - iterator begin(buffers, next_search_start_); - iterator end(buffers, (std::numeric_limits::max)()); + iterator begin = iterator::begin(buffers); + iterator start = begin + next_search_start_; + iterator end = iterator::end(buffers); // Look for a match. std::pair result = asio::detail::partial_search( - begin, end, delim_.begin(), delim_.end()); + start, end, delim_.begin(), delim_.end()); if (result.first != end) { if (result.second) { // Full match. We're done. - std::size_t bytes = result.first.position() + delim_.length(); + std::size_t bytes = result.first - begin + delim_.length(); handler_(ec, bytes); return; } else { // Partial match. Next search needs to start from beginning of match. - next_search_start_ = result.first.position(); + next_search_start_ = result.first - begin; } } else { // No match. Next search can start with the new data. - next_search_start_ = end.position(); + next_search_start_ = end - begin; } // Check if buffer is full. @@ -527,11 +593,10 @@ void async_read_until(AsyncReadStream& s, // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; - typedef asio::detail::const_buffers_iterator< - const_buffers_type> iterator; + typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); - iterator begin(buffers, 0); - iterator end(buffers, (std::numeric_limits::max)()); + iterator begin = iterator::begin(buffers); + iterator end = iterator::end(buffers); // Look for a match. std::size_t next_search_start; @@ -543,20 +608,20 @@ void async_read_until(AsyncReadStream& s, { // Full match. We're done. asio::error_code ec; - std::size_t bytes = result.first.position() + delim.length(); + std::size_t bytes = result.first - begin + delim.length(); s.io_service().post(detail::bind_handler(handler, ec, bytes)); return; } else { // Partial match. Next search needs to start from beginning of match. - next_search_start = result.first.position(); + next_search_start = result.first - begin; } } else { // No match. Next search can start with the new data. - next_search_start = end.position(); + next_search_start = end - begin; } // Check if buffer is full. @@ -611,34 +676,34 @@ namespace detail // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; - typedef asio::detail::const_buffers_iterator< - const_buffers_type> iterator; + typedef asio::buffers_iterator iterator; const_buffers_type buffers = streambuf_.data(); - iterator begin(buffers, next_search_start_); - iterator end(buffers, (std::numeric_limits::max)()); + iterator begin = iterator::begin(buffers); + iterator start = begin + next_search_start_; + iterator end = iterator::end(buffers); // Look for a match. boost::match_results match_results; - if (boost::regex_search(begin, end, match_results, expr_, + if (boost::regex_search(start, end, match_results, expr_, boost::match_default | boost::match_partial)) { if (match_results[0].matched) { // Full match. We're done. - std::size_t bytes = match_results[0].second.position(); + std::size_t bytes = match_results[0].second - begin; handler_(ec, bytes); return; } else { // Partial match. Next search needs to start from beginning of match. - next_search_start_ = match_results[0].first.position(); + next_search_start_ = match_results[0].first - begin; } } else { // No match. Next search can start with the new data. - next_search_start_ = end.position(); + next_search_start_ = end - begin; } // Check if buffer is full. @@ -701,11 +766,10 @@ void async_read_until(AsyncReadStream& s, // Determine the range of the data to be searched. typedef typename asio::basic_streambuf< Allocator>::const_buffers_type const_buffers_type; - typedef asio::detail::const_buffers_iterator< - const_buffers_type> iterator; + typedef asio::buffers_iterator iterator; const_buffers_type buffers = b.data(); - iterator begin(buffers, 0); - iterator end(buffers, (std::numeric_limits::max)()); + iterator begin = iterator::begin(buffers); + iterator end = iterator::end(buffers); // Look for a match. std::size_t next_search_start; @@ -717,20 +781,20 @@ void async_read_until(AsyncReadStream& s, { // Full match. We're done. asio::error_code ec; - std::size_t bytes = match_results[0].second.position(); + std::size_t bytes = match_results[0].second - begin; s.io_service().post(detail::bind_handler(handler, ec, bytes)); return; } else { // Partial match. Next search needs to start from beginning of match. - next_search_start = match_results[0].first.position(); + next_search_start = match_results[0].first - begin; } } else { // No match. Next search can start with the new data. - next_search_start = end.position(); + next_search_start = end - begin; } // Check if buffer is full. @@ -749,6 +813,182 @@ void async_read_until(AsyncReadStream& s, s, b, expr, next_search_start, handler)); } +namespace detail +{ + template + class read_until_match_handler + { + public: + read_until_match_handler(AsyncReadStream& stream, + asio::basic_streambuf& streambuf, + MatchCondition match_condition, std::size_t next_search_start, + ReadHandler handler) + : stream_(stream), + streambuf_(streambuf), + match_condition_(match_condition), + next_search_start_(next_search_start), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + // Check for errors. + if (ec) + { + std::size_t bytes = 0; + handler_(ec, bytes); + return; + } + + // Commit received data to streambuf's get area. + streambuf_.commit(bytes_transferred); + + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::buffers_iterator iterator; + const_buffers_type buffers = streambuf_.data(); + iterator begin = iterator::begin(buffers); + iterator start = begin + next_search_start_; + iterator end = iterator::end(buffers); + + // Look for a match. + std::pair result = match_condition_(start, end); + if (result.first != end) + { + if (result.second) + { + // Full match. We're done. + std::size_t bytes = result.first - begin; + handler_(ec, bytes); + return; + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start_ = result.first - begin; + } + } + else + { + // No match. Next search can start with the new data. + next_search_start_ = end - begin; + } + + // Check if buffer is full. + if (streambuf_.size() == streambuf_.max_size()) + { + std::size_t bytes = 0; + asio::error_code ec(error::not_found); + handler_(ec, bytes); + return; + } + + // Start a new asynchronous read operation to obtain more data. + std::size_t bytes_available = + std::min(512, streambuf_.max_size() - streambuf_.size()); + stream_.async_read_some(streambuf_.prepare(bytes_available), *this); + } + + //private: + AsyncReadStream& stream_; + asio::basic_streambuf& streambuf_; + MatchCondition match_condition_; + std::size_t next_search_start_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_match_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_match_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_match_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +void async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, + MatchCondition match_condition, ReadHandler handler, + typename boost::enable_if >::type*) +{ + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::buffers_iterator iterator; + const_buffers_type buffers = b.data(); + iterator begin = iterator::begin(buffers); + iterator end = iterator::end(buffers); + + // Look for a match. + std::size_t next_search_start; + std::pair result = match_condition(begin, end); + if (result.first != end) + { + if (result.second) + { + // Full match. We're done. + asio::error_code ec; + std::size_t bytes = result.first - begin; + s.io_service().post(detail::bind_handler(handler, ec, bytes)); + return; + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start = result.first - begin; + } + } + else + { + // No match. Next search can start with the new data. + next_search_start = end - begin; + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + asio::error_code ec(error::not_found); + s.io_service().post(detail::bind_handler(handler, ec, 0)); + return; + } + + // Start a new asynchronous read operation to obtain more data. + std::size_t bytes_available = + std::min(512, b.max_size() - b.size()); + s.async_read_some(b.prepare(bytes_available), + detail::read_until_match_handler< + AsyncReadStream, Allocator, MatchCondition, ReadHandler>( + s, b, match_condition, next_search_start, handler)); +} + } // namespace asio #include "asio/detail/pop_options.hpp" diff --git a/libtorrent/include/asio/impl/serial_port_base.ipp b/libtorrent/include/asio/impl/serial_port_base.ipp new file mode 100644 index 000000000..8dd6083ee --- /dev/null +++ b/libtorrent/include/asio/impl/serial_port_base.ipp @@ -0,0 +1,541 @@ +// +// serial_port_base.ipp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_SERIAL_PORT_BASE_IPP +#define ASIO_SERIAL_PORT_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +inline serial_port_base::baud_rate::baud_rate(unsigned int rate) + : value_(rate) +{ +} + +inline unsigned int serial_port_base::baud_rate::value() const +{ + return value_; +} + +inline asio::error_code serial_port_base::baud_rate::store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + storage.BaudRate = value_; +#else + speed_t baud; + switch (value_) + { + // Do POSIX-specified rates first. + case 0: baud = B0; break; + case 50: baud = B50; break; + case 75: baud = B75; break; + case 110: baud = B110; break; + case 134: baud = B134; break; + case 150: baud = B150; break; + case 200: baud = B200; break; + case 300: baud = B300; break; + case 600: baud = B600; break; + case 1200: baud = B1200; break; + case 1800: baud = B1800; break; + case 2400: baud = B2400; break; + case 4800: baud = B4800; break; + case 9600: baud = B9600; break; + case 19200: baud = B19200; break; + case 38400: baud = B38400; break; + // And now the extended ones conditionally. +# ifdef B7200 + case 7200: baud = B7200; break; +# endif +# ifdef B14400 + case 14400: baud = B14400; break; +# endif +# ifdef B57600 + case 57600: baud = B57600; break; +# endif +# ifdef B115200 + case 115200: baud = B115200; break; +# endif +# ifdef B230400 + case 230400: baud = B230400; break; +# endif +# ifdef B460800 + case 460800: baud = B460800; break; +# endif +# ifdef B500000 + case 500000: baud = B500000; break; +# endif +# ifdef B576000 + case 576000: baud = B576000; break; +# endif +# ifdef B921600 + case 921600: baud = B921600; break; +# endif +# ifdef B1000000 + case 1000000: baud = B1000000; break; +# endif +# ifdef B1152000 + case 1152000: baud = B1152000; break; +# endif +# ifdef B2000000 + case 2000000: baud = B2000000; break; +# endif +# ifdef B3000000 + case 3000000: baud = B3000000; break; +# endif +# ifdef B3500000 + case 3500000: baud = B3500000; break; +# endif +# ifdef B4000000 + case 4000000: baud = B4000000; break; +# endif + default: + baud = B0; + ec = asio::error::invalid_argument; + return ec; + } +# if defined(_BSD_SOURCE) + ::cfsetspeed(&storage, baud); +# else + ::cfsetispeed(&storage, baud); + ::cfsetospeed(&storage, baud); +# endif +#endif + ec = asio::error_code(); + return ec; +} + +inline asio::error_code serial_port_base::baud_rate::load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + value_ = storage.BaudRate; +#else + speed_t baud = ::cfgetospeed(&storage); + switch (baud) + { + // First do those specified by POSIX. + case B0: value_ = 0; break; + case B50: value_ = 50; break; + case B75: value_ = 75; break; + case B110: value_ = 110; break; + case B134: value_ = 134; break; + case B150: value_ = 150; break; + case B200: value_ = 200; break; + case B300: value_ = 300; break; + case B600: value_ = 600; break; + case B1200: value_ = 1200; break; + case B1800: value_ = 1800; break; + case B2400: value_ = 2400; break; + case B4800: value_ = 4800; break; + case B9600: value_ = 9600; break; + case B19200: value_ = 19200; break; + case B38400: value_ = 38400; break; + // Now conditionally handle a bunch of extended rates. +# ifdef B7200 + case B7200: value_ = 7200; break; +# endif +# ifdef B14400 + case B14400: value_ = 14400; break; +# endif +# ifdef B57600 + case B57600: value_ = 57600; break; +# endif +# ifdef B115200 + case B115200: value_ = 115200; break; +# endif +# ifdef B230400 + case B230400: value_ = 230400; break; +# endif +# ifdef B460800 + case B460800: value_ = 460800; break; +# endif +# ifdef B500000 + case B500000: value_ = 500000; break; +# endif +# ifdef B576000 + case B576000: value_ = 576000; break; +# endif +# ifdef B921600 + case B921600: value_ = 921600; break; +# endif +# ifdef B1000000 + case B1000000: value_ = 1000000; break; +# endif +# ifdef B1152000 + case B1152000: value_ = 1152000; break; +# endif +# ifdef B2000000 + case B2000000: value_ = 2000000; break; +# endif +# ifdef B3000000 + case B3000000: value_ = 3000000; break; +# endif +# ifdef B3500000 + case B3500000: value_ = 3500000; break; +# endif +# ifdef B4000000 + case B4000000: value_ = 4000000; break; +# endif + default: + value_ = 0; + ec = asio::error::invalid_argument; + return ec; + } +#endif + ec = asio::error_code(); + return ec; +} + +inline serial_port_base::flow_control::flow_control( + serial_port_base::flow_control::type t) + : value_(t) +{ + if (t != none && t != software && t != hardware) + throw std::out_of_range("invalid flow_control value"); +} + +inline serial_port_base::flow_control::type +serial_port_base::flow_control::value() const +{ + return value_; +} + +inline asio::error_code serial_port_base::flow_control::store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + storage.fOutxCtsFlow = FALSE; + storage.fOutxDsrFlow = FALSE; + storage.fTXContinueOnXoff = TRUE; + storage.fDtrControl = DTR_CONTROL_ENABLE; + storage.fDsrSensitivity = FALSE; + storage.fOutX = FALSE; + storage.fInX = FALSE; + storage.fRtsControl = RTS_CONTROL_ENABLE; + switch (value_) + { + case none: + break; + case software: + storage.fOutX = TRUE; + storage.fInX = TRUE; + break; + case hardware: + storage.fOutxCtsFlow = TRUE; + storage.fRtsControl = RTS_CONTROL_HANDSHAKE; + break; + default: + break; + } +#else + switch (value_) + { + case none: + storage.c_iflag &= ~(IXOFF | IXON); +# if defined(_BSD_SOURCE) + storage.c_cflag &= ~CRTSCTS; +# endif + break; + case software: + storage.c_iflag |= IXOFF | IXON; +# if defined(_BSD_SOURCE) + storage.c_cflag &= ~CRTSCTS; +# endif + break; + case hardware: +# if defined(_BSD_SOURCE) + storage.c_iflag &= ~(IXOFF | IXON); + storage.c_cflag |= CRTSCTS; + break; +# else + ec = asio::error::operation_not_supported; + return ec; +# endif + default: + break; + } +#endif + ec = asio::error_code(); + return ec; +} + +inline asio::error_code serial_port_base::flow_control::load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (storage.fOutX && storage.fInX) + { + value_ = software; + } + else if (storage.fOutxCtsFlow && storage.fRtsControl == RTS_CONTROL_HANDSHAKE) + { + value_ = hardware; + } + else + { + value_ = none; + } +#else + if (storage.c_iflag & (IXOFF | IXON)) + { + value_ = software; + } +# if defined(_BSD_SOURCE) + else if (storage.c_cflag & CRTSCTS) + { + value_ = hardware; + } +# endif + else + { + value_ = none; + } +#endif + ec = asio::error_code(); + return ec; +} + +inline serial_port_base::parity::parity(serial_port_base::parity::type t) + : value_(t) +{ + if (t != none && t != odd && t != even) + throw std::out_of_range("invalid parity value"); +} + +inline serial_port_base::parity::type serial_port_base::parity::value() const +{ + return value_; +} + +inline asio::error_code serial_port_base::parity::store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + switch (value_) + { + case none: + storage.fParity = FALSE; + storage.Parity = NOPARITY; + break; + case odd: + storage.fParity = TRUE; + storage.Parity = ODDPARITY; + break; + case even: + storage.fParity = TRUE; + storage.Parity = EVENPARITY; + break; + default: + break; + } +#else + switch (value_) + { + case none: + storage.c_iflag |= IGNPAR; + storage.c_cflag &= ~(PARENB | PARODD); + break; + case even: + storage.c_iflag &= ~(IGNPAR | PARMRK); + storage.c_iflag |= INPCK; + storage.c_cflag |= PARENB; + storage.c_cflag &= ~PARODD; + break; + case odd: + storage.c_iflag &= ~(IGNPAR | PARMRK); + storage.c_iflag |= INPCK; + storage.c_cflag |= (PARENB | PARODD); + break; + default: + break; + } +#endif + ec = asio::error_code(); + return ec; +} + +inline asio::error_code serial_port_base::parity::load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (storage.Parity == EVENPARITY) + { + value_ = even; + } + else if (storage.Parity == ODDPARITY) + { + value_ = odd; + } + else + { + value_ = none; + } +#else + if (storage.c_cflag & PARENB) + { + if (storage.c_cflag & PARODD) + { + value_ = odd; + } + else + { + value_ = even; + } + } + else + { + value_ = none; + } +#endif + ec = asio::error_code(); + return ec; +} + +inline serial_port_base::stop_bits::stop_bits( + serial_port_base::stop_bits::type t) + : value_(t) +{ + if (t != one && t != onepointfive && t != two) + throw std::out_of_range("invalid stop_bits value"); +} + +inline serial_port_base::stop_bits::type +serial_port_base::stop_bits::value() const +{ + return value_; +} + +inline asio::error_code serial_port_base::stop_bits::store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + switch (value_) + { + case one: + storage.StopBits = ONESTOPBIT; + break; + case onepointfive: + storage.StopBits = ONE5STOPBITS; + break; + case two: + storage.StopBits = TWOSTOPBITS; + break; + default: + break; + } +#else + switch (value_) + { + case one: + storage.c_cflag &= ~CSTOPB; + break; + case two: + storage.c_cflag |= CSTOPB; + break; + default: + ec = asio::error::operation_not_supported; + return ec; + } +#endif + ec = asio::error_code(); + return ec; +} + +inline asio::error_code serial_port_base::stop_bits::load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (storage.StopBits == ONESTOPBIT) + { + value_ = one; + } + else if (storage.StopBits == ONE5STOPBITS) + { + value_ = onepointfive; + } + else if (storage.StopBits == TWOSTOPBITS) + { + value_ = two; + } + else + { + value_ = one; + } +#else + value_ = (storage.c_cflag & CSTOPB) ? two : one; +#endif + ec = asio::error_code(); + return ec; +} + +inline serial_port_base::character_size::character_size(unsigned int t) + : value_(t) +{ + if (t < 5 || t > 8) + throw std::out_of_range("invalid character_size value"); +} + +inline unsigned int serial_port_base::character_size::value() const +{ + return value_; +} + +inline asio::error_code serial_port_base::character_size::store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + storage.ByteSize = value_; +#else + storage.c_cflag &= ~CSIZE; + switch (value_) + { + case 5: storage.c_cflag |= CS5; break; + case 6: storage.c_cflag |= CS6; break; + case 7: storage.c_cflag |= CS7; break; + case 8: storage.c_cflag |= CS8; break; + default: break; + } +#endif + ec = asio::error_code(); + return ec; +} + +inline asio::error_code serial_port_base::character_size::load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + value_ = storage.ByteSize; +#else + if ((storage.c_cflag & CSIZE) == CS5) { value_ = 5; } + else if ((storage.c_cflag & CSIZE) == CS6) { value_ = 6; } + else if ((storage.c_cflag & CSIZE) == CS7) { value_ = 7; } + else if ((storage.c_cflag & CSIZE) == CS8) { value_ = 8; } + else + { + // Hmmm, use 8 for now. + value_ = 8; + } +#endif + ec = asio::error_code(); + return ec; +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SERIAL_PORT_BASE_IPP diff --git a/libtorrent/include/asio/impl/write_at.ipp b/libtorrent/include/asio/impl/write_at.ipp new file mode 100644 index 000000000..c6fa3078c --- /dev/null +++ b/libtorrent/include/asio/impl/write_at.ipp @@ -0,0 +1,296 @@ +// +// write_at.ipp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_WRITE_AT_IPP +#define ASIO_WRITE_AT_IPP + +#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/completion_condition.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { + +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec) +{ + asio::detail::consuming_buffers< + const_buffer, ConstBufferSequence> tmp(buffers); + std::size_t total_transferred = 0; + while (tmp.begin() != tmp.end()) + { + std::size_t bytes_transferred = d.write_some_at( + offset + total_transferred, tmp, ec); + tmp.consume(bytes_transferred); + total_transferred += bytes_transferred; + if (completion_condition(ec, total_transferred)) + return total_transferred; + } + ec = asio::error_code(); + return total_transferred; +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers) +{ + asio::error_code ec; + std::size_t bytes_transferred = write_at( + d, offset, buffers, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = write_at( + d, offset, buffers, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec) +{ + std::size_t bytes_transferred = write_at( + d, offset, b.data(), completion_condition, ec); + b.consume(bytes_transferred); + return bytes_transferred; +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b) +{ + asio::error_code ec; + std::size_t bytes_transferred = write_at(d, offset, b, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = write_at( + d, offset, b, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +namespace detail +{ + template + class write_at_handler + { + public: + typedef asio::detail::consuming_buffers< + const_buffer, ConstBufferSequence> buffers_type; + + write_at_handler(AsyncRandomAccessWriteDevice& stream, + boost::uint64_t offset, const buffers_type& buffers, + CompletionCondition completion_condition, WriteHandler handler) + : stream_(stream), + buffers_(buffers), + offset_(offset), + total_transferred_(0), + completion_condition_(completion_condition), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + total_transferred_ += bytes_transferred; + buffers_.consume(bytes_transferred); + if (completion_condition_(ec, total_transferred_) + || buffers_.begin() == buffers_.end()) + { + handler_(ec, total_transferred_); + } + else + { + stream_.async_write_some_at( + offset_ + total_transferred_, buffers_, *this); + } + } + + //private: + AsyncRandomAccessWriteDevice& stream_; + buffers_type buffers_; + boost::uint64_t offset_; + std::size_t total_transferred_; + CompletionCondition completion_condition_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_at_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_at_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_at_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +inline void async_write_at(AsyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, WriteHandler handler) +{ + asio::detail::consuming_buffers< + const_buffer, ConstBufferSequence> tmp(buffers); + d.async_write_some_at(offset, tmp, + detail::write_at_handler( + d, offset, tmp, completion_condition, handler)); +} + +template +inline void async_write_at(AsyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers, + WriteHandler handler) +{ + async_write_at(d, offset, buffers, transfer_all(), handler); +} + +namespace detail +{ + template + class write_at_streambuf_handler + { + public: + write_at_streambuf_handler( + asio::basic_streambuf& streambuf, + WriteHandler handler) + : streambuf_(streambuf), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + streambuf_.consume(bytes_transferred); + handler_(ec, bytes_transferred); + } + + //private: + asio::basic_streambuf& streambuf_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_at_streambuf_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_at_streambuf_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_at_streambuf_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +inline void async_write_at(AsyncRandomAccessWriteDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition, WriteHandler handler) +{ + async_write_at(d, offset, b.data(), completion_condition, + detail::write_at_streambuf_handler< + AsyncRandomAccessWriteDevice, Allocator, WriteHandler>(b, handler)); +} + +template +inline void async_write_at(AsyncRandomAccessWriteDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + WriteHandler handler) +{ + async_write_at(d, offset, b, transfer_all(), handler); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WRITE_AT_IPP diff --git a/libtorrent/include/asio/io_service.hpp b/libtorrent/include/asio/io_service.hpp index 451b65a33..b6a34fdc4 100644 --- a/libtorrent/include/asio/io_service.hpp +++ b/libtorrent/include/asio/io_service.hpp @@ -100,6 +100,32 @@ template bool has_service(io_service& ios); * } * } * @endcode + * + * @par Stopping the io_service from running out of work + * + * Some applications may need to prevent an io_service's run() call from + * returning when there is no more work to do. For example, the io_service may + * be being run in a background thread that is launched prior to the + * application's asynchronous operations. The run() call may be kept running by + * creating an object of type asio::io_service::work: + * + * @code asio::io_service io_service; + * asio::io_service::work work(io_service); + * ... @endcode + * + * To effect a shutdown, the application will then need to call the io_service's + * stop() member function. This will cause the io_service run() call to return + * as soon as possible, abandoning unfinished operations and without permitting + * ready handlers to be dispatched. + * + * Alternatively, if the application requires that all operations and handlers + * be allowed to finish normally, the work object may be explicitly destroyed. + * + * @code asio::io_service io_service; + * auto_ptr work( + * new asio::io_service::work(io_service)); + * ... + * work.reset(); // Allow run() to exit. @endcode */ class io_service : private noncopyable @@ -159,6 +185,9 @@ public: * @return The number of handlers that were executed. * * @throws asio::system_error Thrown on failure. + * + * @note The poll() function may also be used to dispatch ready handlers, + * but without blocking. */ std::size_t run(); @@ -178,6 +207,9 @@ public: * @param ec Set to indicate what error occurred, if any. * * @return The number of handlers that were executed. + * + * @note The poll() function may also be used to dispatch ready handlers, + * but without blocking. */ std::size_t run(asio::error_code& ec); diff --git a/libtorrent/include/asio/ip/icmp.hpp b/libtorrent/include/asio/ip/icmp.hpp new file mode 100644 index 000000000..c36ed2785 --- /dev/null +++ b/libtorrent/include/asio/ip/icmp.hpp @@ -0,0 +1,118 @@ +// +// icmp.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2008 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_IP_ICMP_HPP +#define ASIO_IP_ICMP_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/basic_raw_socket.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace ip { + +/// Encapsulates the flags needed for ICMP. +/** + * The asio::ip::icmp class contains flags necessary for ICMP sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol, InternetProtocol. + */ +class icmp +{ +public: + /// The type of a ICMP endpoint. + typedef basic_endpoint endpoint; + + /// The type of a resolver query. + typedef basic_resolver_query resolver_query; + + /// The type of a resolver iterator. + typedef basic_resolver_iterator resolver_iterator; + + /// Construct to represent the IPv4 ICMP protocol. + static icmp v4() + { + return icmp(IPPROTO_ICMP, PF_INET); + } + + /// Construct to represent the IPv6 ICMP protocol. + static icmp v6() + { + return icmp(IPPROTO_ICMPV6, PF_INET6); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_RAW; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return protocol_; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The ICMP socket type. + typedef basic_raw_socket socket; + + /// The ICMP resolver type. + typedef basic_resolver resolver; + + /// Compare two protocols for equality. + friend bool operator==(const icmp& p1, const icmp& p2) + { + return p1.protocol_ == p2.protocol_ && p1.family_ == p2.family_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const icmp& p1, const icmp& p2) + { + return p1.protocol_ != p2.protocol_ || p1.family_ != p2.family_; + } + +private: + // Construct with a specific family. + explicit icmp(int protocol, int family) + : protocol_(protocol), + family_(family) + { + } + + int protocol_; + int family_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ICMP_HPP diff --git a/libtorrent/include/asio/local/basic_endpoint.hpp b/libtorrent/include/asio/local/basic_endpoint.hpp new file mode 100644 index 000000000..75dd092e3 --- /dev/null +++ b/libtorrent/include/asio/local/basic_endpoint.hpp @@ -0,0 +1,265 @@ +// +// basic_endpoint.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Derived from a public domain implementation written by Daniel Casimiro. +// +// 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_LOCAL_BASIC_ENDPOINT_HPP +#define ASIO_LOCAL_BASIC_ENDPOINT_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 +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_error.hpp" + +#if !defined(ASIO_DISABLE_LOCAL_SOCKETS) +# if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +# define ASIO_HAS_LOCAL_SOCKETS 1 +# endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#endif // !defined(ASIO_DISABLE_LOCAL_SOCKETS) + +#if defined(ASIO_HAS_LOCAL_SOCKETS) \ + || defined(GENERATING_DOCUMENTATION) + + +namespace asio { +namespace local { + +/// Describes an endpoint for a UNIX socket. +/** + * The asio::local::basic_endpoint class template describes an endpoint + * that may be associated with a particular UNIX socket. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Endpoint. + */ +template +class basic_endpoint +{ +public: + /// The protocol type associated with the endpoint. + typedef Protocol protocol_type; + + /// The type of the endpoint structure. This type is dependent on the + /// underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined data_type; +#else + typedef asio::detail::socket_addr_type data_type; +#endif + + /// Default constructor. + basic_endpoint() + { + init("", 0); + } + + /// Construct an endpoint using the specified path name. + basic_endpoint(const char* path) + { + using namespace std; // For strlen. + init(path, strlen(path)); + } + + /// Construct an endpoint using the specified path name. + basic_endpoint(const std::string& path) + { + init(path.data(), path.length()); + } + + /// Copy constructor. + basic_endpoint(const basic_endpoint& other) + : data_(other.data_), + path_length_(other.path_length_) + { + } + + /// Assign from another endpoint. + basic_endpoint& operator=(const basic_endpoint& other) + { + data_ = other.data_; + path_length_ = other.path_length_; + return *this; + } + + /// The protocol associated with the endpoint. + protocol_type protocol() const + { + return protocol_type(); + } + + /// Get the underlying endpoint in the native type. + data_type* data() + { + return &data_.base; + } + + /// Get the underlying endpoint in the native type. + const data_type* data() const + { + return &data_.base; + } + + /// Get the underlying size of the endpoint in the native type. + std::size_t size() const + { + return path_length_ + + offsetof(asio::detail::sockaddr_un_type, sun_path); + } + + /// Set the underlying size of the endpoint in the native type. + void resize(std::size_t size) + { + if (size > sizeof(asio::detail::sockaddr_un_type)) + { + asio::system_error e(asio::error::invalid_argument); + boost::throw_exception(e); + } + else if (size == 0) + { + path_length_ = 0; + } + else + { + path_length_ = size + - offsetof(asio::detail::sockaddr_un_type, sun_path); + + // The path returned by the operating system may be NUL-terminated. + if (path_length_ > 0 && data_.local.sun_path[path_length_] == 0) + --path_length_; + } + } + + /// Get the capacity of the endpoint in the native type. + std::size_t capacity() const + { + return sizeof(asio::detail::sockaddr_un_type); + } + + /// Get the path associated with the endpoint. + std::string path() const + { + return std::string(data_.local.sun_path, path_length_); + } + + /// Set the path associated with the endpoint. + void path(const char* p) + { + using namespace std; // For strlen. + init(p, strlen(p)); + } + + /// Set the path associated with the endpoint. + void path(const std::string& p) + { + init(p.data(), p.length()); + } + + /// Compare two endpoints for equality. + friend bool operator==(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.path() == e2.path(); + } + + /// Compare two endpoints for inequality. + friend bool operator!=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.path() != e2.path(); + } + + /// Compare endpoints for ordering. + friend bool operator<(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.path() < e2.path(); + } + +private: + // The underlying UNIX socket address. + union data_union + { + asio::detail::socket_addr_type base; + asio::detail::sockaddr_un_type local; + } data_; + + // The length of the path associated with the endpoint. + std::size_t path_length_; + + // Initialise with a specified path. + void init(const char* path, std::size_t path_length) + { + if (path_length > sizeof(data_.local.sun_path) - 1) + { + // The buffer is not large enough to store this address. + asio::error_code ec(asio::error::name_too_long); + asio::detail::throw_error(ec); + } + + using namespace std; // For memcpy. + data_.local = asio::detail::sockaddr_un_type(); + data_.local.sun_family = AF_UNIX; + memcpy(data_.local.sun_path, path, path_length); + path_length_ = path_length; + + // NUL-terminate normal path names. Names that start with a NUL are in the + // UNIX domain protocol's "abstract namespace" and are not NUL-terminated. + if (path_length > 0 && data_.local.sun_path[0] == 0) + data_.local.sun_path[path_length] = 0; + } +}; + +/// Output an endpoint as a string. +/** + * Used to output a human-readable string for a specified endpoint. + * + * @param os The output stream to which the string will be written. + * + * @param endpoint The endpoint to be written. + * + * @return The output stream. + * + * @relates asio::local::basic_endpoint + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, + const basic_endpoint& endpoint) +{ + os << endpoint.path(); + return os; +} + +} // namespace local +} // namespace asio + +#endif // defined(ASIO_HAS_LOCAL_SOCKETS) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_LOCAL_BASIC_ENDPOINT_HPP diff --git a/libtorrent/include/asio/local/connect_pair.hpp b/libtorrent/include/asio/local/connect_pair.hpp new file mode 100644 index 000000000..e4f73f91b --- /dev/null +++ b/libtorrent/include/asio/local/connect_pair.hpp @@ -0,0 +1,100 @@ +// +// connect_pair.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_LOCAL_CONNECT_PAIR_HPP +#define ASIO_LOCAL_CONNECT_PAIR_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/basic_socket.hpp" +#include "asio/error.hpp" +#include "asio/local/basic_endpoint.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" + +#if defined(ASIO_HAS_LOCAL_SOCKETS) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace local { + +/// Create a pair of connected sockets. +template +void connect_pair( + basic_socket& socket1, + basic_socket& socket2); + +/// Create a pair of connected sockets. +template +asio::error_code connect_pair( + basic_socket& socket1, + basic_socket& socket2, + asio::error_code& ec); + +template +inline void connect_pair( + basic_socket& socket1, + basic_socket& socket2) +{ + asio::error_code ec; + connect_pair(socket1, socket2, ec); + asio::detail::throw_error(ec); +} + +template +inline asio::error_code connect_pair( + basic_socket& socket1, + basic_socket& socket2, + asio::error_code& ec) +{ + // Check that this function is only being used with a UNIX domain socket. + asio::local::basic_endpoint* tmp + = static_cast(0); + (void)tmp; + + Protocol protocol; + asio::detail::socket_type sv[2]; + if (asio::detail::socket_ops::socketpair(protocol.family(), + protocol.type(), protocol.protocol(), sv, ec) + == asio::detail::socket_error_retval) + return ec; + + if (socket1.assign(protocol, sv[0], ec)) + { + asio::error_code temp_ec; + asio::detail::socket_ops::close(sv[0], temp_ec); + asio::detail::socket_ops::close(sv[1], temp_ec); + return ec; + } + + if (socket2.assign(protocol, sv[1], ec)) + { + asio::error_code temp_ec; + socket1.close(temp_ec); + asio::detail::socket_ops::close(sv[1], temp_ec); + return ec; + } + + return ec; +} + +} // namespace local +} // namespace asio + +#endif // defined(ASIO_HAS_LOCAL_SOCKETS) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_LOCAL_CONNECT_PAIR_HPP diff --git a/libtorrent/include/asio/local/datagram_protocol.hpp b/libtorrent/include/asio/local/datagram_protocol.hpp new file mode 100644 index 000000000..c7adacfc9 --- /dev/null +++ b/libtorrent/include/asio/local/datagram_protocol.hpp @@ -0,0 +1,78 @@ +// +// datagram_protocol.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_LOCAL_DATAGRAM_PROTOCOL_HPP +#define ASIO_LOCAL_DATAGRAM_PROTOCOL_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/basic_datagram_socket.hpp" +#include "asio/local/basic_endpoint.hpp" +#include "asio/detail/socket_types.hpp" + +#if defined(ASIO_HAS_LOCAL_SOCKETS) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace local { + +/// Encapsulates the flags needed for datagram-oriented UNIX sockets. +/** + * The asio::local::datagram_protocol class contains flags necessary for + * datagram-oriented UNIX domain sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class datagram_protocol +{ +public: + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_DGRAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return 0; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return AF_UNIX; + } + + /// The type of a UNIX domain endpoint. + typedef basic_endpoint endpoint; + + /// The UNIX domain socket type. + typedef basic_datagram_socket socket; +}; + +} // namespace local +} // namespace asio + +#endif // defined(ASIO_HAS_LOCAL_SOCKETS) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_LOCAL_DATAGRAM_PROTOCOL_HPP diff --git a/libtorrent/include/asio/local/stream_protocol.hpp b/libtorrent/include/asio/local/stream_protocol.hpp new file mode 100644 index 000000000..91813e8e0 --- /dev/null +++ b/libtorrent/include/asio/local/stream_protocol.hpp @@ -0,0 +1,86 @@ +// +// stream_protocol.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_LOCAL_STREAM_PROTOCOL_HPP +#define ASIO_LOCAL_STREAM_PROTOCOL_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/basic_socket_acceptor.hpp" +#include "asio/basic_socket_iostream.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/local/basic_endpoint.hpp" +#include "asio/detail/socket_types.hpp" + +#if defined(ASIO_HAS_LOCAL_SOCKETS) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace local { + +/// Encapsulates the flags needed for stream-oriented UNIX sockets. +/** + * The asio::local::stream_protocol class contains flags necessary for + * stream-oriented UNIX domain sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class stream_protocol +{ +public: + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_STREAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return 0; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return AF_UNIX; + } + + /// The type of a UNIX domain endpoint. + typedef basic_endpoint endpoint; + + /// The UNIX domain socket type. + typedef basic_stream_socket socket; + + /// The UNIX domain acceptor type. + typedef basic_socket_acceptor acceptor; + + /// The UNIX domain iostream type. + typedef basic_socket_iostream iostream; +}; + +} // namespace local +} // namespace asio + +#endif // defined(ASIO_HAS_LOCAL_SOCKETS) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_LOCAL_STREAM_PROTOCOL_HPP diff --git a/libtorrent/include/asio/posix/basic_descriptor.hpp b/libtorrent/include/asio/posix/basic_descriptor.hpp new file mode 100644 index 000000000..241a3e315 --- /dev/null +++ b/libtorrent/include/asio/posix/basic_descriptor.hpp @@ -0,0 +1,280 @@ +// +// basic_descriptor.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_POSIX_BASIC_DESCRIPTOR_HPP +#define ASIO_POSIX_BASIC_DESCRIPTOR_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 +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_io_object.hpp" +#include "asio/error.hpp" +#include "asio/posix/descriptor_base.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { +namespace posix { + +/// Provides POSIX descriptor functionality. +/** + * The posix::basic_descriptor class template provides the ability to wrap a + * POSIX descriptor. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_descriptor + : public basic_io_object, + public descriptor_base +{ +public: + /// The native representation of a descriptor. + typedef typename DescriptorService::native_type native_type; + + /// A basic_descriptor is always the lowest layer. + typedef basic_descriptor lowest_layer_type; + + /// Construct a basic_descriptor without opening it. + /** + * This constructor creates a descriptor without opening it. + * + * @param io_service The io_service object that the descriptor will use to + * dispatch handlers for any asynchronous operations performed on the + * descriptor. + */ + explicit basic_descriptor(asio::io_service& io_service) + : basic_io_object(io_service) + { + } + + /// Construct a basic_descriptor on an existing native descriptor. + /** + * This constructor creates a descriptor object to hold an existing native + * descriptor. + * + * @param io_service The io_service object that the descriptor will use to + * dispatch handlers for any asynchronous operations performed on the + * descriptor. + * + * @param native_descriptor A native descriptor. + * + * @throws asio::system_error Thrown on failure. + */ + basic_descriptor(asio::io_service& io_service, + const native_type& native_descriptor) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.assign(this->implementation, native_descriptor, ec); + asio::detail::throw_error(ec); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_descriptor cannot contain any further layers, it + * simply returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Assign an existing native descriptor to the descriptor. + /* + * This function opens the descriptor to hold an existing native descriptor. + * + * @param native_descriptor A native descriptor. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const native_type& native_descriptor) + { + asio::error_code ec; + this->service.assign(this->implementation, native_descriptor, ec); + asio::detail::throw_error(ec); + } + + /// Assign an existing native descriptor to the descriptor. + /* + * This function opens the descriptor to hold an existing native descriptor. + * + * @param native_descriptor A native descriptor. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code assign(const native_type& native_descriptor, + asio::error_code& ec) + { + return this->service.assign(this->implementation, native_descriptor, ec); + } + + /// Determine whether the descriptor is open. + bool is_open() const + { + return this->service.is_open(this->implementation); + } + + /// Close the descriptor. + /** + * This function is used to close the descriptor. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + this->service.close(this->implementation, ec); + asio::detail::throw_error(ec); + } + + /// Close the descriptor. + /** + * This function is used to close the descriptor. Any asynchronous read or + * write operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code close(asio::error_code& ec) + { + return this->service.close(this->implementation, ec); + } + + /// Get the native descriptor representation. + /** + * This function may be used to obtain the underlying representation of the + * descriptor. This is intended to allow access to native descriptor + * functionality that is not otherwise provided. + */ + native_type native() + { + return this->service.native(this->implementation); + } + + /// Cancel all asynchronous operations associated with the descriptor. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->service.cancel(this->implementation, ec); + asio::detail::throw_error(ec); + } + + /// Cancel all asynchronous operations associated with the descriptor. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code cancel(asio::error_code& ec) + { + return this->service.cancel(this->implementation, ec); + } + + /// Perform an IO control command on the descriptor. + /** + * This function is used to execute an IO control command on the descriptor. + * + * @param command The IO control command to be performed on the descriptor. + * + * @throws asio::system_error Thrown on failure. + * + * @sa IoControlCommand @n + * asio::posix::descriptor_base::bytes_readable @n + * asio::posix::descriptor_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::posix::stream_descriptor descriptor(io_service); + * ... + * asio::posix::stream_descriptor::bytes_readable command; + * descriptor.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + void io_control(IoControlCommand& command) + { + asio::error_code ec; + this->service.io_control(this->implementation, command, ec); + asio::detail::throw_error(ec); + } + + /// Perform an IO control command on the descriptor. + /** + * This function is used to execute an IO control command on the descriptor. + * + * @param command The IO control command to be performed on the descriptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa IoControlCommand @n + * asio::posix::descriptor_base::bytes_readable @n + * asio::posix::descriptor_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::posix::stream_descriptor descriptor(io_service); + * ... + * asio::posix::stream_descriptor::bytes_readable command; + * asio::error_code ec; + * descriptor.io_control(command, ec); + * if (ec) + * { + * // An error occurred. + * } + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + asio::error_code io_control(IoControlCommand& command, + asio::error_code& ec) + { + return this->service.io_control(this->implementation, command, ec); + } + +protected: + /// Protected destructor to prevent deletion through this type. + ~basic_descriptor() + { + } +}; + +} // namespace posix +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_POSIX_BASIC_DESCRIPTOR_HPP diff --git a/libtorrent/include/asio/posix/basic_stream_descriptor.hpp b/libtorrent/include/asio/posix/basic_stream_descriptor.hpp new file mode 100644 index 000000000..62185eaa0 --- /dev/null +++ b/libtorrent/include/asio/posix/basic_stream_descriptor.hpp @@ -0,0 +1,304 @@ +// +// basic_stream_descriptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_POSIX_BASIC_STREAM_DESCRIPTOR_HPP +#define ASIO_POSIX_BASIC_STREAM_DESCRIPTOR_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 +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/posix/basic_descriptor.hpp" +#include "asio/posix/stream_descriptor_service.hpp" +#include "asio/detail/throw_error.hpp" + +#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace posix { + +/// Provides stream-oriented descriptor functionality. +/** + * The posix::basic_stream_descriptor class template provides asynchronous and + * blocking stream-oriented descriptor functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class basic_stream_descriptor + : public basic_descriptor +{ +public: + /// The native representation of a descriptor. + typedef typename StreamDescriptorService::native_type native_type; + + /// Construct a basic_stream_descriptor without opening it. + /** + * This constructor creates a stream descriptor without opening it. The + * descriptor needs to be opened and then connected or accepted before data + * can be sent or received on it. + * + * @param io_service The io_service object that the stream descriptor will + * use to dispatch handlers for any asynchronous operations performed on the + * descriptor. + */ + explicit basic_stream_descriptor(asio::io_service& io_service) + : basic_descriptor(io_service) + { + } + + /// Construct a basic_stream_descriptor on an existing native descriptor. + /** + * This constructor creates a stream descriptor object to hold an existing + * native descriptor. + * + * @param io_service The io_service object that the stream descriptor will + * use to dispatch handlers for any asynchronous operations performed on the + * descriptor. + * + * @param native_descriptor The new underlying descriptor implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_descriptor(asio::io_service& io_service, + const native_type& native_descriptor) + : basic_descriptor(io_service, native_descriptor) + { + } + + /// Write some data to the descriptor. + /** + * This function is used to write data to the stream descriptor. The function + * call will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the descriptor. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * descriptor.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.write_some(this->implementation, buffers, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Write some data to the descriptor. + /** + * This function is used to write data to the stream descriptor. The function + * call will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the descriptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return this->service.write_some(this->implementation, buffers, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the stream + * descriptor. The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the descriptor. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * descriptor.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_write_some(const ConstBufferSequence& buffers, + WriteHandler handler) + { + this->service.async_write_some(this->implementation, buffers, handler); + } + + /// Read some data from the descriptor. + /** + * This function is used to read data from the stream descriptor. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * descriptor.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.read_some(this->implementation, buffers, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Read some data from the descriptor. + /** + * This function is used to read data from the stream descriptor. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return this->service.read_some(this->implementation, buffers, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the stream + * descriptor. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * descriptor.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_read_some(const MutableBufferSequence& buffers, + ReadHandler handler) + { + this->service.async_read_some(this->implementation, buffers, handler); + } +}; + +} // namespace posix +} // namespace asio + +#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_POSIX_BASIC_STREAM_DESCRIPTOR_HPP diff --git a/libtorrent/include/asio/posix/descriptor_base.hpp b/libtorrent/include/asio/posix/descriptor_base.hpp new file mode 100644 index 000000000..6ea2738df --- /dev/null +++ b/libtorrent/include/asio/posix/descriptor_base.hpp @@ -0,0 +1,93 @@ +// +// descriptor_base.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_POSIX_DESCRIPTOR_BASE_HPP +#define ASIO_POSIX_DESCRIPTOR_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/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/io_control.hpp" +#include "asio/detail/socket_option.hpp" + +namespace asio { +namespace posix { + +/// The descriptor_base class is used as a base for the basic_stream_descriptor +/// class template so that we have a common place to define the associated +/// IO control commands. +class descriptor_base +{ +public: + /// IO control command to set the blocking mode of the descriptor. + /** + * Implements the FIONBIO IO control command. + * + * @par Example + * @code + * asio::posix::stream_descriptor descriptor(io_service); + * ... + * asio::descriptor_base::non_blocking_io command(true); + * descriptor.io_control(command); + * @endcode + * + * @par Concepts: + * IoControlCommand. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined non_blocking_io; +#else + typedef asio::detail::io_control::non_blocking_io non_blocking_io; +#endif + + /// IO control command to get the amount of data that can be read without + /// blocking. + /** + * Implements the FIONREAD IO control command. + * + * @par Example + * @code + * asio::posix::stream_descriptor descriptor(io_service); + * ... + * asio::descriptor_base::bytes_readable command(true); + * descriptor.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + * + * @par Concepts: + * IoControlCommand. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined bytes_readable; +#else + typedef asio::detail::io_control::bytes_readable bytes_readable; +#endif + +protected: + /// Protected destructor to prevent deletion through this type. + ~descriptor_base() + { + } +}; + +} // namespace posix +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_POSIX_DESCRIPTOR_BASE_HPP diff --git a/libtorrent/include/asio/posix/stream_descriptor.hpp b/libtorrent/include/asio/posix/stream_descriptor.hpp new file mode 100644 index 000000000..ccf697726 --- /dev/null +++ b/libtorrent/include/asio/posix/stream_descriptor.hpp @@ -0,0 +1,39 @@ +// +// stream_descriptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_POSIX_STREAM_DESCRIPTOR_HPP +#define ASIO_POSIX_STREAM_DESCRIPTOR_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/posix/basic_stream_descriptor.hpp" + +#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace posix { + +/// Typedef for the typical usage of a stream-oriented descriptor. +typedef basic_stream_descriptor<> stream_descriptor; + +} // namespace posix +} // namespace asio + +#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_POSIX_STREAM_DESCRIPTOR_HPP diff --git a/libtorrent/include/asio/posix/stream_descriptor_service.hpp b/libtorrent/include/asio/posix/stream_descriptor_service.hpp new file mode 100644 index 000000000..df8ccbfcb --- /dev/null +++ b/libtorrent/include/asio/posix/stream_descriptor_service.hpp @@ -0,0 +1,200 @@ +// +// stream_descriptor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_POSIX_STREAM_DESCRIPTOR_SERVICE_HPP +#define ASIO_POSIX_STREAM_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/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/reactive_descriptor_service.hpp" + +#if !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR) +# if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +# define ASIO_HAS_POSIX_STREAM_DESCRIPTOR 1 +# endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#endif // !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR) + +#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace posix { + +/// Default service implementation for a stream descriptor. +class stream_descriptor_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_service::service +#else + : public asio::detail::service_base +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_service::id id; +#endif + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_EPOLL) + typedef detail::reactive_descriptor_service< + detail::epoll_reactor > service_impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::reactive_descriptor_service< + detail::kqueue_reactor > service_impl_type; +#elif defined(ASIO_HAS_DEV_POLL) + typedef detail::reactive_descriptor_service< + detail::dev_poll_reactor > service_impl_type; +#else + typedef detail::reactive_descriptor_service< + detail::select_reactor > service_impl_type; +#endif + +public: + /// The type of a stream descriptor implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef service_impl_type::implementation_type implementation_type; +#endif + + /// The native descriptor type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef service_impl_type::native_type native_type; +#endif + + /// Construct a new stream descriptor service for the specified io_service. + explicit stream_descriptor_service(asio::io_service& io_service) + : asio::detail::service_base(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined descriptorr objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new stream descriptor implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a stream descriptor implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Assign an existing native descriptor to a stream descriptor. + asio::error_code assign(implementation_type& impl, + const native_type& native_descriptor, asio::error_code& ec) + { + return service_impl_.assign(impl, native_descriptor, ec); + } + + /// Determine whether the descriptor is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a stream descriptor implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.close(impl, ec); + } + + /// Get the native descriptor implementation. + native_type native(implementation_type& impl) + { + return service_impl_.native(impl); + } + + /// Cancel all asynchronous operations associated with the descriptor. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.cancel(impl, ec); + } + + /// Perform an IO control command on the descriptor. + template + asio::error_code io_control(implementation_type& impl, + IoControlCommand& command, asio::error_code& ec) + { + return service_impl_.io_control(impl, command, ec); + } + + /// Write the given data to the stream. + template + std::size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.write_some(impl, buffers, ec); + } + + /// Start an asynchronous write. + template + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, WriteHandler descriptorr) + { + service_impl_.async_write_some(impl, buffers, descriptorr); + } + + /// Read some data from the stream. + template + std::size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.read_some(impl, buffers, ec); + } + + /// Start an asynchronous read. + template + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, ReadHandler descriptorr) + { + service_impl_.async_read_some(impl, buffers, descriptorr); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace posix +} // namespace asio + +#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_POSIX_STREAM_DESCRIPTOR_SERVICE_HPP diff --git a/libtorrent/include/asio/raw_socket_service.hpp b/libtorrent/include/asio/raw_socket_service.hpp new file mode 100644 index 000000000..c9d251581 --- /dev/null +++ b/libtorrent/include/asio/raw_socket_service.hpp @@ -0,0 +1,323 @@ +// +// raw_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_RAW_SOCKET_SERVICE_HPP +#define ASIO_RAW_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/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/reactive_socket_service.hpp" +#include "asio/detail/win_iocp_socket_service.hpp" + +namespace asio { + +/// Default service implementation for a raw socket. +template +class raw_socket_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_service::service +#else + : public asio::detail::service_base > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_service::id id; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_socket_service service_impl_type; +#elif defined(ASIO_HAS_EPOLL) + typedef detail::reactive_socket_service< + Protocol, detail::epoll_reactor > service_impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::reactive_socket_service< + Protocol, detail::kqueue_reactor > service_impl_type; +#elif defined(ASIO_HAS_DEV_POLL) + typedef detail::reactive_socket_service< + Protocol, detail::dev_poll_reactor > service_impl_type; +#else + typedef detail::reactive_socket_service< + Protocol, detail::select_reactor > service_impl_type; +#endif + +public: + /// The type of a raw socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef typename service_impl_type::native_type native_type; +#endif + + /// Construct a new raw socket service for the specified io_service. + explicit raw_socket_service(asio::io_service& io_service) + : asio::detail::service_base< + raw_socket_service >(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new raw socket implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a raw socket implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + // Open a new raw socket implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (protocol.type() == SOCK_RAW) + service_impl_.open(impl, protocol, ec); + else + ec = asio::error::invalid_argument; + return ec; + } + + /// Assign an existing native socket to a raw socket. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_type& native_socket, + asio::error_code& ec) + { + return service_impl_.assign(impl, protocol, native_socket, ec); + } + + /// Determine whether the socket is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a raw socket implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.close(impl, ec); + } + + /// Get the native socket implementation. + native_type native(implementation_type& impl) + { + return service_impl_.native(impl); + } + + /// Cancel all asynchronous operations associated with the socket. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.cancel(impl, 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 + { + return service_impl_.at_mark(impl, ec); + } + + /// Determine the number of bytes available for reading. + std::size_t available(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.available(impl, ec); + } + + // Bind the raw socket to the specified local endpoint. + asio::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + return service_impl_.bind(impl, endpoint, ec); + } + + /// Connect the raw socket to the specified endpoint. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + return service_impl_.connect(impl, peer_endpoint, ec); + } + + /// Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, ConnectHandler handler) + { + service_impl_.async_connect(impl, peer_endpoint, handler); + } + + /// Set a socket option. + template + asio::error_code set_option(implementation_type& impl, + const SettableSocketOption& option, asio::error_code& ec) + { + return service_impl_.set_option(impl, option, ec); + } + + /// Get a socket option. + template + asio::error_code get_option(const implementation_type& impl, + GettableSocketOption& option, asio::error_code& ec) const + { + return service_impl_.get_option(impl, option, ec); + } + + /// Perform an IO control command on the socket. + template + asio::error_code io_control(implementation_type& impl, + IoControlCommand& command, asio::error_code& ec) + { + return service_impl_.io_control(impl, command, ec); + } + + /// Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.local_endpoint(impl, ec); + } + + /// Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.remote_endpoint(impl, ec); + } + + /// Disable sends or receives on the socket. + asio::error_code shutdown(implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + return service_impl_.shutdown(impl, what, ec); + } + + /// Send the given data to the peer. + template + std::size_t send(implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.send(impl, buffers, flags, ec); + } + + /// Start an asynchronous send. + template + void async_send(implementation_type& impl, const ConstBufferSequence& buffers, + socket_base::message_flags flags, WriteHandler handler) + { + service_impl_.async_send(impl, buffers, flags, handler); + } + + /// Send raw data to the specified endpoint. + template + std::size_t send_to(implementation_type& impl, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.send_to(impl, buffers, destination, flags, ec); + } + + /// Start an asynchronous send. + template + void async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags, WriteHandler handler) + { + service_impl_.async_send_to(impl, buffers, destination, flags, handler); + } + + /// Receive some data from the peer. + template + std::size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.receive(impl, buffers, flags, ec); + } + + /// Start an asynchronous receive. + template + void async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, ReadHandler handler) + { + service_impl_.async_receive(impl, buffers, flags, handler); + } + + /// Receive raw data with the endpoint of the sender. + template + std::size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.receive_from(impl, buffers, sender_endpoint, flags, + ec); + } + + /// Start an asynchronous receive that will get the endpoint of the sender. + template + void async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, ReadHandler handler) + { + service_impl_.async_receive_from(impl, buffers, sender_endpoint, flags, + handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_RAW_SOCKET_SERVICE_HPP diff --git a/libtorrent/include/asio/read.hpp b/libtorrent/include/asio/read.hpp index 46bca2622..7c0b82247 100644 --- a/libtorrent/include/asio/read.hpp +++ b/libtorrent/include/asio/read.hpp @@ -29,6 +29,9 @@ namespace asio { /** * @defgroup read asio::read + * + * @brief Attempt to read a certain amount of data from a stream before + * returning. */ /*@{*/ @@ -269,6 +272,9 @@ std::size_t read(SyncReadStream& s, basic_streambuf& b, /*@}*/ /** * @defgroup async_read asio::async_read + * + * @brief Start an asynchronous operation to read a certain amount of data from + * a stream. */ /*@{*/ diff --git a/libtorrent/include/asio/read_at.hpp b/libtorrent/include/asio/read_at.hpp new file mode 100644 index 000000000..bcc247a0f --- /dev/null +++ b/libtorrent/include/asio/read_at.hpp @@ -0,0 +1,568 @@ +// +// read_at.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_READ_AT_HPP +#define ASIO_READ_AT_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 +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_streambuf.hpp" +#include "asio/error.hpp" + +namespace asio { + +/** + * @defgroup read_at asio::read_at + * + * @brief Attempt to read a certain amount of data at the specified offset + * before returning. + */ +/*@{*/ + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * device. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::read_at(d, 42, asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::read_at( + * d, 42, buffers, + * asio::transfer_all()); @endcode + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers); + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * device. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * // Result of latest read_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the device's read_some_at function are + * required. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::read_at(d, 42, asio::buffer(data, size), + * asio::transfer_at_least(32)); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition); + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * device. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest read_some_at + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the device's read_some_at function are + * required. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec); + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::read_at( + * d, 42, b, + * asio::transfer_all()); @endcode + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, basic_streambuf& b); + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * // Result of latest read_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the device's read_some_at function are + * required. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, basic_streambuf& b, + CompletionCondition completion_condition); + +/// Attempt to read a certain amount of data at the specified offset before +/// returning. +/** + * This function is used to read a certain number of bytes of data from a + * random access device at the specified offset. The call will block until one + * of the following conditions is true: + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the device's + * read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the SyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * // Result of latest read_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the device's read_some_at function are + * required. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec); + +/*@}*/ +/** + * @defgroup async_read_at asio::async_read_at + * + * @brief Start an asynchronous operation to read a certain amount of data at + * the specified offset. + */ +/*@{*/ + +/// Start an asynchronous operation to read a certain amount of data at the +/// specified offset. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a random access device at the specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the device's + * async_read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the AsyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * device. Although the buffers object may be copied as necessary, ownership of + * the underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes copied into the buffers. If an error + * // occurred, this will be the number of bytes successfully + * // transferred prior to the error. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * asio::async_read_at(d, 42, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::async_read_at( + * d, 42, buffers, + * asio::transfer_all(), + * handler); @endcode + */ +template +void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, + const MutableBufferSequence& buffers, ReadHandler handler); + +/// Start an asynchronous operation to read a certain amount of data at the +/// specified offset. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a random access device at the specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * @param d The device from which the data is to be read. The type must support + * the AsyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * device. Although the buffers object may be copied as necessary, ownership of + * the underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * // Result of latest read_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the device's async_read_some_at function are + * required. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes copied into the buffers. If an error + * // occurred, this will be the number of bytes successfully + * // transferred prior to the error. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::async_read_at(d, 42, + * asio::buffer(data, size), + * asio::transfer_at_least(32), + * handler); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +void async_read_at(AsyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, ReadHandler handler); + +/// Start an asynchronous operation to read a certain amount of data at the +/// specified offset. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a random access device at the specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the device's + * async_read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the AsyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param b A basic_streambuf object into which the data will be read. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes copied into the buffers. If an error + * // occurred, this will be the number of bytes successfully + * // transferred prior to the error. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note This overload is equivalent to calling: + * @code asio::async_read_at( + * d, 42, b, + * asio::transfer_all(), + * handler); @endcode + */ +template +void async_read_at(AsyncRandomAccessReadDevice& d, boost::uint64_t offset, + basic_streambuf& b, ReadHandler handler); + +/// Start an asynchronous operation to read a certain amount of data at the +/// specified offset. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a random access device at the specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the device's + * async_read_some_at function. + * + * @param d The device from which the data is to be read. The type must support + * the AsyncRandomAccessReadDevice concept. + * + * @param offset The offset at which the data will be read. + * + * @param b A basic_streambuf object into which the data will be read. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * // Result of latest read_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the device's async_read_some_at function are + * required. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes copied into the buffers. If an error + * // occurred, this will be the number of bytes successfully + * // transferred prior to the error. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ +template +void async_read_at(AsyncRandomAccessReadDevice& d, + boost::uint64_t offset, basic_streambuf& b, + CompletionCondition completion_condition, ReadHandler handler); + +/*@}*/ + +} // namespace asio + +#include "asio/impl/read_at.ipp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_READ_AT_HPP diff --git a/libtorrent/include/asio/read_until.hpp b/libtorrent/include/asio/read_until.hpp index 8c7e3d379..69922e266 100644 --- a/libtorrent/include/asio/read_until.hpp +++ b/libtorrent/include/asio/read_until.hpp @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include #include #include "asio/detail/pop_options.hpp" @@ -29,12 +32,45 @@ namespace asio { +namespace detail +{ + template + struct has_result_type + { + struct big { char a[100]; }; + template static big helper(U, ...); + template static char helper(U, typename U::result_type* = 0); + static const T& ref(); + enum { value = (sizeof((helper)((ref)())) == 1) }; + }; +} // namespace detail + +/// Type trait used to determine whether a type can be used as a match condition +/// function with read_until and async_read_until. +template +struct is_match_condition +{ +#if defined(GENERATING_DOCUMENTATION) + /// The value member is true if the type may be used as a match condition. + static const bool value; +#else + enum + { + value = boost::is_function::type>::value + || detail::has_result_type::value + }; +#endif +}; + /** * @defgroup read_until asio::read_until + * + * @brief Read data into a streambuf until it contains a delimiter, matches a + * regular expression, or a function object indicates a match. */ /*@{*/ -/// Read data into a streambuf until a delimiter is encountered. +/// Read data into a streambuf until it contains a specified delimiter. /** * This function is used to read data into the specified streambuf until the * streambuf's get area contains the specified delimiter. The call will block @@ -60,6 +96,10 @@ namespace asio { * * @throws asio::system_error Thrown on failure. * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond the delimiter. An application will typically leave + * that data in the streambuf for a subsequent read_until operation to examine. + * * @par Example * To read data into a streambuf until a newline is encountered: * @code asio::streambuf b; @@ -72,7 +112,7 @@ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, char delim); -/// Read data into a streambuf until a delimiter is encountered. +/// Read data into a streambuf until it contains a specified delimiter. /** * This function is used to read data into the specified streambuf until the * streambuf's get area contains the specified delimiter. The call will block @@ -97,13 +137,17 @@ std::size_t read_until(SyncReadStream& s, * * @returns The number of bytes in the streambuf's get area up to and including * the delimiter. Returns 0 if an error occurred. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond the delimiter. An application will typically leave + * that data in the streambuf for a subsequent read_until operation to examine. */ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, char delim, asio::error_code& ec); -/// Read data into a streambuf until a delimiter is encountered. +/// Read data into a streambuf until it contains a specified delimiter. /** * This function is used to read data into the specified streambuf until the * streambuf's get area contains the specified delimiter. The call will block @@ -129,6 +173,10 @@ std::size_t read_until(SyncReadStream& s, * * @throws asio::system_error Thrown on failure. * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond the delimiter. An application will typically leave + * that data in the streambuf for a subsequent read_until operation to examine. + * * @par Example * To read data into a streambuf until a newline is encountered: * @code asio::streambuf b; @@ -141,7 +189,7 @@ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const std::string& delim); -/// Read data into a streambuf until a delimiter is encountered. +/// Read data into a streambuf until it contains a specified delimiter. /** * This function is used to read data into the specified streambuf until the * streambuf's get area contains the specified delimiter. The call will block @@ -166,13 +214,18 @@ std::size_t read_until(SyncReadStream& s, * * @returns The number of bytes in the streambuf's get area up to and including * the delimiter. Returns 0 if an error occurred. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond the delimiter. An application will typically leave + * that data in the streambuf for a subsequent read_until operation to examine. */ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const std::string& delim, asio::error_code& ec); -/// Read data into a streambuf until a regular expression is located. +/// Read data into a streambuf until some part of the data it contains matches +/// a regular expression. /** * This function is used to read data into the specified streambuf until the * streambuf's get area contains some data that matches a regular expression. @@ -198,6 +251,11 @@ std::size_t read_until(SyncReadStream& s, * * @throws asio::system_error Thrown on failure. * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond that which matched the regular expression. An + * application will typically leave that data in the streambuf for a subsequent + * read_until operation to examine. + * * @par Example * To read data into a streambuf until a CR-LF sequence is encountered: * @code asio::streambuf b; @@ -210,7 +268,8 @@ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const boost::regex& expr); -/// Read data into a streambuf until a regular expression is located. +/// Read data into a streambuf until some part of the data it contains matches +/// a regular expression. /** * This function is used to read data into the specified streambuf until the * streambuf's get area contains some data that matches a regular expression. @@ -236,20 +295,189 @@ std::size_t read_until(SyncReadStream& s, * @returns The number of bytes in the streambuf's get area up to and including * the substring that matches the regular expression. Returns 0 if an error * occurred. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond that which matched the regular expression. An + * application will typically leave that data in the streambuf for a subsequent + * read_until operation to examine. */ template std::size_t read_until(SyncReadStream& s, asio::basic_streambuf& b, const boost::regex& expr, asio::error_code& ec); +/// Read data into a streambuf until a function object indicates a match. +/** + * This function is used to read data into the specified streambuf until a + * user-defined match condition function object, when applied to the data + * contained in the streambuf, indicates a successful match. The call will + * block until one of the following conditions is true: + * + * @li The match condition function object returns a std::pair where the second + * element evaluates to true. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the match condition function object already indicates + * a match, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param match_condition The function object to be called to determine whether + * a match exists. The signature of the function object must be: + * @code pair match_condition(iterator begin, iterator end); + * @endcode + * where @c iterator represents the type: + * @code buffers_iterator::const_buffers_type> + * @endcode + * The iterator parameters @c begin and @c end define the range of bytes to be + * scanned to determine whether there is a match. The @c first member of the + * return value is an iterator marking one-past-the-end of the bytes that have + * been consumed by the match function. This iterator is used to calculate the + * @c begin parameter for any subsequent invocation of the match condition. The + * @c second member of the return value is true if a match has been found, false + * otherwise. + * + * @returns The number of bytes in the streambuf's get area that have been fully + * consumed by the match function. + * + * @throws asio::system_error Thrown on failure. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond that which matched the function object. An application + * will typically leave that data in the streambuf for a subsequent + * + * @note The default implementation of the @c is_match_condition type trait + * evaluates to true for function pointers and function objects with a + * @c result_type typedef. It must be specialised for other user-defined + * function objects. + * + * @par Examples + * To read data into a streambuf until whitespace is encountered: + * @code typedef asio::buffers_iterator< + * asio::streambuf::const_buffers_type> iterator; + * + * std::pair + * match_whitespace(iterator begin, iterator end) + * { + * iterator i = begin; + * while (i != end) + * if (std::isspace(*i++)) + * return std::make_pair(i, true); + * return std::make_pair(i, false); + * } + * ... + * asio::streambuf b; + * asio::read_until(s, b, match_whitespace); + * @endcode + * + * To read data into a streambuf until a matching character is found: + * @code class match_char + * { + * public: + * explicit match_char(char c) : c_(c) {} + * + * template + * std::pair operator()( + * Iterator begin, Iterator end) const + * { + * Iterator i = begin; + * while (i != end) + * if (c_ == *i++) + * return std::make_pair(i, true); + * return std::make_pair(i, false); + * } + * + * private: + * char c_; + * }; + * + * namespace asio { + * template <> struct is_match_condition + * : public boost::true_type {}; + * } // namespace asio + * ... + * asio::streambuf b; + * asio::read_until(s, b, match_char('a')); + * @endcode + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, MatchCondition match_condition, + typename boost::enable_if >::type* = 0); + +/// Read data into a streambuf until a function object indicates a match. +/** + * This function is used to read data into the specified streambuf until a + * user-defined match condition function object, when applied to the data + * contained in the streambuf, indicates a successful match. The call will + * block until one of the following conditions is true: + * + * @li The match condition function object returns a std::pair where the second + * element evaluates to true. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the match condition function object already indicates + * a match, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param match_condition The function object to be called to determine whether + * a match exists. The signature of the function object must be: + * @code pair match_condition(iterator begin, iterator end); + * @endcode + * where @c iterator represents the type: + * @code buffers_iterator::const_buffers_type> + * @endcode + * The iterator parameters @c begin and @c end define the range of bytes to be + * scanned to determine whether there is a match. The @c first member of the + * return value is an iterator marking one-past-the-end of the bytes that have + * been consumed by the match function. This iterator is used to calculate the + * @c begin parameter for any subsequent invocation of the match condition. The + * @c second member of the return value is true if a match has been found, false + * otherwise. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes in the streambuf's get area that have been fully + * consumed by the match function. Returns 0 if an error occurred. + * + * @note After a successful read_until operation, the streambuf may contain + * additional data beyond that which matched the function object. An application + * will typically leave that data in the streambuf for a subsequent + * + * @note The default implementation of the @c is_match_condition type trait + * evaluates to true for function pointers and function objects with a + * @c result_type typedef. It must be specialised for other user-defined + * function objects. + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, + MatchCondition match_condition, asio::error_code& ec, + typename boost::enable_if >::type* = 0); + /*@}*/ /** -* @defgroup async_read_until asio::async_read_until -*/ + * @defgroup async_read_until asio::async_read_until + * + * @brief Start an asynchronous operation to read data into a streambuf until it + * contains a delimiter, matches a regular expression, or a function object + * indicates a match. + */ /*@{*/ -/// Start an asynchronous operation to read data into a streambuf until a -/// delimiter is encountered. +/// Start an asynchronous operation to read data into a streambuf until it +/// contains a specified delimiter. /** * This function is used to asynchronously read data into the specified * streambuf until the streambuf's get area contains the specified delimiter. @@ -277,18 +505,24 @@ std::size_t read_until(SyncReadStream& s, * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( - * const asio::error_code& error, // Result of operation. + * // Result of operation. + * const asio::error_code& error, * - * std::size_t bytes_transferred // The number of bytes in the - * // streambuf's get area up to - * // and including the delimiter. - * // 0 if an error occurred. + * // The number of bytes in the streambuf's get + * // area up to and including the delimiter. + * // 0 if an error occurred. + * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * + * @note After a successful async_read_until operation, the streambuf may + * contain additional data beyond the delimiter. An application will typically + * leave that data in the streambuf for a subsequent async_read_until operation + * to examine. + * * @par Example * To asynchronously read data into a streambuf until a newline is encountered: * @code asio::streambuf b; @@ -311,8 +545,8 @@ void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, char delim, ReadHandler handler); -/// Start an asynchronous operation to read data into a streambuf until a -/// delimiter is encountered. +/// Start an asynchronous operation to read data into a streambuf until it +/// contains a specified delimiter. /** * This function is used to asynchronously read data into the specified * streambuf until the streambuf's get area contains the specified delimiter. @@ -340,18 +574,24 @@ void async_read_until(AsyncReadStream& s, * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( - * const asio::error_code& error, // Result of operation. + * // Result of operation. + * const asio::error_code& error, * - * std::size_t bytes_transferred // The number of bytes in the - * // streambuf's get area up to - * // and including the delimiter. - * // 0 if an error occurred. + * // The number of bytes in the streambuf's get + * // area up to and including the delimiter. + * // 0 if an error occurred. + * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * + * @note After a successful async_read_until operation, the streambuf may + * contain additional data beyond the delimiter. An application will typically + * leave that data in the streambuf for a subsequent async_read_until operation + * to examine. + * * @par Example * To asynchronously read data into a streambuf until a newline is encountered: * @code asio::streambuf b; @@ -374,8 +614,8 @@ void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, const std::string& delim, ReadHandler handler); -/// Start an asynchronous operation to read data into a streambuf until a -/// regular expression is located. +/// Start an asynchronous operation to read data into a streambuf until some +/// part of its data matches a regular expression. /** * This function is used to asynchronously read data into the specified * streambuf until the streambuf's get area contains some data that matches a @@ -404,20 +644,25 @@ void async_read_until(AsyncReadStream& s, * Copies will be made of the handler as required. The function signature of the * handler must be: * @code void handler( - * const asio::error_code& error, // Result of operation. + * // Result of operation. + * const asio::error_code& error, * - * std::size_t bytes_transferred // The number of bytes in the - * // streambuf's get area up to - * // and including the substring - * // that matches the regular. - * // expression. 0 if an error - * // occurred. + * // The number of bytes in the streambuf's get + * // area up to and including the substring + * // that matches the regular. expression. + * // 0 if an error occurred. + * std::size_t bytes_transferred * ); @endcode * Regardless of whether the asynchronous operation completes immediately or * not, the handler will not be invoked from within this function. Invocation of * the handler will be performed in a manner equivalent to using * asio::io_service::post(). * + * @note After a successful async_read_until operation, the streambuf may + * contain additional data beyond that which matched the regular expression. An + * application will typically leave that data in the streambuf for a subsequent + * async_read_until operation to examine. + * * @par Example * To asynchronously read data into a streambuf until a CR-LF sequence is * encountered: @@ -441,6 +686,132 @@ void async_read_until(AsyncReadStream& s, asio::basic_streambuf& b, const boost::regex& expr, ReadHandler handler); +/// Start an asynchronous operation to read data into a streambuf until a +/// function object indicates a match. +/** + * This function is used to asynchronously read data into the specified + * streambuf until a user-defined match condition function object, when applied + * to the data contained in the streambuf, indicates a successful match. The + * function call always returns immediately. The asynchronous operation will + * continue until one of the following conditions is true: + * + * @li The match condition function object returns a std::pair where the second + * element evaluates to true. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function. If the match condition function object already + * indicates a match, the operation completes immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param match_condition The function object to be called to determine whether + * a match exists. The signature of the function object must be: + * @code pair match_condition(iterator begin, iterator end); + * @endcode + * where @c iterator represents the type: + * @code buffers_iterator::const_buffers_type> + * @endcode + * The iterator parameters @c begin and @c end define the range of bytes to be + * scanned to determine whether there is a match. The @c first member of the + * return value is an iterator marking one-past-the-end of the bytes that have + * been consumed by the match function. This iterator is used to calculate the + * @c begin parameter for any subsequent invocation of the match condition. The + * @c second member of the return value is true if a match has been found, false + * otherwise. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // The number of bytes in the streambuf's get + * // area that have been fully consumed by the + * // match function. O if an error occurred. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note After a successful async_read_until operation, the streambuf may + * contain additional data beyond that which matched the function object. An + * application will typically leave that data in the streambuf for a subsequent + * async_read_until operation to examine. + * + * @note The default implementation of the @c is_match_condition type trait + * evaluates to true for function pointers and function objects with a + * @c result_type typedef. It must be specialised for other user-defined + * function objects. + * + * @par Examples + * To asynchronously read data into a streambuf until whitespace is encountered: + * @code typedef asio::buffers_iterator< + * asio::streambuf::const_buffers_type> iterator; + * + * std::pair + * match_whitespace(iterator begin, iterator end) + * { + * iterator i = begin; + * while (i != end) + * if (std::isspace(*i++)) + * return std::make_pair(i, true); + * return std::make_pair(i, false); + * } + * ... + * void handler(const asio::error_code& e, std::size_t size); + * ... + * asio::streambuf b; + * asio::async_read_until(s, b, match_whitespace, handler); + * @endcode + * + * To asynchronously read data into a streambuf until a matching character is + * found: + * @code class match_char + * { + * public: + * explicit match_char(char c) : c_(c) {} + * + * template + * std::pair operator()( + * Iterator begin, Iterator end) const + * { + * Iterator i = begin; + * while (i != end) + * if (c_ == *i++) + * return std::make_pair(i, true); + * return std::make_pair(i, false); + * } + * + * private: + * char c_; + * }; + * + * namespace asio { + * template <> struct is_match_condition + * : public boost::true_type {}; + * } // namespace asio + * ... + * void handler(const asio::error_code& e, std::size_t size); + * ... + * asio::streambuf b; + * asio::async_read_until(s, b, match_char('a'), handler); + * @endcode + */ +template +void async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, + MatchCondition match_condition, ReadHandler handler, + typename boost::enable_if >::type* = 0); + /*@}*/ } // namespace asio diff --git a/libtorrent/include/asio/serial_port.hpp b/libtorrent/include/asio/serial_port.hpp new file mode 100644 index 000000000..608ba299a --- /dev/null +++ b/libtorrent/include/asio/serial_port.hpp @@ -0,0 +1,38 @@ +// +// serial_port.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_SERIAL_PORT_HPP +#define ASIO_SERIAL_PORT_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/basic_serial_port.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { + +/// Typedef for the typical usage of a serial port. +typedef basic_serial_port<> serial_port; + +} // namespace asio + +#endif // defined(ASIO_HAS_SERIAL_PORT) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SERIAL_PORT_HPP diff --git a/libtorrent/include/asio/serial_port_base.hpp b/libtorrent/include/asio/serial_port_base.hpp new file mode 100644 index 000000000..2f3d7025b --- /dev/null +++ b/libtorrent/include/asio/serial_port_base.hpp @@ -0,0 +1,157 @@ +// +// serial_port_base.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_SERIAL_PORT_BASE_HPP +#define ASIO_SERIAL_PORT_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/push_options.hpp" +#include +#include +#include +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +# include +#endif +#include "asio/detail/pop_options.hpp" + +#include "asio/error_code.hpp" +#include "asio/detail/socket_types.hpp" + +#if defined(GENERATING_DOCUMENTATION) +# define ASIO_OPTION_STORAGE implementation_defined +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# define ASIO_OPTION_STORAGE DCB +#else +# define ASIO_OPTION_STORAGE termios +#endif + +namespace asio { + +/// The serial_port_base class is used as a base for the basic_serial_port class +/// template so that we have a common place to define the serial port options. +class serial_port_base +{ +public: + /// Serial port option to permit changing the baud rate. + /** + * Implements changing the baud rate for a given serial port. + */ + class baud_rate + { + public: + explicit baud_rate(unsigned int rate = 0); + unsigned int value() const; + asio::error_code store(ASIO_OPTION_STORAGE& storage, + asio::error_code& ec) const; + asio::error_code load(const ASIO_OPTION_STORAGE& storage, + asio::error_code& ec); + private: + unsigned int value_; + }; + + /// Serial port option to permit changing the flow control. + /** + * Implements changing the flow control for a given serial port. + */ + class flow_control + { + public: + enum type { none, software, hardware }; + explicit flow_control(type t = none); + type value() const; + asio::error_code store(ASIO_OPTION_STORAGE& storage, + asio::error_code& ec) const; + asio::error_code load(const ASIO_OPTION_STORAGE& storage, + asio::error_code& ec); + private: + type value_; + }; + + /// Serial port option to permit changing the parity. + /** + * Implements changing the parity for a given serial port. + */ + class parity + { + public: + enum type { none, odd, even }; + explicit parity(type t = none); + type value() const; + asio::error_code store(ASIO_OPTION_STORAGE& storage, + asio::error_code& ec) const; + asio::error_code load(const ASIO_OPTION_STORAGE& storage, + asio::error_code& ec); + private: + type value_; + }; + + /// Serial port option to permit changing the number of stop bits. + /** + * Implements changing the number of stop bits for a given serial port. + */ + class stop_bits + { + public: + enum type { one, onepointfive, two }; + explicit stop_bits(type t = one); + type value() const; + asio::error_code store(ASIO_OPTION_STORAGE& storage, + asio::error_code& ec) const; + asio::error_code load(const ASIO_OPTION_STORAGE& storage, + asio::error_code& ec); + private: + type value_; + }; + + /// Serial port option to permit changing the character size. + /** + * Implements changing the character size for a given serial port. + */ + class character_size + { + public: + explicit character_size(unsigned int t = 8); + unsigned int value() const; + asio::error_code store(ASIO_OPTION_STORAGE& storage, + asio::error_code& ec) const; + asio::error_code load(const ASIO_OPTION_STORAGE& storage, + asio::error_code& ec); + private: + unsigned int value_; + }; + +protected: + /// Protected destructor to prevent deletion through this type. + ~serial_port_base() + { + } + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +private: + // Workaround to enable the empty base optimisation with Borland C++. + char dummy_; +#endif +}; + +} // namespace asio + +#include "asio/impl/serial_port_base.ipp" + +#undef ASIO_OPTION_STORAGE + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SERIAL_PORT_BASE_HPP diff --git a/libtorrent/include/asio/serial_port_service.hpp b/libtorrent/include/asio/serial_port_service.hpp new file mode 100644 index 000000000..e77a53ee4 --- /dev/null +++ b/libtorrent/include/asio/serial_port_service.hpp @@ -0,0 +1,222 @@ +// +// serial_port_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_SERIAL_PORT_SERVICE_HPP +#define ASIO_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 +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/reactive_serial_port_service.hpp" +#include "asio/detail/win_iocp_serial_port_service.hpp" + +#if !defined(ASIO_DISABLE_SERIAL_PORT) +# if defined(ASIO_HAS_IOCP) \ + || !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +# define ASIO_HAS_SERIAL_PORT 1 +# endif // defined(ASIO_HAS_IOCP) +#endif // !defined(ASIO_DISABLE_STREAM_HANDLE) + +#if defined(ASIO_HAS_SERIAL_PORT) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { + +/// Default service implementation for a serial port. +class serial_port_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_service::service +#else + : public asio::detail::service_base +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_service::id id; +#endif + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_serial_port_service service_impl_type; +#elif defined(ASIO_HAS_EPOLL) + typedef detail::reactive_serial_port_service< + detail::epoll_reactor > service_impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::reactive_serial_port_service< + detail::kqueue_reactor > service_impl_type; +#elif defined(ASIO_HAS_DEV_POLL) + typedef detail::reactive_serial_port_service< + detail::dev_poll_reactor > service_impl_type; +#else + typedef detail::reactive_serial_port_service< + detail::select_reactor > service_impl_type; +#endif + +public: + /// The type of a serial port implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef service_impl_type::implementation_type implementation_type; +#endif + + /// The native handle type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef service_impl_type::native_type native_type; +#endif + + /// Construct a new serial port service for the specified io_service. + explicit serial_port_service(asio::io_service& io_service) + : asio::detail::service_base(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new serial port implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a serial port implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Open a serial port. + asio::error_code open(implementation_type& impl, + const std::string& device, asio::error_code& ec) + { + return service_impl_.open(impl, device, ec); + } + + /// Assign an existing native handle to a serial port. + asio::error_code assign(implementation_type& impl, + const native_type& native_handle, asio::error_code& ec) + { + return service_impl_.assign(impl, native_handle, ec); + } + + /// Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a serial port implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.close(impl, ec); + } + + /// Get the native handle implementation. + native_type native(implementation_type& impl) + { + return service_impl_.native(impl); + } + + /// Cancel all asynchronous operations associated with the handle. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.cancel(impl, ec); + } + + /// Set a serial port option. + template + asio::error_code set_option(implementation_type& impl, + const SettableSerialPortOption& option, asio::error_code& ec) + { + return service_impl_.set_option(impl, option, ec); + } + + /// Get a serial port option. + template + asio::error_code get_option(const implementation_type& impl, + GettableSerialPortOption& option, asio::error_code& ec) const + { + return service_impl_.get_option(impl, option, ec); + } + + /// Send a break sequence to the serial port. + asio::error_code send_break(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.send_break(impl, ec); + } + + /// Write the given data to the stream. + template + std::size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.write_some(impl, buffers, ec); + } + + /// Start an asynchronous write. + template + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, WriteHandler handler) + { + service_impl_.async_write_some(impl, buffers, handler); + } + + /// Read some data from the stream. + template + std::size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.read_some(impl, buffers, ec); + } + + /// Start an asynchronous read. + template + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, ReadHandler handler) + { + service_impl_.async_read_some(impl, buffers, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio + +#endif // defined(ASIO_HAS_SERIAL_PORT) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SERIAL_PORT_SERVICE_HPP diff --git a/libtorrent/include/asio/ssl/detail/openssl_operation.hpp b/libtorrent/include/asio/ssl/detail/openssl_operation.hpp index 503559e7f..c8603ac1e 100644 --- a/libtorrent/include/asio/ssl/detail/openssl_operation.hpp +++ b/libtorrent/include/asio/ssl/detail/openssl_operation.hpp @@ -168,14 +168,11 @@ public: ((::SSL_get_shutdown( session_ ) & SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN); - if (is_shut_down_sent && is_shut_down_received && is_operation_done) + if (is_shut_down_sent && is_shut_down_received && is_operation_done && !is_write_needed) // SSL connection is shut down cleanly return handler_(asio::error_code(), 1); - if (is_shut_down_received && !is_write_needed) - return handler_(asio::error::eof, 0); - - if (is_shut_down_received) + if (is_shut_down_received && !is_operation_done) // Shutdown has been requested, while we were reading or writing... // abort our action... return handler_(asio::error::shut_down, 0); @@ -225,7 +222,7 @@ public: return start(); } - else if (is_read_needed) + else if (is_read_needed || (is_shut_down_sent && !is_shut_down_received)) { return read_(); } diff --git a/libtorrent/include/asio/thread.hpp b/libtorrent/include/asio/thread.hpp index 7cc8cdf0b..b0f786c6b 100644 --- a/libtorrent/include/asio/thread.hpp +++ b/libtorrent/include/asio/thread.hpp @@ -58,7 +58,7 @@ public: */ template explicit thread(Function f) - : impl_(f) + : impl_(f, asio::detail::thread::external) { } diff --git a/libtorrent/include/asio/version.hpp b/libtorrent/include/asio/version.hpp index 5da4fd99c..df2fa9f84 100644 --- a/libtorrent/include/asio/version.hpp +++ b/libtorrent/include/asio/version.hpp @@ -18,6 +18,6 @@ // ASIO_VERSION % 100 is the sub-minor version // ASIO_VERSION / 100 % 1000 is the minor version // ASIO_VERSION / 100000 is the major version -#define ASIO_VERSION 100000 // 1.0.0 +#define ASIO_VERSION 100100 // 1.1.0 #endif // ASIO_VERSION_HPP diff --git a/libtorrent/include/asio/windows/basic_handle.hpp b/libtorrent/include/asio/windows/basic_handle.hpp new file mode 100644 index 000000000..f436436b7 --- /dev/null +++ b/libtorrent/include/asio/windows/basic_handle.hpp @@ -0,0 +1,211 @@ +// +// basic_handle.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_WINDOWS_BASIC_HANDLE_HPP +#define ASIO_WINDOWS_BASIC_HANDLE_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 +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_io_object.hpp" +#include "asio/error.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { +namespace windows { + +/// Provides Windows handle functionality. +/** + * The windows::basic_handle class template provides the ability to wrap a + * Windows handle. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_handle + : public basic_io_object +{ +public: + /// The native representation of a handle. + typedef typename HandleService::native_type native_type; + + /// A basic_handle is always the lowest layer. + typedef basic_handle lowest_layer_type; + + /// Construct a basic_handle without opening it. + /** + * This constructor creates a handle without opening it. + * + * @param io_service The io_service object that the handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + */ + explicit basic_handle(asio::io_service& io_service) + : basic_io_object(io_service) + { + } + + /// Construct a basic_handle on an existing native handle. + /** + * This constructor creates a handle object to hold an existing native handle. + * + * @param io_service The io_service object that the handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + * + * @param native_handle A native handle. + * + * @throws asio::system_error Thrown on failure. + */ + basic_handle(asio::io_service& io_service, + const native_type& native_handle) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.assign(this->implementation, native_handle, ec); + asio::detail::throw_error(ec); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_handle cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Assign an existing native handle to the handle. + /* + * This function opens the handle to hold an existing native handle. + * + * @param native_handle A native handle. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const native_type& native_handle) + { + asio::error_code ec; + this->service.assign(this->implementation, native_handle, ec); + asio::detail::throw_error(ec); + } + + /// Assign an existing native handle to the handle. + /* + * This function opens the handle to hold an existing native handle. + * + * @param native_handle A native handle. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code assign(const native_type& native_handle, + asio::error_code& ec) + { + return this->service.assign(this->implementation, native_handle, ec); + } + + /// Determine whether the handle is open. + bool is_open() const + { + return this->service.is_open(this->implementation); + } + + /// Close the handle. + /** + * This function is used to close the handle. Any asynchronous read or write + * operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + this->service.close(this->implementation, ec); + asio::detail::throw_error(ec); + } + + /// Close the handle. + /** + * This function is used to close the handle. Any asynchronous read or write + * operations will be cancelled immediately, and will complete with the + * asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code close(asio::error_code& ec) + { + return this->service.close(this->implementation, ec); + } + + /// Get the native handle representation. + /** + * This function may be used to obtain the underlying representation of the + * handle. This is intended to allow access to native handle functionality + * that is not otherwise provided. + */ + native_type native() + { + return this->service.native(this->implementation); + } + + /// Cancel all asynchronous operations associated with the handle. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->service.cancel(this->implementation, ec); + asio::detail::throw_error(ec); + } + + /// Cancel all asynchronous operations associated with the handle. + /** + * This function causes all outstanding asynchronous read or write operations + * to finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code cancel(asio::error_code& ec) + { + return this->service.cancel(this->implementation, ec); + } + +protected: + /// Protected destructor to prevent deletion through this type. + ~basic_handle() + { + } +}; + +} // namespace windows +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WINDOWS_BASIC_HANDLE_HPP diff --git a/libtorrent/include/asio/windows/basic_random_access_handle.hpp b/libtorrent/include/asio/windows/basic_random_access_handle.hpp new file mode 100644 index 000000000..3f015619e --- /dev/null +++ b/libtorrent/include/asio/windows/basic_random_access_handle.hpp @@ -0,0 +1,320 @@ +// +// basic_random_access_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP +#define ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_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 +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/windows/basic_handle.hpp" +#include "asio/windows/random_access_handle_service.hpp" +#include "asio/detail/throw_error.hpp" + +#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace windows { + +/// Provides random-access handle functionality. +/** + * The windows::basic_random_access_handle class template provides asynchronous + * and blocking random-access handle functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_random_access_handle + : public basic_handle +{ +public: + /// The native representation of a handle. + typedef typename RandomAccessHandleService::native_type native_type; + + /// Construct a basic_random_access_handle without opening it. + /** + * This constructor creates a random-access handle without opening it. The + * handle needs to be opened before data can be written to or or read from it. + * + * @param io_service The io_service object that the random-access handle will + * use to dispatch handlers for any asynchronous operations performed on the + * handle. + */ + explicit basic_random_access_handle(asio::io_service& io_service) + : basic_handle(io_service) + { + } + + /// Construct a basic_random_access_handle on an existing native handle. + /** + * This constructor creates a random-access handle object to hold an existing + * native handle. + * + * @param io_service The io_service object that the random-access handle will + * use to dispatch handlers for any asynchronous operations performed on the + * handle. + * + * @param native_handle The new underlying handle implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_random_access_handle(asio::io_service& io_service, + const native_type& native_handle) + : basic_handle(io_service, native_handle) + { + } + + /// Write some data to the handle at the specified offset. + /** + * This function is used to write data to the random-access handle. The + * function call will block until one or more bytes of the data has been + * written successfully, or until an error occurs. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more data buffers to be written to the handle. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some_at operation may not write all of the data. Consider + * using the @ref write_at function if you need to ensure that all data is + * written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * handle.write_some_at(42, asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some_at(boost::uint64_t offset, + const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.write_some_at( + this->implementation, offset, buffers, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Write some data to the handle at the specified offset. + /** + * This function is used to write data to the random-access handle. The + * function call will block until one or more bytes of the data has been + * written successfully, or until an error occurs. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more data buffers to be written to the handle. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write_at function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some_at(boost::uint64_t offset, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return this->service.write_some_at( + this->implementation, offset, buffers, ec); + } + + /// Start an asynchronous write at the specified offset. + /** + * This function is used to asynchronously write data to the random-access + * handle. The function call always returns immediately. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more data buffers to be written to the handle. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write_at function if you need to ensure that + * all data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * handle.async_write_some_at(42, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_write_some_at(boost::uint64_t offset, + const ConstBufferSequence& buffers, WriteHandler handler) + { + this->service.async_write_some_at( + this->implementation, offset, buffers, handler); + } + + /// Read some data from the handle at the specified offset. + /** + * This function is used to read data from the random-access handle. The + * function call will block until one or more bytes of data has been read + * successfully, or until an error occurs. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read_at function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * handle.read_some_at(42, asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some_at(boost::uint64_t offset, + const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.read_some_at( + this->implementation, offset, buffers, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Read some data from the handle at the specified offset. + /** + * This function is used to read data from the random-access handle. The + * function call will block until one or more bytes of data has been read + * successfully, or until an error occurs. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read_at function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some_at(boost::uint64_t offset, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return this->service.read_some_at( + this->implementation, offset, buffers, ec); + } + + /// Start an asynchronous read at the specified offset. + /** + * This function is used to asynchronously read data from the random-access + * handle. The function call always returns immediately. + * + * @param offset The offset at which the data will be read. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read_at function if you need to ensure that + * the requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * handle.async_read_some_at(42, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_read_some_at(boost::uint64_t offset, + const MutableBufferSequence& buffers, ReadHandler handler) + { + this->service.async_read_some_at( + this->implementation, offset, buffers, handler); + } +}; + +} // namespace windows +} // namespace asio + +#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP diff --git a/libtorrent/include/asio/windows/basic_stream_handle.hpp b/libtorrent/include/asio/windows/basic_stream_handle.hpp new file mode 100644 index 000000000..aca40aff1 --- /dev/null +++ b/libtorrent/include/asio/windows/basic_stream_handle.hpp @@ -0,0 +1,302 @@ +// +// basic_stream_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_WINDOWS_BASIC_STREAM_HANDLE_HPP +#define ASIO_WINDOWS_BASIC_STREAM_HANDLE_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 +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/windows/basic_handle.hpp" +#include "asio/windows/stream_handle_service.hpp" +#include "asio/detail/throw_error.hpp" + +#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace windows { + +/// Provides stream-oriented handle functionality. +/** + * The windows::basic_stream_handle class template provides asynchronous and + * blocking stream-oriented handle functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class basic_stream_handle + : public basic_handle +{ +public: + /// The native representation of a handle. + typedef typename StreamHandleService::native_type native_type; + + /// Construct a basic_stream_handle without opening it. + /** + * This constructor creates a stream handle without opening it. The handle + * needs to be opened and then connected or accepted before data can be sent + * or received on it. + * + * @param io_service The io_service object that the stream handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + */ + explicit basic_stream_handle(asio::io_service& io_service) + : basic_handle(io_service) + { + } + + /// Construct a basic_stream_handle on an existing native handle. + /** + * This constructor creates a stream handle object to hold an existing native + * handle. + * + * @param io_service The io_service object that the stream handle will use to + * dispatch handlers for any asynchronous operations performed on the handle. + * + * @param native_handle The new underlying handle implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_handle(asio::io_service& io_service, + const native_type& native_handle) + : basic_handle(io_service, native_handle) + { + } + + /// Write some data to the handle. + /** + * This function is used to write data to the stream handle. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the handle. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * handle.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.write_some(this->implementation, buffers, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Write some data to the handle. + /** + * This function is used to write data to the stream handle. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the handle. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return this->service.write_some(this->implementation, buffers, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the stream handle. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the handle. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * handle.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_write_some(const ConstBufferSequence& buffers, + WriteHandler handler) + { + this->service.async_write_some(this->implementation, buffers, handler); + } + + /// Read some data from the handle. + /** + * This function is used to read data from the stream handle. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * handle.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.read_some(this->implementation, buffers, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Read some data from the handle. + /** + * This function is used to read data from the stream handle. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return this->service.read_some(this->implementation, buffers, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the stream handle. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * handle.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_read_some(const MutableBufferSequence& buffers, + ReadHandler handler) + { + this->service.async_read_some(this->implementation, buffers, handler); + } +}; + +} // namespace windows +} // namespace asio + +#endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WINDOWS_BASIC_STREAM_HANDLE_HPP diff --git a/libtorrent/include/asio/windows/random_access_handle.hpp b/libtorrent/include/asio/windows/random_access_handle.hpp new file mode 100644 index 000000000..7bc8db85a --- /dev/null +++ b/libtorrent/include/asio/windows/random_access_handle.hpp @@ -0,0 +1,39 @@ +// +// random_access_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_WINDOWS_RANDOM_ACCESS_HANDLE_HPP +#define ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_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/windows/basic_random_access_handle.hpp" + +#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace windows { + +/// Typedef for the typical usage of a random-access handle. +typedef basic_random_access_handle<> random_access_handle; + +} // namespace windows +} // namespace asio + +#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_HPP diff --git a/libtorrent/include/asio/windows/random_access_handle_service.hpp b/libtorrent/include/asio/windows/random_access_handle_service.hpp new file mode 100644 index 000000000..3430deb32 --- /dev/null +++ b/libtorrent/include/asio/windows/random_access_handle_service.hpp @@ -0,0 +1,179 @@ +// +// random_access_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_WINDOWS_RANDOM_ACCESS_HANDLE_SERVICE_HPP +#define ASIO_WINDOWS_RANDOM_ACCESS_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/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/win_iocp_handle_service.hpp" + +#if !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE) +# if defined(ASIO_HAS_IOCP) +# define ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE 1 +# endif // defined(ASIO_HAS_IOCP) +#endif // !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE) + +#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace windows { + +/// Default service implementation for a random-access handle. +class random_access_handle_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_service::service +#else + : public asio::detail::service_base +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_service::id id; +#endif + +private: + // The type of the platform-specific implementation. + typedef detail::win_iocp_handle_service service_impl_type; + +public: + /// The type of a random-access handle implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef service_impl_type::implementation_type implementation_type; +#endif + + /// The native handle type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef service_impl_type::native_type native_type; +#endif + + /// Construct a new random-access handle service for the specified io_service. + explicit random_access_handle_service(asio::io_service& io_service) + : asio::detail::service_base< + random_access_handle_service>(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new random-access handle implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a random-access handle implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Assign an existing native handle to a random-access handle. + asio::error_code assign(implementation_type& impl, + const native_type& native_handle, asio::error_code& ec) + { + return service_impl_.assign(impl, native_handle, ec); + } + + /// Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a random-access handle implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.close(impl, ec); + } + + /// Get the native handle implementation. + native_type native(implementation_type& impl) + { + return service_impl_.native(impl); + } + + /// Cancel all asynchronous operations associated with the handle. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.cancel(impl, ec); + } + + /// Write the given data at the specified offset. + template + std::size_t write_some_at(implementation_type& impl, boost::uint64_t offset, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.write_some_at(impl, offset, buffers, ec); + } + + /// Start an asynchronous write at the specified offset. + template + void async_write_some_at(implementation_type& impl, boost::uint64_t offset, + const ConstBufferSequence& buffers, WriteHandler handler) + { + service_impl_.async_write_some_at(impl, offset, buffers, handler); + } + + /// Read some data from the specified offset. + template + std::size_t read_some_at(implementation_type& impl, boost::uint64_t offset, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.read_some_at(impl, offset, buffers, ec); + } + + /// Start an asynchronous read at the specified offset. + template + void async_read_some_at(implementation_type& impl, boost::uint64_t offset, + const MutableBufferSequence& buffers, ReadHandler handler) + { + service_impl_.async_read_some_at(impl, offset, buffers, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace windows +} // namespace asio + +#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_SERVICE_HPP diff --git a/libtorrent/include/asio/windows/stream_handle.hpp b/libtorrent/include/asio/windows/stream_handle.hpp new file mode 100644 index 000000000..84b216d9f --- /dev/null +++ b/libtorrent/include/asio/windows/stream_handle.hpp @@ -0,0 +1,39 @@ +// +// stream_handle.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_WINDOWS_STREAM_HANDLE_HPP +#define ASIO_WINDOWS_STREAM_HANDLE_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/windows/basic_stream_handle.hpp" + +#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace windows { + +/// Typedef for the typical usage of a stream-oriented handle. +typedef basic_stream_handle<> stream_handle; + +} // namespace windows +} // namespace asio + +#endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WINDOWS_STREAM_HANDLE_HPP diff --git a/libtorrent/include/asio/windows/stream_handle_service.hpp b/libtorrent/include/asio/windows/stream_handle_service.hpp new file mode 100644 index 000000000..26bd529c4 --- /dev/null +++ b/libtorrent/include/asio/windows/stream_handle_service.hpp @@ -0,0 +1,177 @@ +// +// stream_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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_WINDOWS_STREAM_HANDLE_SERVICE_HPP +#define ASIO_WINDOWS_STREAM_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/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/win_iocp_handle_service.hpp" + +#if !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE) +# if defined(ASIO_HAS_IOCP) +# define ASIO_HAS_WINDOWS_STREAM_HANDLE 1 +# endif // defined(ASIO_HAS_IOCP) +#endif // !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE) + +#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) + +namespace asio { +namespace windows { + +/// Default service implementation for a stream handle. +class stream_handle_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_service::service +#else + : public asio::detail::service_base +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_service::id id; +#endif + +private: + // The type of the platform-specific implementation. + typedef detail::win_iocp_handle_service service_impl_type; + +public: + /// The type of a stream handle implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef service_impl_type::implementation_type implementation_type; +#endif + + /// The native handle type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef service_impl_type::native_type native_type; +#endif + + /// Construct a new stream handle service for the specified io_service. + explicit stream_handle_service(asio::io_service& io_service) + : asio::detail::service_base(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new stream handle implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a stream handle implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Assign an existing native handle to a stream handle. + asio::error_code assign(implementation_type& impl, + const native_type& native_handle, asio::error_code& ec) + { + return service_impl_.assign(impl, native_handle, ec); + } + + /// Determine whether the handle is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a stream handle implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.close(impl, ec); + } + + /// Get the native handle implementation. + native_type native(implementation_type& impl) + { + return service_impl_.native(impl); + } + + /// Cancel all asynchronous operations associated with the handle. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.cancel(impl, ec); + } + + /// Write the given data to the stream. + template + std::size_t write_some(implementation_type& impl, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.write_some(impl, buffers, ec); + } + + /// Start an asynchronous write. + template + void async_write_some(implementation_type& impl, + const ConstBufferSequence& buffers, WriteHandler handler) + { + service_impl_.async_write_some(impl, buffers, handler); + } + + /// Read some data from the stream. + template + std::size_t read_some(implementation_type& impl, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.read_some(impl, buffers, ec); + } + + /// Start an asynchronous read. + template + void async_read_some(implementation_type& impl, + const MutableBufferSequence& buffers, ReadHandler handler) + { + service_impl_.async_read_some(impl, buffers, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace windows +} // namespace asio + +#endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WINDOWS_STREAM_HANDLE_SERVICE_HPP diff --git a/libtorrent/include/asio/write.hpp b/libtorrent/include/asio/write.hpp index 1c72c1cd5..b1965880b 100644 --- a/libtorrent/include/asio/write.hpp +++ b/libtorrent/include/asio/write.hpp @@ -29,6 +29,8 @@ namespace asio { /** * @defgroup write asio::write + * + * @brief Write a certain amount of data to a stream before returning. */ /*@{*/ @@ -166,7 +168,7 @@ template & b, /*@}*/ /** * @defgroup async_write asio::async_write + * + * @brief Start an asynchronous operation to write a certain amount of data to a + * stream. */ /*@{*/ -/// Start an asynchronous operation to write of all of the supplied data to a +/// Start an asynchronous operation to write all of the supplied data to a /// stream. /** * This function is used to asynchronously write a certain number of bytes of @@ -405,7 +410,7 @@ template = 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_streambuf.hpp" +#include "asio/error.hpp" + +namespace asio { + +/** + * @defgroup write_at asio::write_at + * + * @brief Write a certain amount of data at a specified offset before returning. + */ +/*@{*/ + +/// Write all of the supplied data at the specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * device. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::write_at(d, 42, asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::write_at( + * d, offset, buffers, + * asio::transfer_all()); @endcode + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers); + +/// Write a certain amount of data at a specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * device. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * // Result of latest write_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the device's write_some_at function are + * required. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::write_at(d, 42, asio::buffer(data, size), + * asio::transfer_at_least(32)); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition); + +/// Write a certain amount of data at a specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * device. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * // Result of latest write_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the device's write_some_at function are + * required. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec); + +/// Write all of the supplied data at the specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param b The basic_streambuf object from which data will be written. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::write_at( + * d, 42, b, + * asio::transfer_all()); @endcode + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, basic_streambuf& b); + +/// Write a certain amount of data at a specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param b The basic_streambuf object from which data will be written. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * // Result of latest write_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the device's write_some_at function are + * required. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, + basic_streambuf& b, CompletionCondition completion_condition); + +/// Write a certain amount of data at a specified offset before returning. +/** + * This function is used to write a certain number of bytes of data to a random + * access device at a specified offset. The call will block until one of the + * following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the device's + * write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the SyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param b The basic_streambuf object from which data will be written. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * // Result of latest write_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the device's write_some_at function are + * required. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::uint64_t offset, + basic_streambuf& b, CompletionCondition completion_condition, + asio::error_code& ec); + +/*@}*/ +/** + * @defgroup async_write_at asio::async_write_at + * + * @brief Start an asynchronous operation to write a certain amount of data at + * the specified offset. + */ +/*@{*/ + +/// Start an asynchronous operation to write all of the supplied data at the +/// specified offset. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a random access device at a specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the device's + * async_write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the AsyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more buffers containing the data to be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes written from the buffers. If an error + * // occurred, this will be less than the sum of the buffer sizes. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * asio::async_write_at(d, 42, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, + const ConstBufferSequence& buffers, WriteHandler handler); + +/// Start an asynchronous operation to write a certain amount of data at the +/// specified offset. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a random access device at a specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the device's + * async_write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the AsyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param buffers One or more buffers containing the data to be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * // Result of latest write_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the device's async_write_some_at function are + * required. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes written from the buffers. If an error + * // occurred, this will be less than the sum of the buffer sizes. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::async_write_at(d, 42, + * asio::buffer(data, size), + * asio::transfer_at_least(32), + * handler); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +void async_write_at(AsyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, WriteHandler handler); + +/// Start an asynchronous operation to write all of the supplied data at the +/// specified offset. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a random access device at a specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the device's + * async_write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the AsyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param b A basic_streambuf object from which data will be written. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes written from the buffers. If an error + * // occurred, this will be less than the sum of the buffer sizes. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ +template +void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, + basic_streambuf& b, WriteHandler handler); + +/// Start an asynchronous operation to write a certain amount of data at the +/// specified offset. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a random access device at a specified offset. The function call + * always returns immediately. The asynchronous operation will continue until + * one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the device's + * async_write_some_at function. + * + * @param d The device to which the data is to be written. The type must support + * the AsyncRandomAccessWriteDevice concept. + * + * @param offset The offset at which the data will be written. + * + * @param b A basic_streambuf object from which data will be written. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * // Result of latest async_write_some_at operation. + * const asio::error_code& error, + * + * // Number of bytes transferred so far. + * std::size_t bytes_transferred + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the device's async_write_some_at function are + * required. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * // Result of operation. + * const asio::error_code& error, + * + * // Number of bytes written from the buffers. If an error + * // occurred, this will be less than the sum of the buffer sizes. + * std::size_t bytes_transferred + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ +template +void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, + basic_streambuf& b, CompletionCondition completion_condition, + WriteHandler handler); + +/*@}*/ + +} // namespace asio + +#include "asio/impl/write_at.ipp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WRITE_AT_HPP