diff --git a/example/ssl/README b/example/ssl/README index 85a4a4da..12cf9ea2 100644 --- a/example/ssl/README +++ b/example/ssl/README @@ -1 +1,8 @@ The passphrase for both the CA and server private keys is "test". + + +------------------------------------------------------------------------------- +Copyright (c) 2003-2006 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) diff --git a/example/ssl/server.cpp b/example/ssl/server.cpp index 16bc3588..22102ddf 100644 --- a/example/ssl/server.cpp +++ b/example/ssl/server.cpp @@ -100,6 +100,7 @@ public: boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use); + context_.set_password_callback(boost::bind(&server::get_password, this)); context_.use_certificate_chain_file("server.pem"); context_.use_private_key_file("server.pem", boost::asio::ssl::context::pem); context_.use_tmp_dh_file("dh512.pem"); @@ -110,6 +111,11 @@ public: boost::asio::placeholders::error)); } + std::string get_password() const + { + return "test"; + } + void handle_accept(session* new_session, const boost::asio::error& error) { if (!error) diff --git a/example/timers/tick_count_timer.cpp b/example/timers/tick_count_timer.cpp index 2c561946..180f2a33 100644 --- a/example/timers/tick_count_timer.cpp +++ b/example/timers/tick_count_timer.cpp @@ -1,3 +1,13 @@ +// +// tick_count_timer.cpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 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) +// + #include #include #include diff --git a/example/timers/time_t_timer.cpp b/example/timers/time_t_timer.cpp index 9fba0934..5ca32093 100644 --- a/example/timers/time_t_timer.cpp +++ b/example/timers/time_t_timer.cpp @@ -1,3 +1,13 @@ +// +// time_t_timer.cpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 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) +// + #include #include #include diff --git a/include/boost/asio.hpp b/include/boost/asio.hpp index de84ebd1..b530490a 100644 --- a/include/boost/asio.hpp +++ b/include/boost/asio.hpp @@ -18,11 +18,9 @@ #include #include #include -#include #include #include -#include -#include +#include #include #include #include @@ -39,18 +37,20 @@ #include #include #include -#include +#include #include #include #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -58,11 +58,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include diff --git a/include/boost/asio/basic_io_object.hpp b/include/boost/asio/basic_io_object.hpp index 01daec19..9bfb19a0 100644 --- a/include/boost/asio/basic_io_object.hpp +++ b/include/boost/asio/basic_io_object.hpp @@ -35,13 +35,6 @@ public: /// The underlying implementation type of I/O object. typedef typename service_type::implementation_type implementation_type; - /// Construct a basic_io_object. - explicit basic_io_object(boost::asio::io_service& io_service) - : service(boost::asio::use_service(io_service)) - { - service.construct(implementation); - } - /// Get the io_service associated with the object. /** * This function may be used to obtain the io_service object that the I/O @@ -52,10 +45,17 @@ public: */ boost::asio::io_service& io_service() { - return service.owner(); + return service.io_service(); } protected: + /// Construct a basic_io_object. + explicit basic_io_object(boost::asio::io_service& io_service) + : service(boost::asio::use_service(io_service)) + { + service.construct(implementation); + } + /// Protected destructor to prevent deletion through this type. ~basic_io_object() { diff --git a/include/boost/asio/basic_socket.hpp b/include/boost/asio/basic_socket.hpp index a369c504..c6c19767 100644 --- a/include/boost/asio/basic_socket.hpp +++ b/include/boost/asio/basic_socket.hpp @@ -290,6 +290,38 @@ public: return this->service.native(this->implementation); } + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. + * + * @throws boost::asio::error Thrown on failure. + */ + void cancel() + { + this->service.cancel(this->implementation, throw_error()); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void cancel(Error_Handler error_handler) + { + this->service.cancel(this->implementation, error_handler); + } + /// Bind the socket to the given local endpoint. /** * This function binds the socket to the specified endpoint on the local diff --git a/include/boost/asio/basic_socket_acceptor.hpp b/include/boost/asio/basic_socket_acceptor.hpp index 8eb60a01..b2ecf6d3 100644 --- a/include/boost/asio/basic_socket_acceptor.hpp +++ b/include/boost/asio/basic_socket_acceptor.hpp @@ -115,8 +115,8 @@ public: * @param endpoint An endpoint on the local machine on which the acceptor * will listen for new connections. * - * @param listen_backlog The maximum length of the queue of pending - * connections. A value of 0 means use the default queue length. + * @param reuse_addr Whether the constructor should set the socket option + * socket_base::reuse_address. * * @throws boost::asio::error Thrown on failure. * @@ -124,18 +124,26 @@ public: * @code * basic_socket_acceptor acceptor(io_service); * acceptor.open(endpoint.protocol()); + * if (reuse_addr) + * acceptor.set_option(socket_base::reuse_address(true)); * acceptor.bind(endpoint); * acceptor.listen(listen_backlog); * @endcode */ basic_socket_acceptor(boost::asio::io_service& io_service, - const endpoint_type& endpoint, int listen_backlog = 0) + const endpoint_type& endpoint, bool reuse_addr = true) : basic_io_object(io_service) { this->service.open(this->implementation, endpoint.protocol(), throw_error()); + if (reuse_addr) + { + this->service.set_option(this->implementation, + socket_base::reuse_address(true), throw_error()); + } this->service.bind(this->implementation, endpoint, throw_error()); - this->service.listen(this->implementation, listen_backlog, throw_error()); + this->service.listen(this->implementation, + socket_base::max_connections, throw_error()); } /// Construct a basic_socket_acceptor on an existing native acceptor. @@ -312,10 +320,9 @@ public: * This function puts the socket acceptor into the state where it may accept * new connections. * - * @param backlog The maximum length of the queue of pending connections. A - * value of 0 means use the default queue length. + * @param backlog The maximum length of the queue of pending connections. */ - void listen(int backlog = 0) + void listen(int backlog = socket_base::max_connections) { this->service.listen(this->implementation, backlog, throw_error()); } @@ -326,8 +333,7 @@ public: * This function puts the socket acceptor into the state where it may accept * new connections. * - * @param backlog The maximum length of the queue of pending connections. A - * value of 0 means use the default queue length. + * @param backlog The maximum length of the queue of pending connections. * * @param error_handler A handler to be called when the operation completes, * to indicate whether or not an error has occurred. Copies will be made of @@ -341,7 +347,8 @@ public: * boost::asio::ip::tcp::acceptor acceptor(io_service); * ... * boost::asio::error error; - * acceptor.listen(0, boost::asio::assign_error(error)); + * acceptor.listen(boost::asio::socket_base::max_connections, + * boost::asio::assign_error(error)); * if (error) * { * // An error occurred. @@ -413,6 +420,38 @@ public: return this->service.native(this->implementation); } + /// Cancel all asynchronous operations associated with the acceptor. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. + * + * @throws boost::asio::error Thrown on failure. + */ + void cancel() + { + this->service.cancel(this->implementation, throw_error()); + } + + /// Cancel all asynchronous operations associated with the acceptor. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void cancel(Error_Handler error_handler) + { + this->service.cancel(this->implementation, error_handler); + } + /// Set an option on the acceptor. /** * This function is used to set an option on the acceptor. diff --git a/include/boost/asio/basic_socket_iostream.hpp b/include/boost/asio/basic_socket_iostream.hpp index 148da19f..ae735b98 100644 --- a/include/boost/asio/basic_socket_iostream.hpp +++ b/include/boost/asio/basic_socket_iostream.hpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include #if !defined(BOOST_ASIO_SOCKET_IOSTREAM_MAX_ARITY) @@ -36,7 +36,7 @@ // template < typename T1, ..., typename Tn > // explicit basic_socket_iostream( T1 x1, ..., Tn xn ) // : basic_iostream(&this->boost::base_from_member< -// basic_socketbuf >::member) +// basic_socket_streambuf >::member) // { // try // { @@ -55,7 +55,7 @@ template < BOOST_PP_ENUM_PARAMS(n, typename T) > \ explicit basic_socket_iostream( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \ : std::basic_iostream(&this->boost::base_from_member< \ - basic_socketbuf >::member) \ + basic_socket_streambuf >::member) \ { \ try \ { \ @@ -111,14 +111,14 @@ namespace asio { template > class basic_socket_iostream - : public boost::base_from_member >, + : public boost::base_from_member >, public std::basic_iostream { public: /// Construct a basic_socket_iostream without establishing a connection. basic_socket_iostream() : std::basic_iostream(&this->boost::base_from_member< - basic_socketbuf >::member) + basic_socket_streambuf >::member) { } @@ -159,11 +159,11 @@ public: } /// Return a pointer to the underlying streambuf. - basic_socketbuf* rdbuf() const + basic_socket_streambuf* rdbuf() const { - return const_cast*>( + return const_cast*>( &this->boost::base_from_member< - basic_socketbuf >::member); + basic_socket_streambuf >::member); } }; diff --git a/include/boost/asio/basic_socketbuf.hpp b/include/boost/asio/basic_socket_streambuf.hpp similarity index 89% rename from include/boost/asio/basic_socketbuf.hpp rename to include/boost/asio/basic_socket_streambuf.hpp index c5ea8c65..4179fe79 100644 --- a/include/boost/asio/basic_socketbuf.hpp +++ b/include/boost/asio/basic_socket_streambuf.hpp @@ -1,6 +1,6 @@ // -// basic_socketbuf.hpp -// ~~~~~~~~~~~~~~~~~~~ +// basic_socket_streambuf.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) // @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_ASIO_BASIC_SOCKETBUF_HPP -#define BOOST_ASIO_BASIC_SOCKETBUF_HPP +#ifndef BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP +#define BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once @@ -32,13 +32,13 @@ #include #include -#if !defined(BOOST_ASIO_SOCKETBUF_MAX_ARITY) -#define BOOST_ASIO_SOCKETBUF_MAX_ARITY 5 -#endif // !defined(BOOST_ASIO_SOCKETBUF_MAX_ARITY) +#if !defined(BOOST_ASIO_SOCKET_STREAMBUF_MAX_ARITY) +#define BOOST_ASIO_SOCKET_STREAMBUF_MAX_ARITY 5 +#endif // !defined(BOOST_ASIO_SOCKET_STREAMBUF_MAX_ARITY) // A macro that should expand to: // template < typename T1, ..., typename Tn > -// explicit basic_socketbuf( T1 x1, ..., Tn xn ) +// explicit basic_socket_streambuf( T1 x1, ..., Tn xn ) // : basic_socket( // boost::base_from_member::member) // { @@ -51,7 +51,7 @@ #define BOOST_ASIO_PRIVATE_CTR_DEF( z, n, data ) \ template < BOOST_PP_ENUM_PARAMS(n, typename T) > \ - explicit basic_socketbuf( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \ + explicit basic_socket_streambuf( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \ : basic_socket( \ boost::base_from_member::member) \ { \ @@ -92,7 +92,7 @@ namespace asio { /// Iostream streambuf for a socket. template > -class basic_socketbuf +class basic_socket_streambuf : public std::streambuf, private boost::base_from_member, public basic_socket @@ -101,8 +101,8 @@ public: /// The endpoint type. typedef typename Protocol::endpoint endpoint_type; - /// Construct a basic_socketbuf without establishing a connection. - basic_socketbuf() + /// Construct a basic_socket_streambuf without establishing a connection. + basic_socket_streambuf() : basic_socket( boost::base_from_member::member) { @@ -110,7 +110,7 @@ public: } /// Establish a connection to the specified endpoint. - explicit basic_socketbuf(const endpoint_type& endpoint) + explicit basic_socket_streambuf(const endpoint_type& endpoint) : basic_socket( boost::base_from_member::member) { @@ -126,15 +126,15 @@ public: * a resolver query object. */ template - explicit basic_socketbuf(T1 t1, ..., TN tn); + explicit basic_socket_streambuf(T1 t1, ..., TN tn); #else BOOST_PP_REPEAT_FROM_TO( - 1, BOOST_PP_INC(BOOST_ASIO_SOCKETBUF_MAX_ARITY), + 1, BOOST_PP_INC(BOOST_ASIO_SOCKET_STREAMBUF_MAX_ARITY), BOOST_ASIO_PRIVATE_CTR_DEF, _ ) #endif /// Destructor flushes buffered data. - ~basic_socketbuf() + ~basic_socket_streambuf() { sync(); } @@ -158,7 +158,7 @@ public: void connect(T1 t1, ..., TN tn); #else BOOST_PP_REPEAT_FROM_TO( - 1, BOOST_PP_INC(BOOST_ASIO_SOCKETBUF_MAX_ARITY), + 1, BOOST_PP_INC(BOOST_ASIO_SOCKET_STREAMBUF_MAX_ARITY), BOOST_ASIO_PRIVATE_CONNECT_DEF, _ ) #endif @@ -279,4 +279,4 @@ private: #include -#endif // BOOST_ASIO_BASIC_SOCKETBUF_HPP +#endif // BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP diff --git a/include/boost/asio/basic_strand.hpp b/include/boost/asio/basic_strand.hpp deleted file mode 100644 index 99d8934a..00000000 --- a/include/boost/asio/basic_strand.hpp +++ /dev/null @@ -1,146 +0,0 @@ -// -// basic_strand.hpp -// ~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_ASIO_BASIC_STRAND_HPP -#define BOOST_ASIO_BASIC_STRAND_HPP - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -#include - -#include -#include -#include - -namespace boost { -namespace asio { - -/// Provides serialised handler execution. -/** - * The basic_strand class template provides the ability to post and dispatch - * handlers with the guarantee that none of those handlers will execute - * concurrently. - * - * Most applications will use the boost::asio::strand typedef. - * - * @par Thread Safety: - * @e Distinct @e objects: Safe.@n - * @e Shared @e objects: Safe. - * - * @par Concepts: - * Dispatcher. - */ -template -class basic_strand - : public basic_io_object -{ -public: - /// Constructor. - /** - * Constructs the strand. - * - * @param io_service The io_service object that the strand will use to - * dispatch handlers that are ready to be run. - */ - explicit basic_strand(boost::asio::io_service& io_service) - : basic_io_object(io_service) - { - } - - /// Request the strand to invoke the given handler. - /** - * This function is used to ask the strand to execute the given handler. - * - * The strand object guarantees that handlers posted or dispatched through - * the strand will not be executed concurrently. The handler may be executed - * inside this function if the guarantee can be met. If this function is - * called from within a handler that was posted or dispatched through the same - * strand, then the new handler will be executed immediately. - * - * The strand's guarantee is in addition to the guarantee provided by the - * underlying io_service. The io_service guarantees that the handler will only - * be called in a thread in which the io_service's run member function is - * currently being invoked. - * - * @param handler The handler to be called. The strand will make a copy of the - * handler object as required. The function signature of the handler must be: - * @code void handler(); @endcode - */ - template - void dispatch(Handler handler) - { - this->service.dispatch(this->implementation, handler); - } - - /// Request the strand to invoke the given handler and return - /// immediately. - /** - * This function is used to ask the strand to execute the given handler, but - * without allowing the strand to call the handler from inside this function. - * - * The strand object guarantees that handlers posted or dispatched through - * the strand will not be executed concurrently. The strand's guarantee is in - * addition to the guarantee provided by the underlying io_service. The - * io_service guarantees that the handler will only be called in a thread in - * which the io_service's run member function is currently being invoked. - * - * @param handler The handler to be called. The strand will make a copy of the - * handler object as required. The function signature of the handler must be: - * @code void handler(); @endcode - */ - template - void post(Handler handler) - { - this->service.post(this->implementation, handler); - } - - /// Create a new handler that automatically dispatches the wrapped handler - /// on the strand. - /** - * This function is used to create a new handler function object that, when - * invoked, will automatically pass the wrapped handler to the strand's - * dispatch function. - * - * @param handler The handler to be wrapped. The strand will make a copy of - * the handler object as required. The function signature of the handler must - * be: @code void handler(A1 a1, ... An an); @endcode - * - * @return A function object that, when invoked, passes the wrapped handler to - * the strand's dispatch function. Given a function object with the signature: - * @code R f(A1 a1, ... An an); @endcode - * If this function object is passed to the wrap function like so: - * @code strand.wrap(f); @endcode - * then the return value is a function object with the signature - * @code void g(A1 a1, ... An an); @endcode - * that, when invoked, executes code equivalent to: - * @code strand.dispatch(boost::bind(f, a1, ... an)); @endcode - */ - template -#if defined(GENERATING_DOCUMENTATION) - unspecified -#else - detail::wrapped_handler, Handler> -#endif - wrap(Handler handler) - { - return detail::wrapped_handler< - basic_strand, - Handler>(*this, handler); - } -}; - -} // namespace asio -} // namespace boost - -#include - -#endif // BOOST_ASIO_BASIC_STRAND_HPP diff --git a/include/boost/asio/basic_streambuf.hpp b/include/boost/asio/basic_streambuf.hpp index 8a08e9cc..f9079dd6 100644 --- a/include/boost/asio/basic_streambuf.hpp +++ b/include/boost/asio/basic_streambuf.hpp @@ -69,6 +69,12 @@ public: return pptr() - gptr(); } + /// Return the maximum size of the buffer. + std::size_t max_size() const + { + return max_size_; + } + /// Get a list of buffers that represents the get area. const_buffers_type data() const { @@ -89,7 +95,7 @@ public: { if (pptr() + n > epptr()) n = epptr() - pptr(); - pbump(n); + pbump(static_cast(n)); } /// Move the start of the get area by the specified number of characters. diff --git a/include/boost/asio/buffer.hpp b/include/boost/asio/buffer.hpp index 0d8edcb3..f3652e48 100644 --- a/include/boost/asio/buffer.hpp +++ b/include/boost/asio/buffer.hpp @@ -26,6 +26,20 @@ #include #include +#if defined(BOOST_MSVC) +# if defined(_HAS_ITERATOR_DEBUGGING) +# if !defined(BOOST_ASIO_DISABLE_BUFFER_DEBUGGING) +# define BOOST_ASIO_ENABLE_BUFFER_DEBUGGING +# endif // !defined(BOOST_ASIO_DISABLE_BUFFER_DEBUGGING) +# endif // defined(_HAS_ITERATOR_DEBUGGING) +#endif // defined(BOOST_MSVC) + +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) +# include +# include +# include +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + namespace boost { namespace asio { @@ -62,6 +76,21 @@ public: { } +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + mutable_buffer(void* data, std::size_t size, + boost::function debug_check) + : data_(data), + size_(size), + debug_check_(debug_check) + { + } + + const boost::function& get_debug_check() const + { + return debug_check_; + } +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + private: friend void* boost::asio::detail::buffer_cast_helper( const mutable_buffer& b); @@ -70,12 +99,20 @@ private: void* data_; std::size_t size_; + +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + boost::function debug_check_; +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING }; namespace detail { inline void* buffer_cast_helper(const mutable_buffer& b) { +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + if (b.debug_check_) + b.debug_check_(); +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING return b.data_; } @@ -115,7 +152,11 @@ inline mutable_buffer operator+(const mutable_buffer& b, std::size_t start) return mutable_buffer(); char* new_data = buffer_cast(b) + start; std::size_t new_size = buffer_size(b) - start; - return mutable_buffer(new_data, new_size); + return mutable_buffer(new_data, new_size +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + ); } /// Create a new modifiable buffer that is offset from the start of another. @@ -128,7 +169,11 @@ inline mutable_buffer operator+(std::size_t start, const mutable_buffer& b) return mutable_buffer(); char* new_data = buffer_cast(b) + start; std::size_t new_size = buffer_size(b) - start; - return mutable_buffer(new_data, new_size); + return mutable_buffer(new_data, new_size +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + ); } /// Adapts a single modifiable buffer so that it meets the requirements of the @@ -189,9 +234,27 @@ public: const_buffer(const mutable_buffer& b) : data_(boost::asio::detail::buffer_cast_helper(b)), size_(boost::asio::detail::buffer_size_helper(b)) +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , debug_check_(b.get_debug_check()) +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING { } +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + const_buffer(const void* data, std::size_t size, + boost::function debug_check) + : data_(data), + size_(size), + debug_check_(debug_check) + { + } + + const boost::function& get_debug_check() const + { + return debug_check_; + } +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + private: friend const void* boost::asio::detail::buffer_cast_helper( const const_buffer& b); @@ -200,12 +263,20 @@ private: const void* data_; std::size_t size_; + +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + boost::function debug_check_; +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING }; namespace detail { inline const void* buffer_cast_helper(const const_buffer& b) { +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + if (b.debug_check_) + b.debug_check_(); +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING return b.data_; } @@ -245,7 +316,11 @@ inline const_buffer operator+(const const_buffer& b, std::size_t start) return const_buffer(); const char* new_data = buffer_cast(b) + start; std::size_t new_size = buffer_size(b) - start; - return const_buffer(new_data, new_size); + return const_buffer(new_data, new_size +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + ); } /// Create a new non-modifiable buffer that is offset from the start of another. @@ -258,7 +333,11 @@ inline const_buffer operator+(std::size_t start, const const_buffer& b) return const_buffer(); const char* new_data = buffer_cast(b) + start; std::size_t new_size = buffer_size(b) - start; - return const_buffer(new_data, new_size); + return const_buffer(new_data, new_size +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + ); } /// Adapts a single non-modifiable buffer so that it meets the requirements of @@ -292,6 +371,30 @@ public: } }; +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) +namespace detail { + +template +class buffer_debug_check +{ +public: + buffer_debug_check(Iterator iter) + : iter_(iter) + { + } + + void operator()() + { + *iter_; + } + +private: + Iterator iter_; +}; + +} // namespace detail +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + /** @defgroup buffer boost::asio::buffer * * @brief The boost::asio::buffer function is used to create a buffer object to @@ -356,7 +459,11 @@ inline mutable_buffer_container_1 buffer(const mutable_buffer& b, return mutable_buffer_container_1( mutable_buffer(buffer_cast(b), buffer_size(b) < max_size_in_bytes - ? buffer_size(b) : max_size_in_bytes)); + ? buffer_size(b) : max_size_in_bytes +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + )); } /// Create a new non-modifiable buffer from an existing buffer. @@ -372,7 +479,11 @@ inline const_buffer_container_1 buffer(const const_buffer& b, return const_buffer_container_1( const_buffer(buffer_cast(b), buffer_size(b) < max_size_in_bytes - ? buffer_size(b) : max_size_in_bytes)); + ? buffer_size(b) : max_size_in_bytes +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + )); } /// Create a new modifiable buffer that represents the given memory range. @@ -561,7 +672,13 @@ template inline mutable_buffer_container_1 buffer(std::vector& data) { return mutable_buffer_container_1( - mutable_buffer(&data[0], data.size() * sizeof(Pod_Type))); + mutable_buffer(&data[0], data.size() * sizeof(Pod_Type) +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::iterator + >(data.begin()) +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + )); } /// Create a new modifiable buffer that represents the given POD vector. @@ -576,7 +693,13 @@ inline mutable_buffer_container_1 buffer(std::vector& data, return mutable_buffer_container_1( mutable_buffer(&data[0], data.size() * sizeof(Pod_Type) < max_size_in_bytes - ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::iterator + >(data.begin()) +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + )); } /// Create a new non-modifiable buffer that represents the given POD vector. @@ -589,7 +712,13 @@ inline const_buffer_container_1 buffer( const std::vector& data) { return const_buffer_container_1( - const_buffer(&data[0], data.size() * sizeof(Pod_Type))); + const_buffer(&data[0], data.size() * sizeof(Pod_Type) +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::const_iterator + >(data.begin()) +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + )); } /// Create a new non-modifiable buffer that represents the given POD vector. @@ -604,7 +733,13 @@ inline const_buffer_container_1 buffer( return const_buffer_container_1( const_buffer(&data[0], data.size() * sizeof(Pod_Type) < max_size_in_bytes - ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::const_iterator + >(data.begin()) +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + )); } /// Create a new non-modifiable buffer that represents the given string. @@ -614,7 +749,11 @@ inline const_buffer_container_1 buffer( */ inline const_buffer_container_1 buffer(const std::string& data) { - return const_buffer_container_1(const_buffer(data.data(), data.size())); + return const_buffer_container_1(const_buffer(data.data(), data.size() +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check(data.begin()) +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + )); } /// Create a new non-modifiable buffer that represents the given string. @@ -628,7 +767,11 @@ inline const_buffer_container_1 buffer(const std::string& data, return const_buffer_container_1( const_buffer(data.data(), data.size() < max_size_in_bytes - ? data.size() : max_size_in_bytes)); + ? data.size() : max_size_in_bytes +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check(data.begin()) +#endif // BOOST_ASIO_ENABLE_BUFFER_DEBUGGING + )); } /*@}*/ diff --git a/include/boost/asio/datagram_socket_service.hpp b/include/boost/asio/datagram_socket_service.hpp index 1ce4837d..3b35981f 100644 --- a/include/boost/asio/datagram_socket_service.hpp +++ b/include/boost/asio/datagram_socket_service.hpp @@ -130,6 +130,13 @@ public: return service_impl_.native(impl); } + /// Cancel all asynchronous operations associated with the socket. + template + void cancel(implementation_type& impl, Error_Handler error_handler) + { + service_impl_.cancel(impl, error_handler); + } + // Bind the datagram socket to the specified local endpoint. template void bind(implementation_type& impl, const endpoint_type& endpoint, diff --git a/include/boost/asio/detail/bind_handler.hpp b/include/boost/asio/detail/bind_handler.hpp index f41a30a6..46a1388a 100644 --- a/include/boost/asio/detail/bind_handler.hpp +++ b/include/boost/asio/detail/bind_handler.hpp @@ -18,7 +18,7 @@ #include #include -#include +#include namespace boost { namespace asio { @@ -65,12 +65,12 @@ inline void asio_handler_deallocate(void* pointer, std::size_t size, pointer, size, &this_handler->handler_); } -template -inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, +template +inline void asio_handler_invoke(const Function& function, binder1* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->handler_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); } template @@ -123,13 +123,12 @@ inline void asio_handler_deallocate(void* pointer, std::size_t size, pointer, size, &this_handler->handler_); } -template -inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, +template +inline void asio_handler_invoke(const Function& function, binder2* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->handler_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); } template @@ -185,13 +184,13 @@ inline void asio_handler_deallocate(void* pointer, std::size_t size, pointer, size, &this_handler->handler_); } -template -inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, +template +inline void asio_handler_invoke(const Function& function, binder3* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->handler_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); } template @@ -252,13 +251,13 @@ inline void asio_handler_deallocate(void* pointer, std::size_t size, pointer, size, &this_handler->handler_); } -template -inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, +template +inline void asio_handler_invoke(const Function& function, binder4* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->handler_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); } template handler_); } -template -inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, +template +inline void asio_handler_invoke(const Function& function, binder5* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->handler_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); } template (owner(), handler), &impl); + wait_handler(io_service(), handler), &impl); } private: diff --git a/include/boost/asio/detail/epoll_reactor.hpp b/include/boost/asio/detail/epoll_reactor.hpp index 3d66c930..2ce249fd 100644 --- a/include/boost/asio/detail/epoll_reactor.hpp +++ b/include/boost/asio/detail/epoll_reactor.hpp @@ -297,6 +297,21 @@ public: timer_queues_.push_back(&timer_queue); } + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& timer_queue) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + { + if (timer_queues_[i] == &timer_queue) + { + timer_queues_.erase(timer_queues_.begin() + i); + return; + } + } + } + // Schedule a timer in the given timer queue to expire at the specified // absolute time. The handler object will be invoked when the timer expires. template diff --git a/include/boost/asio/detail/fd_set_adapter.hpp b/include/boost/asio/detail/fd_set_adapter.hpp index 19f35444..89e79fca 100644 --- a/include/boost/asio/detail/fd_set_adapter.hpp +++ b/include/boost/asio/detail/fd_set_adapter.hpp @@ -17,48 +17,22 @@ #include -#include +#include +#include +#include + +#include +#include namespace boost { namespace asio { namespace detail { -// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. -class fd_set_adapter -{ -public: - fd_set_adapter() - : max_descriptor_(invalid_socket) - { - FD_ZERO(&fd_set_); - } - - void set(socket_type descriptor) - { - if (max_descriptor_ == invalid_socket || descriptor > max_descriptor_) - max_descriptor_ = descriptor; - FD_SET(descriptor, &fd_set_); - } - - bool is_set(socket_type descriptor) const - { - return FD_ISSET(descriptor, &fd_set_) != 0; - } - - operator fd_set*() - { - return &fd_set_; - } - - socket_type max_descriptor() const - { - return max_descriptor_; - } - -private: - fd_set fd_set_; - socket_type max_descriptor_; -}; +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef win_fd_set_adapter fd_set_adapter; +#else +typedef posix_fd_set_adapter fd_set_adapter; +#endif } // namespace detail } // namespace asio diff --git a/include/boost/asio/detail/handler_dispatch_helpers.hpp b/include/boost/asio/detail/handler_invoke_helpers.hpp similarity index 50% rename from include/boost/asio/detail/handler_dispatch_helpers.hpp rename to include/boost/asio/detail/handler_invoke_helpers.hpp index e1cff4a9..dbe93b66 100644 --- a/include/boost/asio/detail/handler_dispatch_helpers.hpp +++ b/include/boost/asio/detail/handler_invoke_helpers.hpp @@ -1,6 +1,6 @@ // -// handler_dispatch_helpers.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// handler_invoke_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) // @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_ASIO_DETAIL_HANDLER_DISPATCH_HELPERS_HPP -#define BOOST_ASIO_DETAIL_HANDLER_DISPATCH_HELPERS_HPP +#ifndef BOOST_ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP +#define BOOST_ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once @@ -21,27 +21,27 @@ #include #include -#include +#include -// Calls to asio_handler_dispatch must be made from a namespace that does not -// contain overloads of this function. The boost_asio_handler_dispatch_helpers +// Calls to asio_handler_invoke must be made from a namespace that does not +// contain overloads of this function. The asio_handler_invoke_helpers // namespace is defined here for that purpose. -namespace boost_asio_handler_dispatch_helpers { +namespace asio_handler_invoke_helpers { -template -inline void dispatch_handler(const Handler& handler, Context* context) +template +inline void invoke(const Function& function, Context* context) { #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) - Handler tmp(handler); + Function tmp(function); tmp(); #else using namespace boost::asio; - asio_handler_dispatch(handler, context); + asio_handler_invoke(function, context); #endif } -} // namespace boost_asio_handler_dispatch_helpers +} // namespace asio_handler_invoke_helpers #include -#endif // BOOST_ASIO_DETAIL_HANDLER_DISPATCH_HELPERS_HPP +#endif // BOOST_ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP diff --git a/include/boost/asio/detail/hash_map.hpp b/include/boost/asio/detail/hash_map.hpp index 5e86701e..be208e2f 100644 --- a/include/boost/asio/detail/hash_map.hpp +++ b/include/boost/asio/detail/hash_map.hpp @@ -30,6 +30,16 @@ namespace boost { namespace asio { namespace detail { +template +inline std::size_t calculate_hash_value(const T& t) +{ + // It would be better to use "using boost::hash_value;" here, but it makes + // Borland C++ crash. + using namespace boost; + + return hash_value(t); +} + template class hash_map : private noncopyable @@ -85,7 +95,7 @@ public: // Find an entry in the map. iterator find(const K& k) { - size_t bucket = boost::hash_value(k) % num_buckets; + size_t bucket = calculate_hash_value(k) % num_buckets; iterator it = buckets_[bucket].first; if (it == values_.end()) return values_.end(); @@ -103,7 +113,7 @@ public: // Find an entry in the map. const_iterator find(const K& k) const { - size_t bucket = boost::hash_value(k) % num_buckets; + size_t bucket = calculate_hash_value(k) % num_buckets; const_iterator it = buckets_[bucket].first; if (it == values_.end()) return it; @@ -121,7 +131,7 @@ public: // Insert a new entry into the map. std::pair insert(const value_type& v) { - size_t bucket = boost::hash_value(v.first) % num_buckets; + size_t bucket = calculate_hash_value(v.first) % num_buckets; iterator it = buckets_[bucket].first; if (it == values_.end()) { @@ -146,7 +156,7 @@ public: { assert(it != values_.end()); - size_t bucket = boost::hash_value(it->first) % num_buckets; + size_t bucket = calculate_hash_value(it->first) % num_buckets; bool is_first = (it == buckets_[bucket].first); bool is_last = (it == buckets_[bucket].last); if (is_first && is_last) diff --git a/include/boost/asio/detail/io_control.hpp b/include/boost/asio/detail/io_control.hpp index e8e9453a..715bb6f0 100644 --- a/include/boost/asio/detail/io_control.hpp +++ b/include/boost/asio/detail/io_control.hpp @@ -29,19 +29,18 @@ namespace asio { namespace detail { namespace io_control { -// Helper template for implementing boolean-based IO control commands. -template -class boolean +// IO control command for non-blocking I/O. +class non_blocking_io { public: // Default constructor. - boolean() + non_blocking_io() : value_(0) { } // Construct with a specific command value. - boolean(bool value) + non_blocking_io(bool value) : value_(value ? 1 : 0) { } @@ -49,16 +48,16 @@ public: // Get the name of the IO control command. int name() const { - return Name; + return FIONBIO; } - // Set the value of the boolean. + // Set the value of the I/O control command. void set(bool value) { value_ = value ? 1 : 0; } - // Get the current value of the boolean. + // Get the current value of the I/O control command. bool get() const { return value_ != 0; @@ -80,36 +79,35 @@ private: detail::ioctl_arg_type value_; }; -// Helper template for implementing size-based IO control commands. -template -class size +// I/O control command for getting number of bytes available. +class bytes_readable { public: // Default constructor. - size() + bytes_readable() : value_(0) { } // Construct with a specific command value. - size(std::size_t value) - : value_(value) + bytes_readable(std::size_t value) + : value_(static_cast(value)) { } // Get the name of the IO control command. int name() const { - return Name; + return FIONREAD; } - // Set the value of the size. + // Set the value of the I/O control command. void set(std::size_t value) { value_ = static_cast(value); } - // Get the current value of the size. + // Get the current value of the I/O control command. std::size_t get() const { return static_cast(value_); diff --git a/include/boost/asio/detail/kqueue_reactor.hpp b/include/boost/asio/detail/kqueue_reactor.hpp index fa15eb0e..d2021457 100644 --- a/include/boost/asio/detail/kqueue_reactor.hpp +++ b/include/boost/asio/detail/kqueue_reactor.hpp @@ -286,6 +286,21 @@ public: timer_queues_.push_back(&timer_queue); } + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& timer_queue) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + { + if (timer_queues_[i] == &timer_queue) + { + timer_queues_.erase(timer_queues_.begin() + i); + return; + } + } + } + // Schedule a timer in the given timer queue to expire at the specified // absolute time. The handler object will be invoked when the timer expires. template diff --git a/include/boost/asio/detail/win_local_free_on_block_exit.hpp b/include/boost/asio/detail/local_free_on_block_exit.hpp similarity index 75% rename from include/boost/asio/detail/win_local_free_on_block_exit.hpp rename to include/boost/asio/detail/local_free_on_block_exit.hpp index 1cce9560..691e30d6 100644 --- a/include/boost/asio/detail/win_local_free_on_block_exit.hpp +++ b/include/boost/asio/detail/local_free_on_block_exit.hpp @@ -1,6 +1,6 @@ // -// win_local_free_on_block_exit.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// local_free_on_block_exit.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) // @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP -#define BOOST_ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP +#ifndef BOOST_ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP +#define BOOST_ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once @@ -30,18 +30,18 @@ namespace boost { namespace asio { namespace detail { -class win_local_free_on_block_exit +class local_free_on_block_exit : private noncopyable { public: // Constructor blocks all signals for the calling thread. - explicit win_local_free_on_block_exit(void* p) + explicit local_free_on_block_exit(void* p) : p_(p) { } // Destructor restores the previous signal mask. - ~win_local_free_on_block_exit() + ~local_free_on_block_exit() { ::LocalFree(p_); } @@ -58,4 +58,4 @@ private: #include -#endif // BOOST_ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP +#endif // BOOST_ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP diff --git a/include/boost/asio/detail/posix_fd_set_adapter.hpp b/include/boost/asio/detail/posix_fd_set_adapter.hpp new file mode 100644 index 00000000..51a5b64b --- /dev/null +++ b/include/boost/asio/detail/posix_fd_set_adapter.hpp @@ -0,0 +1,73 @@ +// +// posix_fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP +#define BOOST_ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +namespace boost { +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class posix_fd_set_adapter +{ +public: + posix_fd_set_adapter() + : max_descriptor_(invalid_socket) + { + FD_ZERO(&fd_set_); + } + + void set(socket_type descriptor) + { + if (max_descriptor_ == invalid_socket || descriptor > max_descriptor_) + max_descriptor_ = descriptor; + FD_SET(descriptor, &fd_set_); + } + + bool is_set(socket_type descriptor) const + { + return FD_ISSET(descriptor, &fd_set_) != 0; + } + + operator fd_set*() + { + return &fd_set_; + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + +private: + fd_set fd_set_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include + +#endif // BOOST_ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP diff --git a/include/boost/asio/detail/reactive_socket_service.hpp b/include/boost/asio/detail/reactive_socket_service.hpp index 7af8d907..df268644 100644 --- a/include/boost/asio/detail/reactive_socket_service.hpp +++ b/include/boost/asio/detail/reactive_socket_service.hpp @@ -74,7 +74,8 @@ public: { user_set_non_blocking = 1, // The user wants a non-blocking socket. internal_non_blocking = 2, // The socket has been set non-blocking. - enable_connection_aborted = 4 // User wants connection_aborted errors. + enable_connection_aborted = 4, // User wants connection_aborted errors. + user_set_linger = 8 // The user set the linger option. }; // Flags indicating the current state of the socket. @@ -109,7 +110,29 @@ public: // Destroy a socket implementation. void destroy(implementation_type& impl) { - close(impl, boost::asio::ignore_error()); + if (impl.socket_ != invalid_socket) + { + reactor_.close_descriptor(impl.socket_); + + if (impl.flags_ & implementation_type::internal_non_blocking) + { + ioctl_arg_type non_blocking = 0; + socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking); + impl.flags_ &= ~implementation_type::internal_non_blocking; + } + + if (impl.flags_ & implementation_type::user_set_linger) + { + ::linger opt; + opt.l_onoff = 0; + opt.l_linger = 0; + socket_ops::setsockopt(impl.socket_, + SOL_SOCKET, SO_LINGER, &opt, sizeof(opt)); + } + + socket_ops::close(impl.socket_); + impl.socket_ = invalid_socket; + } } // Open a new socket implementation. @@ -193,6 +216,22 @@ public: return impl.socket_; } + // Cancel all operations associated with the socket. + template + void cancel(implementation_type& impl, Error_Handler error_handler) + { + if (impl.socket_ == invalid_socket) + { + boost::asio::error error(boost::asio::error::bad_descriptor); + error_handler(error); + } + else + { + reactor_.cancel_ops(impl.socket_); + error_handler(boost::asio::error(0)); + } + } + // Bind the socket to the specified local endpoint. template void bind(implementation_type& impl, const endpoint_type& endpoint, @@ -210,9 +249,6 @@ public: void listen(implementation_type& impl, int backlog, Error_Handler error_handler) { - if (backlog == 0) - backlog = SOMAXCONN; - if (socket_ops::listen(impl.socket_, backlog) == socket_error_retval) error_handler(boost::asio::error(socket_ops::get_error())); else @@ -242,6 +278,12 @@ public: } else { + if (option.level(impl.protocol_) == SOL_SOCKET + && option.name(impl.protocol_) == SO_LINGER) + { + impl.flags_ |= implementation_type::user_set_linger; + } + if (socket_ops::setsockopt(impl.socket_, option.level(impl.protocol_), option.name(impl.protocol_), option.data(impl.protocol_), option.size(impl.protocol_))) @@ -361,12 +403,21 @@ public: typename Const_Buffers::const_iterator iter = buffers.begin(); typename Const_Buffers::const_iterator end = buffers.end(); size_t i = 0; + size_t total_buffer_size = 0; for (; iter != end && i < max_buffers; ++iter, ++i) { boost::asio::const_buffer buffer(*iter); socket_ops::init_buf(bufs[i], boost::asio::buffer_cast(buffer), boost::asio::buffer_size(buffer)); + total_buffer_size += boost::asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + error_handler(boost::asio::error(0)); + return 0; } // Send the data. @@ -472,10 +523,32 @@ public: if (impl.socket_ == invalid_socket) { boost::asio::error error(boost::asio::error::bad_descriptor); - owner().post(bind_handler(handler, error, 0)); + io_service().post(bind_handler(handler, error, 0)); } else { + if (impl.protocol_.type() == SOCK_STREAM) + { + // Determine total size of buffers. + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + boost::asio::const_buffer buffer(*iter); + total_buffer_size += boost::asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (total_buffer_size == 0) + { + boost::asio::error error(boost::asio::error::success); + io_service().post(bind_handler(handler, error, 0)); + return; + } + } + // Make socket non-blocking. if (!(impl.flags_ & implementation_type::internal_non_blocking)) { @@ -483,7 +556,7 @@ public: if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) { boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error, 0)); + io_service().post(bind_handler(handler, error, 0)); return; } impl.flags_ |= implementation_type::internal_non_blocking; @@ -491,7 +564,7 @@ public: reactor_.start_write_op(impl.socket_, send_handler( - impl.socket_, owner(), buffers, flags, handler)); + impl.socket_, io_service(), buffers, flags, handler)); } } @@ -623,7 +696,7 @@ public: if (impl.socket_ == invalid_socket) { boost::asio::error error(boost::asio::error::bad_descriptor); - owner().post(bind_handler(handler, error, 0)); + io_service().post(bind_handler(handler, error, 0)); } else { @@ -634,7 +707,7 @@ public: if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) { boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error, 0)); + io_service().post(bind_handler(handler, error, 0)); return; } impl.flags_ |= implementation_type::internal_non_blocking; @@ -642,7 +715,7 @@ public: reactor_.start_write_op(impl.socket_, send_to_handler( - impl.socket_, owner(), buffers, destination, flags, handler)); + impl.socket_, io_service(), buffers, destination, flags, handler)); } } @@ -656,12 +729,21 @@ public: typename Mutable_Buffers::const_iterator iter = buffers.begin(); typename Mutable_Buffers::const_iterator end = buffers.end(); size_t i = 0; + size_t total_buffer_size = 0; for (; iter != end && i < max_buffers; ++iter, ++i) { boost::asio::mutable_buffer buffer(*iter); socket_ops::init_buf(bufs[i], boost::asio::buffer_cast(buffer), boost::asio::buffer_size(buffer)); + total_buffer_size += boost::asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + error_handler(boost::asio::error(0)); + return 0; } // Receive some data. @@ -778,10 +860,32 @@ public: if (impl.socket_ == invalid_socket) { boost::asio::error error(boost::asio::error::bad_descriptor); - owner().post(bind_handler(handler, error, 0)); + io_service().post(bind_handler(handler, error, 0)); } else { + if (impl.protocol_.type() == SOCK_STREAM) + { + // Determine total size of buffers. + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + boost::asio::mutable_buffer buffer(*iter); + total_buffer_size += boost::asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (total_buffer_size == 0) + { + boost::asio::error error(boost::asio::error::success); + io_service().post(bind_handler(handler, error, 0)); + return; + } + } + // Make socket non-blocking. if (!(impl.flags_ & implementation_type::internal_non_blocking)) { @@ -789,7 +893,7 @@ public: if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) { boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error, 0)); + io_service().post(bind_handler(handler, error, 0)); return; } impl.flags_ |= implementation_type::internal_non_blocking; @@ -799,13 +903,13 @@ public: { reactor_.start_except_op(impl.socket_, receive_handler( - impl.socket_, owner(), buffers, flags, handler)); + impl.socket_, io_service(), buffers, flags, handler)); } else { reactor_.start_read_op(impl.socket_, receive_handler( - impl.socket_, owner(), buffers, flags, handler)); + impl.socket_, io_service(), buffers, flags, handler)); } } } @@ -955,7 +1059,7 @@ public: if (impl.socket_ == invalid_socket) { boost::asio::error error(boost::asio::error::bad_descriptor); - owner().post(bind_handler(handler, error, 0)); + io_service().post(bind_handler(handler, error, 0)); } else { @@ -966,7 +1070,7 @@ public: if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) { boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error, 0)); + io_service().post(bind_handler(handler, error, 0)); return; } impl.flags_ |= implementation_type::internal_non_blocking; @@ -974,7 +1078,7 @@ public: reactor_.start_read_op(impl.socket_, receive_from_handler( - impl.socket_, owner(), buffers, + impl.socket_, io_service(), buffers, sender_endpoint, flags, handler)); } } @@ -1197,12 +1301,12 @@ public: if (impl.socket_ == invalid_socket) { boost::asio::error error(boost::asio::error::bad_descriptor); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); } else if (peer.native() != invalid_socket) { boost::asio::error error(boost::asio::error::already_connected); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); } else { @@ -1213,7 +1317,7 @@ public: if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) { boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } impl.flags_ |= implementation_type::internal_non_blocking; @@ -1221,7 +1325,7 @@ public: reactor_.start_read_op(impl.socket_, accept_handler( - impl.socket_, owner(), peer, impl.protocol_, + impl.socket_, io_service(), peer, impl.protocol_, (impl.flags_ & implementation_type::enable_connection_aborted) != 0, handler)); } @@ -1302,12 +1406,12 @@ public: if (impl.socket_ == invalid_socket) { boost::asio::error error(boost::asio::error::bad_descriptor); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); } else if (peer.native() != invalid_socket) { boost::asio::error error(boost::asio::error::already_connected); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); } else { @@ -1318,7 +1422,7 @@ public: if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) { boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } impl.flags_ |= implementation_type::internal_non_blocking; @@ -1326,7 +1430,7 @@ public: reactor_.start_read_op(impl.socket_, accept_endp_handler( - impl.socket_, owner(), peer, peer_endpoint, + impl.socket_, io_service(), peer, peer_endpoint, (impl.flags_ & implementation_type::enable_connection_aborted) != 0, handler)); } @@ -1469,7 +1573,7 @@ public: if (impl.socket_ == invalid_socket) { boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } @@ -1478,7 +1582,7 @@ public: { socket_ops::close(impl.socket_); boost::asio::error error(err); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } } @@ -1490,7 +1594,7 @@ public: if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) { boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } impl.flags_ |= implementation_type::internal_non_blocking; @@ -1504,7 +1608,7 @@ public: // The connect operation has finished successfully so we need to post the // handler immediately. boost::asio::error error(boost::asio::error::success); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); } else if (socket_ops::get_error() == boost::asio::error::in_progress || socket_ops::get_error() == boost::asio::error::would_block) @@ -1514,13 +1618,13 @@ public: boost::shared_ptr completed(new bool(false)); reactor_.start_write_and_except_ops(impl.socket_, connect_handler( - impl.socket_, completed, owner(), reactor_, handler)); + impl.socket_, completed, io_service(), reactor_, handler)); } else { // The connect operation has failed, so post the handler immediately. boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); } } diff --git a/include/boost/asio/detail/resolver_service.hpp b/include/boost/asio/detail/resolver_service.hpp index 5935e1dc..a90e3a25 100644 --- a/include/boost/asio/detail/resolver_service.hpp +++ b/include/boost/asio/detail/resolver_service.hpp @@ -216,7 +216,7 @@ public: start_work_thread(); work_io_service_->post( resolve_query_handler( - impl, query, owner(), handler)); + impl, query, io_service(), handler)); } } @@ -314,7 +314,7 @@ public: start_work_thread(); work_io_service_->post( resolve_endpoint_handler( - impl, endpoint, owner(), handler)); + impl, endpoint, io_service(), handler)); } } diff --git a/include/boost/asio/detail/select_reactor.hpp b/include/boost/asio/detail/select_reactor.hpp index b1adeb59..d8ae6142 100644 --- a/include/boost/asio/detail/select_reactor.hpp +++ b/include/boost/asio/detail/select_reactor.hpp @@ -195,6 +195,21 @@ public: timer_queues_.push_back(&timer_queue); } + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& timer_queue) + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + { + if (timer_queues_[i] == &timer_queue) + { + timer_queues_.erase(timer_queues_.begin() + i); + return; + } + } + } + // Schedule a timer in the given timer queue to expire at the specified // absolute time. The handler object will be invoked when the timer expires. template diff --git a/include/boost/asio/detail/socket_ops.hpp b/include/boost/asio/detail/socket_ops.hpp index 4bd092f9..855e2a9a 100644 --- a/include/boost/asio/detail/socket_ops.hpp +++ b/include/boost/asio/detail/socket_ops.hpp @@ -561,6 +561,12 @@ inline int gethostname(char* name, int namelen) return error_wrapper(::gethostname(name, namelen)); } +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) \ + || defined(__MACH__) && defined(__APPLE__) + +// The following functions are only needed for emulation of getaddrinfo and +// getnameinfo. + inline int translate_netdb_error(int error) { switch (error) @@ -1151,7 +1157,6 @@ inline int getaddrinfo_emulation(const char* host, const char* service, case NO_RECOVERY: return EAI_FAIL; case NO_DATA: - return EAI_NODATA; default: return EAI_NONAME; } @@ -1240,92 +1245,6 @@ inline int getaddrinfo_emulation(const char* host, const char* service, return 0; } -inline int translate_addrinfo_error(int error) -{ - switch (error) - { - case 0: - return boost::asio::error::success; - case EAI_AGAIN: - return boost::asio::error::host_not_found_try_again; - case EAI_BADFLAGS: - return boost::asio::error::invalid_argument; - case EAI_FAIL: - return boost::asio::error::no_recovery; - case EAI_FAMILY: - return boost::asio::error::address_family_not_supported; - case EAI_MEMORY: - return boost::asio::error::no_memory; - case EAI_NONAME: - return boost::asio::error::host_not_found; - case EAI_SERVICE: - return boost::asio::error::service_not_found; - case EAI_SOCKTYPE: - return boost::asio::error::socket_type_not_supported; - default: // Possibly the non-portable EAI_SYSTEM. - return get_error(); - } -} - -inline int getaddrinfo(const char* host, const char* service, - const addrinfo_type* hints, addrinfo_type** result) -{ - set_error(0); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) -# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) - // Building for Windows XP, Windows Server 2003, or later. - int error = ::getaddrinfo(host, service, hints, result); - return translate_addrinfo_error(error); -# else - // Building for Windows 2000 or earlier. - typedef int (WSAAPI *gai_t)(const char*, - const char*, const addrinfo_type*, addrinfo_type**); - if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) - { - if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo")) - { - int error = gai(host, service, hints, result); - return translate_addrinfo_error(error); - } - } - int error = getaddrinfo_emulation(host, service, hints, result); - return translate_addrinfo_error(error); -# endif -#elif defined(__MACH__) && defined(__APPLE__) - int error = getaddrinfo_emulation(host, service, hints, result); - return translate_addrinfo_error(error); -#else - int error = ::getaddrinfo(host, service, hints, result); - return translate_addrinfo_error(error); -#endif -} - -inline void freeaddrinfo(addrinfo_type* ai) -{ -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) -# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) - // Building for Windows XP, Windows Server 2003, or later. - ::freeaddrinfo(ai); -# else - // Building for Windows 2000 or earlier. - typedef int (WSAAPI *fai_t)(addrinfo_type*); - if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) - { - if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo")) - { - fai(ai); - return; - } - } - freeaddrinfo_emulation(ai); -# endif -#elif defined(__MACH__) && defined(__APPLE__) - freeaddrinfo_emulation(ai); -#else - ::freeaddrinfo(ai); -#endif -} - inline int getnameinfo_emulation(const socket_addr_type* sa, socket_addr_len_type salen, char* host, std::size_t hostlen, char* serv, std::size_t servlen, int flags) @@ -1453,6 +1372,95 @@ inline int getnameinfo_emulation(const socket_addr_type* sa, return 0; } +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // || defined(__MACH__) && defined(__APPLE__) + +inline int translate_addrinfo_error(int error) +{ + switch (error) + { + case 0: + return boost::asio::error::success; + case EAI_AGAIN: + return boost::asio::error::host_not_found_try_again; + case EAI_BADFLAGS: + return boost::asio::error::invalid_argument; + case EAI_FAIL: + return boost::asio::error::no_recovery; + case EAI_FAMILY: + return boost::asio::error::address_family_not_supported; + case EAI_MEMORY: + return boost::asio::error::no_memory; + case EAI_NONAME: + return boost::asio::error::host_not_found; + case EAI_SERVICE: + return boost::asio::error::service_not_found; + case EAI_SOCKTYPE: + return boost::asio::error::socket_type_not_supported; + default: // Possibly the non-portable EAI_SYSTEM. + return get_error(); + } +} + +inline int getaddrinfo(const char* host, const char* service, + const addrinfo_type* hints, addrinfo_type** result) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) + // Building for Windows XP, Windows Server 2003, or later. + int error = ::getaddrinfo(host, service, hints, result); + return translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gai_t)(const char*, + const char*, const addrinfo_type*, addrinfo_type**); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo")) + { + int error = gai(host, service, hints, result); + return translate_addrinfo_error(error); + } + } + int error = getaddrinfo_emulation(host, service, hints, result); + return translate_addrinfo_error(error); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + int error = getaddrinfo_emulation(host, service, hints, result); + return translate_addrinfo_error(error); +#else + int error = ::getaddrinfo(host, service, hints, result); + return translate_addrinfo_error(error); +#endif +} + +inline void freeaddrinfo(addrinfo_type* ai) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) + // Building for Windows XP, Windows Server 2003, or later. + ::freeaddrinfo(ai); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *fai_t)(addrinfo_type*); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo")) + { + fai(ai); + return; + } + } + freeaddrinfo_emulation(ai); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + freeaddrinfo_emulation(ai); +#else + ::freeaddrinfo(ai); +#endif +} + inline int getnameinfo(const socket_addr_type* addr, socket_addr_len_type addrlen, char* host, std::size_t hostlen, char* serv, std::size_t servlen, int flags) @@ -1461,7 +1469,8 @@ inline int getnameinfo(const socket_addr_type* addr, # if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) // Building for Windows XP, Windows Server 2003, or later. set_error(0); - int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags); + int error = ::getnameinfo(addr, addrlen, host, static_cast(hostlen), + serv, static_cast(servlen), flags); return translate_addrinfo_error(error); # else // Building for Windows 2000 or earlier. diff --git a/include/boost/asio/detail/socket_types.hpp b/include/boost/asio/detail/socket_types.hpp index 9d0f3e24..d69b561a 100644 --- a/include/boost/asio/detail/socket_types.hpp +++ b/include/boost/asio/detail/socket_types.hpp @@ -23,6 +23,9 @@ #include #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) +# error WinSock.h has already been included +# endif // defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) # if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) # if defined(_MSC_VER) || defined(__BORLANDC__) # pragma message("Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately") @@ -56,7 +59,6 @@ # define BOOST_ASIO_WSPIAPI_H_DEFINED # endif // !defined(_WSPIAPI_H_) # endif // defined(__BORLANDC__) -# define FD_SETSIZE 1024 # if !defined(BOOST_ASIO_NO_WIN32_LEAN_AND_MEAN) # if !defined(WIN32_LEAN_AND_MEAN) # define WIN32_LEAN_AND_MEAN @@ -168,6 +170,13 @@ const int message_do_not_route = MSG_DONTROUTE; const int custom_socket_option_level = 0xA5100000; const int enable_connection_aborted_option = 1; +#if defined(_WIN64) +std::size_t hash_value(SOCKET s) +{ + return static_cast(s); +} +#endif // defined(_WIN64) + } // namespace detail } // namespace asio } // namespace boost diff --git a/include/boost/asio/detail/strand_service.hpp b/include/boost/asio/detail/strand_service.hpp index 430b43cb..d536ab7b 100644 --- a/include/boost/asio/detail/strand_service.hpp +++ b/include/boost/asio/detail/strand_service.hpp @@ -20,13 +20,14 @@ #include #include #include +#include #include #include #include #include #include -#include +#include #include #include @@ -43,18 +44,86 @@ public: class invoke_current_handler; class post_next_waiter_on_exit; - // The native type of the deadline timer. - class implementation_type + // The underlying implementation of a strand. + class strand_impl { +#if defined (__BORLANDC__) + public: +#else + private: +#endif + void add_ref() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + ++ref_count_; + } + + void release() + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + --ref_count_; + if (ref_count_ == 0) + { + lock.unlock(); + delete this; + } + } + private: // Only this service will have access to the internal values. friend class strand_service; friend class post_next_waiter_on_exit; friend class invoke_current_handler; + strand_impl(strand_service& owner) + : owner_(owner), + current_handler_(0), + first_waiter_(0), + last_waiter_(0), + ref_count_(0) + { + // Insert implementation into linked list of all implementations. + boost::asio::detail::mutex::scoped_lock lock(owner_.mutex_); + next_ = owner_.impl_list_; + prev_ = 0; + if (owner_.impl_list_) + owner_.impl_list_->prev_ = this; + owner_.impl_list_ = this; + } + + ~strand_impl() + { + // Remove implementation from linked list of all implementations. + boost::asio::detail::mutex::scoped_lock lock(owner_.mutex_); + if (owner_.impl_list_ == this) + owner_.impl_list_ = next_; + if (prev_) + prev_->next_ = next_; + if (next_) + next_->prev_= prev_; + next_ = 0; + prev_ = 0; + lock.unlock(); + + if (current_handler_) + { + current_handler_->destroy(); + } + + while (first_waiter_) + { + handler_base* next = first_waiter_->next_; + first_waiter_->destroy(); + first_waiter_ = next; + } + } + // Mutex to protect access to internal data. boost::asio::detail::mutex mutex_; + // The service that owns this implementation. + strand_service& owner_; + // The handler that is ready to execute. If this pointer is non-null then it // indicates that a handler holds the lock. handler_base* current_handler_; @@ -66,13 +135,37 @@ public: handler_base* last_waiter_; // Storage for posted handlers. + typedef boost::aligned_storage<64> handler_storage_type; +#if defined(__BORLANDC__) boost::aligned_storage<64> handler_storage_; +#else + handler_storage_type handler_storage_; +#endif // Pointers to adjacent socket implementations in linked list. - implementation_type* next_; - implementation_type* prev_; + strand_impl* next_; + strand_impl* prev_; + + // The reference count on the strand implementation. + size_t ref_count_; + +#if !defined(__BORLANDC__) + friend void intrusive_ptr_add_ref(strand_impl* p) + { + p->add_ref(); + } + + friend void intrusive_ptr_release(strand_impl* p) + { + p->release(); + } +#endif }; + friend class strand_impl; + + typedef boost::intrusive_ptr implementation_type; + // Base class for all handler types. class handler_base { @@ -105,6 +198,7 @@ public: private: friend class strand_service; + friend class strand_impl; friend class post_next_waiter_on_exit; handler_base* next_; invoke_func_type invoke_func_; @@ -116,7 +210,7 @@ public: { public: invoke_current_handler(strand_service& service_impl, - implementation_type& impl) + const implementation_type& impl) : service_impl_(service_impl), impl_(impl) { @@ -124,7 +218,7 @@ public: void operator()() { - impl_.current_handler_->invoke(service_impl_, impl_); + impl_->current_handler_->invoke(service_impl_, impl_); } friend void* asio_handler_allocate(std::size_t size, @@ -140,20 +234,24 @@ public: void* do_handler_allocate(std::size_t size) { - BOOST_ASSERT(size <= impl_.handler_storage_.size); - return impl_.handler_storage_.address(); +#if defined(__BORLANDC__) + BOOST_ASSERT(size <= boost::aligned_storage<64>::size); +#else + BOOST_ASSERT(size <= strand_impl::handler_storage_type::size); +#endif + return impl_->handler_storage_.address(); } - template - friend void asio_handler_dispatch(Handler_To_Dispatch handler, + template + friend void asio_handler_invoke(Function function, invoke_current_handler*) { - handler(); + function(); } private: strand_service& service_impl_; - implementation_type& impl_; + implementation_type impl_; }; // Helper class to automatically enqueue next waiter on block exit. @@ -172,15 +270,15 @@ public: { if (!cancelled_) { - boost::asio::detail::mutex::scoped_lock lock(impl_.mutex_); - impl_.current_handler_ = impl_.first_waiter_; - if (impl_.current_handler_) + boost::asio::detail::mutex::scoped_lock lock(impl_->mutex_); + impl_->current_handler_ = impl_->first_waiter_; + if (impl_->current_handler_) { - impl_.first_waiter_ = impl_.first_waiter_->next_; - if (impl_.first_waiter_ == 0) - impl_.last_waiter_ = 0; + impl_->first_waiter_ = impl_->first_waiter_->next_; + if (impl_->first_waiter_ == 0) + impl_->last_waiter_ = 0; lock.unlock(); - service_impl_.owner().post( + service_impl_.io_service().post( invoke_current_handler(service_impl_, impl_)); } } @@ -236,10 +334,10 @@ public: ptr.reset(); // Indicate that this strand is executing on the current thread. - call_stack::context ctx(&impl); + call_stack::context ctx(impl.get()); // Make the upcall. - boost_asio_handler_dispatch_helpers::dispatch_handler(handler, &handler); + asio_handler_invoke_helpers::invoke(handler, &handler); } static void do_destroy(handler_base* base) @@ -255,7 +353,7 @@ public: Handler handler_; }; - // Construct a new timer service for the specified io_service. + // Construct a new strand service for the specified io_service. explicit strand_service(boost::asio::io_service& io_service) : boost::asio::io_service::service(io_service), mutex_(), @@ -268,7 +366,7 @@ public: { // Construct a list of all handlers to be destroyed. boost::asio::detail::mutex::scoped_lock lock(mutex_); - implementation_type* impl = impl_list_; + strand_impl* impl = impl_list_; handler_base* first_handler = 0; while (impl) { @@ -298,62 +396,29 @@ public: } } - // Construct a new timer implementation. + // Construct a new strand implementation. void construct(implementation_type& impl) { - impl.current_handler_ = 0; - impl.first_waiter_ = 0; - impl.last_waiter_ = 0; - - // Insert implementation into linked list of all implementations. - boost::asio::detail::mutex::scoped_lock lock(mutex_); - impl.next_ = impl_list_; - impl.prev_ = 0; - if (impl_list_) - impl_list_->prev_ = &impl; - impl_list_ = &impl; + impl = implementation_type(new strand_impl(*this)); } - // Destroy a timer implementation. + // Destroy a strand implementation. void destroy(implementation_type& impl) { - if (impl.current_handler_) - { - impl.current_handler_->destroy(); - impl.current_handler_ = 0; - } - - while (impl.first_waiter_) - { - handler_base* next = impl.first_waiter_->next_; - impl.first_waiter_->destroy(); - impl.first_waiter_ = next; - } - impl.last_waiter_ = 0; - - // Remove implementation from linked list of all implementations. - boost::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; + implementation_type().swap(impl); } // Request the io_service to invoke the given handler. template void dispatch(implementation_type& impl, Handler handler) { - if (call_stack::contains(&impl)) + if (call_stack::contains(impl.get())) { - boost_asio_handler_dispatch_helpers::dispatch_handler(handler, &handler); + asio_handler_invoke_helpers::invoke(handler, &handler); } else { - boost::asio::detail::mutex::scoped_lock lock(impl.mutex_); + boost::asio::detail::mutex::scoped_lock lock(impl->mutex_); // Allocate and construct an object to wrap the handler. typedef handler_wrapper value_type; @@ -361,12 +426,12 @@ public: raw_handler_ptr raw_ptr(handler); handler_ptr ptr(raw_ptr, handler); - if (impl.current_handler_ == 0) + if (impl->current_handler_ == 0) { // This handler now has the lock, so can be dispatched immediately. - impl.current_handler_ = ptr.get(); + impl->current_handler_ = ptr.get(); lock.unlock(); - owner().dispatch(invoke_current_handler(*this, impl)); + io_service().dispatch(invoke_current_handler(*this, impl)); ptr.release(); } else @@ -374,15 +439,15 @@ public: // Another handler already holds the lock, so this handler must join // the list of waiters. The handler will be posted automatically when // its turn comes. - if (impl.last_waiter_) + if (impl->last_waiter_) { - impl.last_waiter_->next_ = ptr.get(); - impl.last_waiter_ = impl.last_waiter_->next_; + impl->last_waiter_->next_ = ptr.get(); + impl->last_waiter_ = impl->last_waiter_->next_; } else { - impl.first_waiter_ = ptr.get(); - impl.last_waiter_ = ptr.get(); + impl->first_waiter_ = ptr.get(); + impl->last_waiter_ = ptr.get(); } ptr.release(); } @@ -393,7 +458,7 @@ public: template void post(implementation_type& impl, Handler handler) { - boost::asio::detail::mutex::scoped_lock lock(impl.mutex_); + boost::asio::detail::mutex::scoped_lock lock(impl->mutex_); // Allocate and construct an object to wrap the handler. typedef handler_wrapper value_type; @@ -401,12 +466,12 @@ public: raw_handler_ptr raw_ptr(handler); handler_ptr ptr(raw_ptr, handler); - if (impl.current_handler_ == 0) + if (impl->current_handler_ == 0) { // This handler now has the lock, so can be dispatched immediately. - impl.current_handler_ = ptr.get(); + impl->current_handler_ = ptr.get(); lock.unlock(); - owner().post(invoke_current_handler(*this, impl)); + io_service().post(invoke_current_handler(*this, impl)); ptr.release(); } else @@ -414,15 +479,15 @@ public: // Another handler already holds the lock, so this handler must join the // list of waiters. The handler will be posted automatically when its turn // comes. - if (impl.last_waiter_) + if (impl->last_waiter_) { - impl.last_waiter_->next_ = ptr.get(); - impl.last_waiter_ = impl.last_waiter_->next_; + impl->last_waiter_->next_ = ptr.get(); + impl->last_waiter_ = impl->last_waiter_->next_; } else { - impl.first_waiter_ = ptr.get(); - impl.last_waiter_ = ptr.get(); + impl->first_waiter_ = ptr.get(); + impl->last_waiter_ = ptr.get(); } ptr.release(); } @@ -433,13 +498,33 @@ private: boost::asio::detail::mutex mutex_; // The head of a linked list of all implementations. - implementation_type* impl_list_; + strand_impl* impl_list_; }; } // namespace detail } // namespace asio } // namespace boost +#if defined(__BORLANDC__) + +namespace boost { + +inline void intrusive_ptr_add_ref( + boost::asio::detail::strand_service::strand_impl* p) +{ + p->add_ref(); +} + +inline void intrusive_ptr_release( + boost::asio::detail::strand_service::strand_impl* p) +{ + p->release(); +} + +} // namespace boost + +#endif // defined(__BORLANDC__) + #include #endif // BOOST_ASIO_DETAIL_STRAND_SERVICE_HPP diff --git a/include/boost/asio/detail/task_io_service.hpp b/include/boost/asio/detail/task_io_service.hpp index 280022be..31316750 100644 --- a/include/boost/asio/detail/task_io_service.hpp +++ b/include/boost/asio/detail/task_io_service.hpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include @@ -69,8 +69,8 @@ public: handler_queue_end_ = &task_handler_; } - // Run the event processing loop. - void run() + // Run the event loop until interrupted or no more work. + size_t run() { typename call_stack::context ctx(this); @@ -80,72 +80,49 @@ public: boost::asio::detail::mutex::scoped_lock lock(mutex_); - while (!interrupted_ && outstanding_work_ > 0) - { - if (handler_queue_) - { - // Prepare to execute first handler from queue. - handler_base* h = handler_queue_; - handler_queue_ = h->next_; - if (handler_queue_ == 0) - handler_queue_end_ = 0; - bool more_handlers = (handler_queue_ != 0); - lock.unlock(); + size_t n = 0; + while (do_one(lock, &this_idle_thread)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } - if (h == &task_handler_) - { - task_cleanup c(lock, handler_queue_, - handler_queue_end_, task_handler_); + // Run until interrupted or one operation is performed. + size_t run_one() + { + typename call_stack::context ctx(this); - // Run the task. May throw an exception. Only block if the handler - // queue is empty, otherwise we want to return as soon as possible to - // execute the handlers. - task_.run(!more_handlers); - } - else - { - handler_cleanup c(lock, outstanding_work_); + idle_thread_info this_idle_thread; + this_idle_thread.prev = &this_idle_thread; + this_idle_thread.next = &this_idle_thread; - // Invoke the handler. May throw an exception. - h->call(); // call() deletes the handler object - } - } - else - { - // Nothing to run right now, so just wait for work to do. - if (first_idle_thread_) - { - this_idle_thread.next = first_idle_thread_; - this_idle_thread.prev = first_idle_thread_->prev; - first_idle_thread_->prev->next = &this_idle_thread; - first_idle_thread_->prev = &this_idle_thread; - } - first_idle_thread_ = &this_idle_thread; - this_idle_thread.wakeup_event.clear(); - lock.unlock(); - this_idle_thread.wakeup_event.wait(); - lock.lock(); - if (this_idle_thread.next == &this_idle_thread) - { - first_idle_thread_ = 0; - } - else - { - if (first_idle_thread_ == &this_idle_thread) - first_idle_thread_ = this_idle_thread.next; - this_idle_thread.next->prev = this_idle_thread.prev; - this_idle_thread.prev->next = this_idle_thread.next; - this_idle_thread.next = &this_idle_thread; - this_idle_thread.prev = &this_idle_thread; - } - } - } + boost::asio::detail::mutex::scoped_lock lock(mutex_); - if (!interrupted_) - { - // No more work to do! - interrupt_all_threads(); - } + return do_one(lock, &this_idle_thread); + } + + // Poll for operations without blocking. + size_t poll() + { + typename call_stack::context ctx(this); + + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + size_t n = 0; + while (do_one(lock, 0)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } + + // Poll for one operation without blocking. + size_t poll_one() + { + typename call_stack::context ctx(this); + + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + return do_one(lock, 0); } // Interrupt the event processing loop. @@ -182,7 +159,7 @@ public: void dispatch(Handler handler) { if (call_stack::contains(this)) - boost_asio_handler_dispatch_helpers::dispatch_handler(handler, &handler); + asio_handler_invoke_helpers::invoke(handler, &handler); else post(handler); } @@ -225,6 +202,93 @@ public: } private: + struct idle_thread_info; + + size_t do_one(boost::asio::detail::mutex::scoped_lock& lock, + idle_thread_info* this_idle_thread) + { + if (outstanding_work_ == 0 && !interrupted_) + { + interrupt_all_threads(); + return 0; + } + + bool polling = !this_idle_thread; + bool task_has_run = false; + while (!interrupted_) + { + if (handler_queue_) + { + // Prepare to execute first handler from queue. + handler_base* h = handler_queue_; + handler_queue_ = h->next_; + if (handler_queue_ == 0) + handler_queue_end_ = 0; + bool more_handlers = (handler_queue_ != 0); + lock.unlock(); + + if (h == &task_handler_) + { + // If the task has already run and we're polling then we're done. + if (task_has_run && polling) + return 0; + task_has_run = true; + + task_cleanup c(lock, *this); + + // Run the task. May throw an exception. Only block if the handler + // queue is empty and we have an idle_thread_info object, otherwise + // we want to return as soon as possible. + task_.run(!more_handlers && !polling); + } + else + { + handler_cleanup c(lock, *this); + + // Invoke the handler. May throw an exception. + h->call(); // call() deletes the handler object + + return 1; + } + } + else if (this_idle_thread) + { + // Nothing to run right now, so just wait for work to do. + if (first_idle_thread_) + { + this_idle_thread->next = first_idle_thread_; + this_idle_thread->prev = first_idle_thread_->prev; + first_idle_thread_->prev->next = this_idle_thread; + first_idle_thread_->prev = this_idle_thread; + } + first_idle_thread_ = this_idle_thread; + this_idle_thread->wakeup_event.clear(); + lock.unlock(); + this_idle_thread->wakeup_event.wait(); + lock.lock(); + if (this_idle_thread->next == this_idle_thread) + { + first_idle_thread_ = 0; + } + else + { + if (first_idle_thread_ == this_idle_thread) + first_idle_thread_ = this_idle_thread->next; + this_idle_thread->next->prev = this_idle_thread->prev; + this_idle_thread->prev->next = this_idle_thread->next; + this_idle_thread->next = this_idle_thread; + this_idle_thread->prev = this_idle_thread; + } + } + else + { + return 0; + } + } + + return 0; + } + // Interrupt the task and all idle threads. void interrupt_all_threads() { @@ -332,7 +396,7 @@ private: ptr.reset(); // Make the upcall. - boost_asio_handler_dispatch_helpers::dispatch_handler(handler, &handler); + asio_handler_invoke_helpers::invoke(handler, &handler); } static void do_destroy(handler_base* base) @@ -349,16 +413,15 @@ private: }; // Helper class to perform task-related operations on block exit. + class task_cleanup; + friend class task_cleanup; class task_cleanup { public: task_cleanup(boost::asio::detail::mutex::scoped_lock& lock, - handler_base*& handler_queue, handler_base*& handler_queue_end, - handler_base& task_handler) + task_io_service& task_io_svc) : lock_(lock), - handler_queue_(handler_queue), - handler_queue_end_(handler_queue_end), - task_handler_(task_handler) + task_io_service_(task_io_svc) { } @@ -366,45 +429,50 @@ private: { // Reinsert the task at the end of the handler queue. lock_.lock(); - task_handler_.next_ = 0; - if (handler_queue_end_) + task_io_service_.task_handler_.next_ = 0; + if (task_io_service_.handler_queue_end_) { - handler_queue_end_->next_ = &task_handler_; - handler_queue_end_ = &task_handler_; + task_io_service_.handler_queue_end_->next_ + = &task_io_service_.task_handler_; + task_io_service_.handler_queue_end_ + = &task_io_service_.task_handler_; } else { - handler_queue_ = handler_queue_end_ = &task_handler_; + task_io_service_.handler_queue_ + = task_io_service_.handler_queue_end_ + = &task_io_service_.task_handler_; } } private: boost::asio::detail::mutex::scoped_lock& lock_; - handler_base*& handler_queue_; - handler_base*& handler_queue_end_; - handler_base& task_handler_; + task_io_service& task_io_service_; }; // Helper class to perform handler-related operations on block exit. + class handler_cleanup; + friend class handler_cleanup; class handler_cleanup { public: handler_cleanup(boost::asio::detail::mutex::scoped_lock& lock, - int& outstanding_work) + task_io_service& task_io_svc) : lock_(lock), - outstanding_work_(outstanding_work) + task_io_service_(task_io_svc) { } ~handler_cleanup() { lock_.lock(); - --outstanding_work_; + if (--task_io_service_.outstanding_work_ == 0) + task_io_service_.interrupt_all_threads(); } private: boost::asio::detail::mutex::scoped_lock& lock_; - int& outstanding_work_; + task_io_service& task_io_service_; }; // Mutex to protect access to internal data. diff --git a/include/boost/asio/detail/win_fd_set_adapter.hpp b/include/boost/asio/detail/win_fd_set_adapter.hpp new file mode 100644 index 00000000..b7b32f04 --- /dev/null +++ b/include/boost/asio/detail/win_fd_set_adapter.hpp @@ -0,0 +1,86 @@ +// +// win_fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP +#define BOOST_ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +namespace boost { +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class win_fd_set_adapter +{ +public: + enum { win_fd_set_size = 1024 }; + + win_fd_set_adapter() + : max_descriptor_(invalid_socket) + { + fd_set_.fd_count = 0; + } + + void set(socket_type descriptor) + { + for (u_int i = 0; i < fd_set_.fd_count; ++i) + if (fd_set_.fd_array[i] == descriptor) + return; + if (fd_set_.fd_count < win_fd_set_size) + fd_set_.fd_array[fd_set_.fd_count++] = descriptor; + } + + bool is_set(socket_type descriptor) const + { + return !!__WSAFDIsSet(descriptor, + const_cast(reinterpret_cast(&fd_set_))); + } + + operator fd_set*() + { + return reinterpret_cast(&fd_set_); + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + +private: + // This structure is defined to be compatible with the Windows API fd_set + // structure, but without being dependent on the value of FD_SETSIZE. + struct win_fd_set + { + u_int fd_count; + SOCKET fd_array[win_fd_set_size]; + }; + + win_fd_set fd_set_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include + +#endif // BOOST_ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP diff --git a/include/boost/asio/detail/win_iocp_io_service.hpp b/include/boost/asio/detail/win_iocp_io_service.hpp index 3d81f985..aab47ce6 100644 --- a/include/boost/asio/detail/win_iocp_io_service.hpp +++ b/include/boost/asio/detail/win_iocp_io_service.hpp @@ -22,6 +22,7 @@ #if defined(BOOST_ASIO_HAS_IOCP) #include +#include #include #include @@ -29,7 +30,7 @@ #include #include #include -#include +#include #include #include @@ -74,10 +75,11 @@ public: DWORD_PTR completion_key = 0; #endif LPOVERLAPPED overlapped = 0; - ::GetQueuedCompletionStatus(iocp_.handle, + ::SetLastError(0); + BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, &completion_key, &overlapped, 0); DWORD last_error = ::GetLastError(); - if (last_error == WAIT_TIMEOUT) + if (!ok && overlapped == 0 && last_error == WAIT_TIMEOUT) break; if (overlapped) static_cast(overlapped)->destroy(); @@ -91,79 +93,56 @@ public: ::CreateIoCompletionPort(sock_as_handle, iocp_.handle, 0, 0); } - struct auto_work - { - auto_work(win_iocp_io_service& io_service) - : io_service_(io_service) - { - io_service_.work_started(); - } - - ~auto_work() - { - io_service_.work_finished(); - } - - private: - win_iocp_io_service& io_service_; - }; - - // Run the event processing loop. - void run() + // Run the event loop until interrupted or no more work. + size_t run() { if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) - return; + return 0; call_stack::context ctx(this); - for (;;) - { - // Get the next operation from the queue. - DWORD bytes_transferred = 0; -#if (WINVER < 0x0500) - DWORD completion_key = 0; -#else - DWORD_PTR completion_key = 0; -#endif - LPOVERLAPPED overlapped = 0; - ::SetLastError(0); - ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, - &completion_key, &overlapped, INFINITE); - DWORD last_error = ::GetLastError(); + size_t n = 0; + while (do_one(true)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } - if (overlapped) - { - // We may have been passed a last_error value in the completion_key. - if (last_error == 0) - { - last_error = completion_key; - } + // Run until interrupted or one operation is performed. + size_t run_one() + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + return 0; - // Ensure that the io_service does not exit due to running out of work - // while we make the upcall. - auto_work work(*this); + call_stack::context ctx(this); - // Dispatch the operation. - operation* op = static_cast(overlapped); - op->do_completion(last_error, bytes_transferred); - } - else - { - // The interrupted_ flag is always checked to ensure that any leftover - // interrupts from a previous run invocation are ignored. - if (::InterlockedExchangeAdd(&interrupted_, 0) != 0) - { - // Wake up next thread that is blocked on GetQueuedCompletionStatus. - if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) - { - DWORD last_error = ::GetLastError(); - system_exception e("pqcs", last_error); - boost::throw_exception(e); - } - break; - } - } - } + return do_one(true); + } + + // Poll for operations without blocking. + size_t poll() + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + return 0; + + call_stack::context ctx(this); + + size_t n = 0; + while (do_one(false)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } + + // Poll for one operation without blocking. + size_t poll_one() + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + return 0; + + call_stack::context ctx(this); + + return do_one(false); } // Interrupt the event processing loop. @@ -199,68 +178,12 @@ public: interrupt(); } - template - struct handler_operation - : public operation - { - handler_operation(win_iocp_io_service& io_service, - Handler handler) - : operation(&handler_operation::do_completion_impl, - &handler_operation::destroy_impl), - io_service_(io_service), - handler_(handler) - { - io_service_.work_started(); - } - - ~handler_operation() - { - io_service_.work_finished(); - } - - private: - // Prevent copying and assignment. - handler_operation(const handler_operation&); - void operator=(const handler_operation&); - - static void do_completion_impl(operation* op, DWORD, size_t) - { - // Take ownership of the operation object. - typedef handler_operation op_type; - op_type* handler_op(static_cast(op)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(handler_op->handler_, handler_op); - - // Make a copy of the handler so that the memory can be deallocated before - // the upcall is made. - Handler handler(handler_op->handler_); - - // Free the memory associated with the handler. - ptr.reset(); - - // Make the upcall. - boost_asio_handler_dispatch_helpers::dispatch_handler(handler, &handler); - } - - static void destroy_impl(operation* op) - { - // Take ownership of the operation object. - typedef handler_operation op_type; - op_type* handler_op(static_cast(op)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(handler_op->handler_, handler_op); - } - - win_iocp_io_service& io_service_; - Handler handler_; - }; - // Request invocation of the given handler. template void dispatch(Handler handler) { if (call_stack::contains(this)) - boost_asio_handler_dispatch_helpers::dispatch_handler(handler, &handler); + asio_handler_invoke_helpers::invoke(handler, &handler); else post(handler); } @@ -306,6 +229,140 @@ public: } private: + // Dequeues at most one operation from the I/O completion port, and then + // executes it. Returns the number of operations that were dequeued (i.e. + // either 0 or 1). + size_t do_one(bool block) + { + for (;;) + { + // Get the next operation from the queue. + DWORD bytes_transferred = 0; +#if (WINVER < 0x0500) + DWORD completion_key = 0; +#else + DWORD_PTR completion_key = 0; +#endif + LPOVERLAPPED overlapped = 0; + ::SetLastError(0); + BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, + &completion_key, &overlapped, block ? INFINITE : 0); + DWORD last_error = ::GetLastError(); + + if (!ok && overlapped == 0) + return 0; + + if (overlapped) + { + // We may have been passed a last_error value in the completion_key. + if (last_error == 0) + { + last_error = completion_key; + } + + // Ensure that the io_service does not exit due to running out of work + // while we make the upcall. + auto_work work(*this); + + // Dispatch the operation. + operation* op = static_cast(overlapped); + op->do_completion(last_error, bytes_transferred); + + return 1; + } + else + { + // The interrupted_ flag is always checked to ensure that any leftover + // interrupts from a previous run invocation are ignored. + if (::InterlockedExchangeAdd(&interrupted_, 0) != 0) + { + // Wake up next thread that is blocked on GetQueuedCompletionStatus. + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + DWORD last_error = ::GetLastError(); + system_exception e("pqcs", last_error); + boost::throw_exception(e); + } + + return 0; + } + } + } + } + + struct auto_work + { + auto_work(win_iocp_io_service& io_service) + : io_service_(io_service) + { + io_service_.work_started(); + } + + ~auto_work() + { + io_service_.work_finished(); + } + + private: + win_iocp_io_service& io_service_; + }; + + template + struct handler_operation + : public operation + { + handler_operation(win_iocp_io_service& io_service, + Handler handler) + : operation(&handler_operation::do_completion_impl, + &handler_operation::destroy_impl), + io_service_(io_service), + handler_(handler) + { + io_service_.work_started(); + } + + ~handler_operation() + { + io_service_.work_finished(); + } + + private: + // Prevent copying and assignment. + handler_operation(const handler_operation&); + void operator=(const handler_operation&); + + static void do_completion_impl(operation* op, DWORD, size_t) + { + // Take ownership of the operation object. + typedef handler_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Make the upcall. + asio_handler_invoke_helpers::invoke(handler, &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef handler_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + win_iocp_io_service& io_service_; + Handler handler_; + }; + // The IO completion port used for queueing operations. struct iocp_holder { diff --git a/include/boost/asio/detail/win_iocp_socket_service.hpp b/include/boost/asio/detail/win_iocp_socket_service.hpp index e8e6ace1..9c1b243e 100644 --- a/include/boost/asio/detail/win_iocp_socket_service.hpp +++ b/include/boost/asio/detail/win_iocp_socket_service.hpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #include @@ -65,7 +65,49 @@ public: typedef boost::weak_ptr weak_cancel_token_type; // The native type of a socket. - typedef socket_type native_type; + class native_type + { + public: + native_type(socket_type s) + : socket_(s), + have_remote_endpoint_(false) + { + } + + native_type(socket_type s, const endpoint_type& ep) + : socket_(s), + have_remote_endpoint_(true), + remote_endpoint_(ep) + { + } + + void operator=(socket_type s) + { + socket_ = s; + have_remote_endpoint_ = false; + remote_endpoint_ = endpoint_type(); + } + + operator socket_type() const + { + return socket_; + } + + bool have_remote_endpoint() const + { + return have_remote_endpoint_; + } + + endpoint_type remote_endpoint() const + { + return remote_endpoint_; + } + + private: + socket_type socket_; + bool have_remote_endpoint_; + endpoint_type remote_endpoint_; + }; // The implementation type of the socket. class implementation_type @@ -87,11 +129,12 @@ public: friend class win_iocp_socket_service; // The native socket representation. - socket_type socket_; + native_type socket_; enum { - enable_connection_aborted = 1 // User wants connection_aborted errors. + enable_connection_aborted = 1, // User wants connection_aborted errors. + user_set_linger = 2 // The user set the linger option. }; // Flags indicating the current state of the socket. @@ -108,6 +151,12 @@ public: // The protocol associated with the socket. protocol_type protocol_; + // The ID of the thread from which it is safe to cancel asynchronous + // operations. 0 means no asynchronous operations have been started yet. + // ~0 means asynchronous operations have been started from more than one + // thread, and cancellation is not supported for the socket. + DWORD safe_cancellation_thread_id_; + // Pointers to adjacent socket implementations in linked list. implementation_type* next_; implementation_type* prev_; @@ -147,6 +196,7 @@ public: { impl.socket_ = invalid_socket; impl.cancel_token_.reset(); + impl.safe_cancellation_thread_id_ = 0; // Insert implementation into linked list of all implementations. boost::asio::detail::mutex::scoped_lock lock(mutex_); @@ -160,7 +210,31 @@ public: // Destroy a socket implementation. void destroy(implementation_type& impl) { - close(impl, boost::asio::ignore_error()); + if (impl.socket_ != invalid_socket) + { + // Check if the reactor was created, in which case we need to close the + // socket on the reactor as well to cancel any operations that might be + // running there. + reactor_type* reactor = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (reactor) + reactor->close_descriptor(impl.socket_); + + if (impl.flags_ & implementation_type::user_set_linger) + { + ::linger opt; + opt.l_onoff = 0; + opt.l_linger = 0; + socket_ops::setsockopt(impl.socket_, + SOL_SOCKET, SO_LINGER, &opt, sizeof(opt)); + } + + socket_ops::close(impl.socket_); + impl.socket_ = invalid_socket; + impl.cancel_token_.reset(); + impl.safe_cancellation_thread_id_ = 0; + } // Remove implementation from linked list of all implementations. boost::asio::detail::mutex::scoped_lock lock(mutex_); @@ -238,6 +312,7 @@ public: { impl.socket_ = invalid_socket; impl.cancel_token_.reset(); + impl.safe_cancellation_thread_id_ = 0; } } @@ -250,6 +325,44 @@ public: return impl.socket_; } + // Cancel all operations associated with the socket. + template + void cancel(implementation_type& impl, Error_Handler error_handler) + { + if (impl.socket_ == invalid_socket) + { + boost::asio::error error(boost::asio::error::bad_descriptor); + error_handler(error); + } + else if (impl.safe_cancellation_thread_id_ == 0) + { + // No operations have been started, so there's nothing to cancel. + error_handler(boost::asio::error(0)); + } + else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) + { + // Asynchronous operations have been started from the current thread only, + // so it is safe to try to cancel them using CancelIo. + socket_type sock = impl.socket_; + HANDLE sock_as_handle = reinterpret_cast(sock); + if (!::CancelIo(sock_as_handle)) + { + DWORD last_error = ::GetLastError(); + error_handler(boost::asio::error(last_error)); + } + else + { + error_handler(boost::asio::error(0)); + } + } + else + { + // Asynchronous operations have been started from more than one thread, + // so cancellation is not safe. + error_handler(boost::asio::error(boost::asio::error::not_supported)); + } + } + // Bind the socket to the specified local endpoint. template void bind(implementation_type& impl, const endpoint_type& endpoint, @@ -267,9 +380,6 @@ public: void listen(implementation_type& impl, int backlog, Error_Handler error_handler) { - if (backlog == 0) - backlog = SOMAXCONN; - if (socket_ops::listen(impl.socket_, backlog) == socket_error_retval) error_handler(boost::asio::error(socket_ops::get_error())); else @@ -299,6 +409,12 @@ public: } else { + if (option.level(impl.protocol_) == SOL_SOCKET + && option.name(impl.protocol_) == SO_LINGER) + { + impl.flags_ |= implementation_type::user_set_linger; + } + if (socket_ops::setsockopt(impl.socket_, option.level(impl.protocol_), option.name(impl.protocol_), option.data(impl.protocol_), option.size(impl.protocol_))) @@ -375,15 +491,38 @@ public: void get_remote_endpoint(const implementation_type& impl, endpoint_type& endpoint, Error_Handler error_handler) const { - socket_addr_len_type addr_len = endpoint.capacity(); - if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len)) + if (impl.socket_.have_remote_endpoint()) { - error_handler(boost::asio::error(socket_ops::get_error())); - return; - } + // Check if socket is still connected. + DWORD connect_time = 0; + size_t connect_time_len = sizeof(connect_time); + if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_CONNECT_TIME, + &connect_time, &connect_time_len) == socket_error_retval) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return; + } + if (connect_time == 0xFFFFFFFF) + { + error_handler(boost::asio::error(boost::asio::error::not_connected)); + return; + } - endpoint.resize(addr_len); - error_handler(boost::asio::error(0)); + endpoint = impl.socket_.remote_endpoint(); + error_handler(boost::asio::error(0)); + } + else + { + socket_addr_len_type addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len)) + { + error_handler(boost::asio::error(socket_ops::get_error())); + return; + } + + endpoint.resize(addr_len); + error_handler(boost::asio::error(0)); + } } /// Disable sends or receives on the socket. @@ -407,12 +546,21 @@ public: typename Const_Buffers::const_iterator iter = buffers.begin(); typename Const_Buffers::const_iterator end = buffers.end(); DWORD i = 0; + size_t total_buffer_size = 0; for (; iter != end && i < max_buffers; ++iter, ++i) { boost::asio::const_buffer buffer(*iter); bufs[i].len = static_cast(boost::asio::buffer_size(buffer)); bufs[i].buf = const_cast( boost::asio::buffer_cast(buffer)); + total_buffer_size += boost::asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + error_handler(boost::asio::error(0)); + return 0; } // Send the data. @@ -460,6 +608,20 @@ public: typedef handler_alloc_traits alloc_traits; handler_ptr ptr(handler_op->handler_, handler_op); +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + typename Const_Buffers::const_iterator iter + = handler_op->buffers_.begin(); + typename Const_Buffers::const_iterator end + = handler_op->buffers_.end(); + while (iter != end) + { + boost::asio::const_buffer buffer(*iter); + boost::asio::buffer_cast(buffer); + ++iter; + } +#endif // defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Map ERROR_NETNAME_DELETED to more useful error. if (last_error == ERROR_NETNAME_DELETED) { @@ -478,7 +640,7 @@ public: // Call the handler. boost::asio::error error(last_error); - boost_asio_handler_dispatch_helpers::dispatch_handler( + asio_handler_invoke_helpers::invoke( detail::bind_handler(handler, error, bytes_transferred), &handler); } @@ -503,24 +665,41 @@ public: void async_send(implementation_type& impl, const Const_Buffers& buffers, socket_base::message_flags flags, Handler handler) { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + // Allocate and construct an operation to wrap the handler. typedef send_operation value_type; typedef handler_alloc_traits alloc_traits; raw_handler_ptr raw_ptr(handler); handler_ptr ptr(raw_ptr, - owner(), impl.cancel_token_, buffers, handler); + io_service(), impl.cancel_token_, buffers, handler); // Copy buffers into WSABUF array. ::WSABUF bufs[max_buffers]; typename Const_Buffers::const_iterator iter = buffers.begin(); typename Const_Buffers::const_iterator end = buffers.end(); DWORD i = 0; + size_t total_buffer_size = 0; for (; iter != end && i < max_buffers; ++iter, ++i) { boost::asio::const_buffer buffer(*iter); bufs[i].len = static_cast(boost::asio::buffer_size(buffer)); bufs[i].buf = const_cast( boost::asio::buffer_cast(buffer)); + total_buffer_size += boost::asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + ptr.reset(); + boost::asio::error error(boost::asio::error::success); + iocp_service_.post(bind_handler(handler, error, 0)); + return; } // Send the data. @@ -603,6 +782,20 @@ public: typedef handler_alloc_traits alloc_traits; handler_ptr ptr(handler_op->handler_, handler_op); +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + typename Const_Buffers::const_iterator iter + = handler_op->buffers_.begin(); + typename Const_Buffers::const_iterator end + = handler_op->buffers_.end(); + while (iter != end) + { + boost::asio::const_buffer buffer(*iter); + boost::asio::buffer_cast(buffer); + ++iter; + } +#endif // defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Make a copy of the handler so that the memory can be deallocated before // the upcall is made. Handler handler(handler_op->handler_); @@ -612,7 +805,7 @@ public: // Call the handler. boost::asio::error error(last_error); - boost_asio_handler_dispatch_helpers::dispatch_handler( + asio_handler_invoke_helpers::invoke( detail::bind_handler(handler, error, bytes_transferred), &handler); } @@ -637,11 +830,17 @@ public: const endpoint_type& destination, socket_base::message_flags flags, Handler handler) { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + // Allocate and construct an operation to wrap the handler. typedef send_to_operation value_type; typedef handler_alloc_traits alloc_traits; raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, owner(), buffers, handler); + handler_ptr ptr(raw_ptr, io_service(), buffers, handler); // Copy buffers into WSABUF array. ::WSABUF bufs[max_buffers]; @@ -685,11 +884,20 @@ public: typename Mutable_Buffers::const_iterator iter = buffers.begin(); typename Mutable_Buffers::const_iterator end = buffers.end(); DWORD i = 0; + size_t total_buffer_size = 0; for (; iter != end && i < max_buffers; ++iter, ++i) { boost::asio::mutable_buffer buffer(*iter); bufs[i].len = static_cast(boost::asio::buffer_size(buffer)); bufs[i].buf = boost::asio::buffer_cast(buffer); + total_buffer_size += boost::asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + error_handler(boost::asio::error(0)); + return 0; } // Receive some data. @@ -743,6 +951,20 @@ public: typedef handler_alloc_traits alloc_traits; handler_ptr ptr(handler_op->handler_, handler_op); +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + typename Mutable_Buffers::const_iterator iter + = handler_op->buffers_.begin(); + typename Mutable_Buffers::const_iterator end + = handler_op->buffers_.end(); + while (iter != end) + { + boost::asio::mutable_buffer buffer(*iter); + boost::asio::buffer_cast(buffer); + ++iter; + } +#endif // defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Map ERROR_NETNAME_DELETED to more useful error. if (last_error == ERROR_NETNAME_DELETED) { @@ -767,7 +989,7 @@ public: // Call the handler. boost::asio::error error(last_error); - boost_asio_handler_dispatch_helpers::dispatch_handler( + asio_handler_invoke_helpers::invoke( detail::bind_handler(handler, error, bytes_transferred), &handler); } @@ -792,23 +1014,40 @@ public: void async_receive(implementation_type& impl, const Mutable_Buffers& buffers, socket_base::message_flags flags, Handler handler) { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + // 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); handler_ptr ptr(raw_ptr, - owner(), impl.cancel_token_, buffers, handler); + io_service(), impl.cancel_token_, buffers, handler); // Copy buffers into WSABUF array. ::WSABUF bufs[max_buffers]; typename Mutable_Buffers::const_iterator iter = buffers.begin(); typename Mutable_Buffers::const_iterator end = buffers.end(); DWORD i = 0; + size_t total_buffer_size = 0; for (; iter != end && i < max_buffers; ++iter, ++i) { boost::asio::mutable_buffer buffer(*iter); bufs[i].len = static_cast(boost::asio::buffer_size(buffer)); bufs[i].buf = boost::asio::buffer_cast(buffer); + total_buffer_size += boost::asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + ptr.reset(); + boost::asio::error error(boost::asio::error::success); + iocp_service_.post(bind_handler(handler, error, 0)); + return; } // Receive some data. @@ -906,6 +1145,20 @@ public: typedef handler_alloc_traits alloc_traits; handler_ptr ptr(handler_op->handler_, handler_op); +#if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + typename Mutable_Buffers::const_iterator iter + = handler_op->buffers_.begin(); + typename Mutable_Buffers::const_iterator end + = handler_op->buffers_.end(); + while (iter != end) + { + boost::asio::mutable_buffer buffer(*iter); + boost::asio::buffer_cast(buffer); + ++iter; + } +#endif // defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + // Check for connection closed. if (last_error == 0 && bytes_transferred == 0) { @@ -924,7 +1177,7 @@ public: // Call the handler. boost::asio::error error(last_error); - boost_asio_handler_dispatch_helpers::dispatch_handler( + asio_handler_invoke_helpers::invoke( detail::bind_handler(handler, error, bytes_transferred), &handler); } @@ -952,12 +1205,18 @@ public: const Mutable_Buffers& buffers, endpoint_type& sender_endp, socket_base::message_flags flags, Handler handler) { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + // Allocate and construct an operation to wrap the handler. typedef receive_from_operation value_type; typedef handler_alloc_traits alloc_traits; raw_handler_ptr raw_ptr(handler); handler_ptr ptr(raw_ptr, - owner(), sender_endp, buffers, handler); + io_service(), sender_endp, buffers, handler); // Copy buffers into WSABUF array. ::WSABUF bufs[max_buffers]; @@ -1104,7 +1363,7 @@ public: new_socket_(new_socket), peer_(peer), protocol_(protocol), - work_(io_service.owner()), + work_(io_service.io_service()), enable_connection_aborted_(enable_connection_aborted), handler_(handler) { @@ -1194,13 +1453,37 @@ public: } } + // Get the address of the peer. + endpoint_type peer_endpoint; + if (last_error == 0) + { + LPSOCKADDR local_addr = 0; + int local_addr_length = 0; + LPSOCKADDR remote_addr = 0; + int remote_addr_length = 0; + GetAcceptExSockaddrs(handler_op->output_buffer(), 0, + handler_op->address_length(), handler_op->address_length(), + &local_addr, &local_addr_length, &remote_addr, &remote_addr_length); + if (remote_addr_length > peer_endpoint.capacity()) + { + last_error = boost::asio::error::invalid_argument; + } + else + { + using namespace std; // For memcpy. + memcpy(peer_endpoint.data(), remote_addr, remote_addr_length); + peer_endpoint.resize(remote_addr_length); + } + } + // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname // and getpeername will work on the accepted socket. if (last_error == 0) { - DWORD update_ctx_param = handler_op->socket_; - if (socket_ops::setsockopt(handler_op->new_socket_.get(), SOL_SOCKET, - SO_UPDATE_ACCEPT_CONTEXT, &update_ctx_param, sizeof(DWORD)) != 0) + SOCKET update_ctx_param = handler_op->socket_; + if (socket_ops::setsockopt(handler_op->new_socket_.get(), + SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + &update_ctx_param, sizeof(SOCKET)) != 0) { last_error = socket_ops::get_error(); } @@ -1212,7 +1495,7 @@ public: { boost::asio::error temp_error; handler_op->peer_.assign(handler_op->protocol_, - handler_op->new_socket_.get(), + native_type(handler_op->new_socket_.get(), peer_endpoint), boost::asio::assign_error(temp_error)); if (temp_error) last_error = temp_error.code(); @@ -1229,7 +1512,7 @@ public: // Call the handler. boost::asio::error error(last_error); - boost_asio_handler_dispatch_helpers::dispatch_handler( + asio_handler_invoke_helpers::invoke( detail::bind_handler(handler, error), &handler); } @@ -1258,11 +1541,17 @@ public: template void async_accept(implementation_type& impl, Socket& peer, Handler handler) { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + // Check whether acceptor has been initialised. if (impl.socket_ == invalid_socket) { boost::asio::error error(boost::asio::error::bad_descriptor); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } @@ -1270,7 +1559,7 @@ public: if (peer.native() != invalid_socket) { boost::asio::error error(boost::asio::error::already_connected); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } @@ -1280,7 +1569,7 @@ public: if (sock.get() == invalid_socket) { boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } @@ -1347,7 +1636,7 @@ public: peer_(peer), protocol_(protocol), peer_endpoint_(peer_endpoint), - work_(io_service.owner()), + work_(io_service.io_service()), enable_connection_aborted_(enable_connection_aborted), handler_(handler) { @@ -1453,10 +1742,10 @@ public: } else { - handler_op->peer_endpoint_.resize(remote_addr_length); using namespace std; // For memcpy. memcpy(handler_op->peer_endpoint_.data(), remote_addr, remote_addr_length); + handler_op->peer_endpoint_.resize(remote_addr_length); } } @@ -1464,9 +1753,10 @@ public: // and getpeername will work on the accepted socket. if (last_error == 0) { - DWORD update_ctx_param = handler_op->socket_; - if (socket_ops::setsockopt(handler_op->new_socket_.get(), SOL_SOCKET, - SO_UPDATE_ACCEPT_CONTEXT, &update_ctx_param, sizeof(DWORD)) != 0) + SOCKET update_ctx_param = handler_op->socket_; + if (socket_ops::setsockopt(handler_op->new_socket_.get(), + SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + &update_ctx_param, sizeof(SOCKET)) != 0) { last_error = socket_ops::get_error(); } @@ -1478,7 +1768,8 @@ public: { boost::asio::error temp_error; handler_op->peer_.assign(handler_op->peer_endpoint_.protocol(), - handler_op->new_socket_.get(), + native_type(handler_op->new_socket_.get(), + handler_op->peer_endpoint_), boost::asio::assign_error(temp_error)); if (temp_error) last_error = temp_error.code(); @@ -1495,7 +1786,7 @@ public: // Call the handler. boost::asio::error error(last_error); - boost_asio_handler_dispatch_helpers::dispatch_handler( + asio_handler_invoke_helpers::invoke( detail::bind_handler(handler, error), &handler); } @@ -1526,11 +1817,17 @@ public: void async_accept_endpoint(implementation_type& impl, Socket& peer, endpoint_type& peer_endpoint, Handler handler) { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + // Check whether acceptor has been initialised. if (impl.socket_ == invalid_socket) { boost::asio::error error(boost::asio::error::bad_descriptor); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } @@ -1538,7 +1835,7 @@ public: if (peer.native() != invalid_socket) { boost::asio::error error(boost::asio::error::already_connected); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } @@ -1548,7 +1845,7 @@ public: if (sock.get() == invalid_socket) { boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } @@ -1713,13 +2010,19 @@ public: void async_connect(implementation_type& impl, const endpoint_type& peer_endpoint, Handler handler) { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + // 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 = &(boost::asio::use_service(owner())); + reactor = &(boost::asio::use_service(io_service())); interlocked_exchange_pointer( reinterpret_cast(&reactor_), reactor); } @@ -1737,7 +2040,7 @@ public: if (impl.socket_ == invalid_socket) { boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } iocp_service_.register_socket(impl.socket_); @@ -1749,7 +2052,7 @@ public: if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) { boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); return; } @@ -1760,7 +2063,7 @@ public: // The connect operation has finished successfully so we need to post the // handler immediately. boost::asio::error error(boost::asio::error::success); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); } else if (socket_ops::get_error() == boost::asio::error::in_progress || socket_ops::get_error() == boost::asio::error::would_block) @@ -1770,13 +2073,13 @@ public: boost::shared_ptr completed(new bool(false)); reactor->start_write_and_except_ops(impl.socket_, connect_handler( - impl.socket_, completed, owner(), *reactor, handler)); + impl.socket_, completed, io_service(), *reactor, handler)); } else { // The connect operation has failed, so post the handler immediately. boost::asio::error error(socket_ops::get_error()); - owner().post(bind_handler(handler, error)); + io_service().post(bind_handler(handler, error)); } } diff --git a/include/boost/asio/detail/wrapped_handler.hpp b/include/boost/asio/detail/wrapped_handler.hpp index 30184199..a151b78b 100644 --- a/include/boost/asio/detail/wrapped_handler.hpp +++ b/include/boost/asio/detail/wrapped_handler.hpp @@ -19,7 +19,7 @@ #include #include -#include +#include namespace boost { namespace asio { @@ -163,21 +163,21 @@ public: Context context_; }; -template -inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, +template +inline void asio_handler_invoke(const Function& function, wrapped_handler* this_handler) { this_handler->dispatcher_.dispatch( - rewrapped_handler( - handler, this_handler->handler_)); + rewrapped_handler( + function, this_handler->handler_)); } -template -inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, +template +inline void asio_handler_invoke(const Function& function, rewrapped_handler* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->context_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->context_); } } // namespace detail diff --git a/include/boost/asio/error.hpp b/include/boost/asio/error.hpp index edbdf272..60a0fb7e 100644 --- a/include/boost/asio/error.hpp +++ b/include/boost/asio/error.hpp @@ -30,8 +30,8 @@ #endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) #include +#include #include -#include namespace boost { namespace asio { @@ -233,7 +233,7 @@ public: | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, code_, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); - detail::win_local_free_on_block_exit local_free_obj(msg); + detail::local_free_on_block_exit local_free_obj(msg); if (length && msg[length - 1] == '\n') msg[--length] = '\0'; if (length && msg[length - 1] == '\r') @@ -273,7 +273,8 @@ public: default: #if defined(__sun) || defined(__QNX__) return strerror(code_); -#elif defined(__MACH__) && defined(__APPLE__) || defined(__NetBSD__) +#elif defined(__MACH__) && defined(__APPLE__) \ + || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) try { char buf[256] = ""; diff --git a/include/boost/asio/handler_alloc_hook.hpp b/include/boost/asio/handler_alloc_hook.hpp index 1864b383..8a68c891 100644 --- a/include/boost/asio/handler_alloc_hook.hpp +++ b/include/boost/asio/handler_alloc_hook.hpp @@ -79,7 +79,7 @@ inline void* asio_handler_allocate(std::size_t size, ...) inline void asio_handler_deallocate(void* pointer, std::size_t size, ...) { (void)(size); - return ::operator delete(pointer); + ::operator delete(pointer); } } // namespace asio diff --git a/include/boost/asio/handler_dispatch_hook.hpp b/include/boost/asio/handler_invoke_hook.hpp similarity index 63% rename from include/boost/asio/handler_dispatch_hook.hpp rename to include/boost/asio/handler_invoke_hook.hpp index d9d3fd48..e6cdc296 100644 --- a/include/boost/asio/handler_dispatch_hook.hpp +++ b/include/boost/asio/handler_invoke_hook.hpp @@ -1,6 +1,6 @@ // -// handler_dispatch_hook.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// handler_invoke_hook.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) // @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_ASIO_HANDLER_DISPATCH_HOOK_HPP -#define BOOST_ASIO_HANDLER_DISPATCH_HOOK_HPP +#ifndef BOOST_ASIO_HANDLER_INVOKE_HOOK_HPP +#define BOOST_ASIO_HANDLER_INVOKE_HOOK_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once @@ -20,47 +20,47 @@ namespace boost { namespace asio { -/// Default dispatch function for handlers. +/// Default invoke function for handlers. /** * Completion handlers for asynchronous operations are invoked by the * io_service associated with the corresponding object (e.g. a socket or * deadline_timer). Certain guarantees are made on when the handler may be * invoked, in particular that a handler can only be invoked from a thread that * is currently calling boost::asio::io_service::run() on the corresponding - * io_service object. Handlers may subsequently be dispatched through other + * io_service object. Handlers may subsequently be invoked through other * objects (such as boost::asio::strand objects) that provide additional * guarantees. * * When asynchronous operations are composed from other asynchronous - * operations, all intermediate handlers should be dispatched using the same + * operations, all intermediate handlers should be invoked using the same * method as the final handler. This is required to ensure that user-defined * objects are not accessed in a way that may violate the guarantees. This - * hooking function ensures that the dispatch method used for the final handler + * hooking function ensures that the invoked method used for the final handler * is accessible at each intermediate step. * - * Implement asio_handler_dispatch for your own handlers to specify a custom - * dispatching strategy. + * Implement asio_handler_invoke for your own handlers to specify a custom + * invocation strategy. * * This default implementation is simply: * @code - * handler(); + * function(); * @endcode * * @par Example: * @code * class my_handler; * - * template - * void asio_handler_dispatch(Handler handler, my_handler* context) + * template + * void asio_handler_invoke(Function function, my_handler* context) * { - * context->strand_.dispatch(handler); + * context->strand_.dispatch(function); * } * @endcode */ -template -inline void asio_handler_dispatch(Handler handler, ...) +template +inline void asio_handler_invoke(Function function, ...) { - handler(); + function(); } } // namespace asio @@ -68,4 +68,4 @@ inline void asio_handler_dispatch(Handler handler, ...) #include -#endif // BOOST_ASIO_HANDLER_DISPATCH_HOOK_HPP +#endif // BOOST_ASIO_HANDLER_INVOKE_HOOK_HPP diff --git a/include/boost/asio/impl/io_service.ipp b/include/boost/asio/impl/io_service.ipp index a1c1e809..185ac50b 100644 --- a/include/boost/asio/impl/io_service.ipp +++ b/include/boost/asio/impl/io_service.ipp @@ -32,9 +32,24 @@ inline io_service::io_service() { } -inline void io_service::run() +inline size_t io_service::run() { - impl_.run(); + return impl_.run(); +} + +inline size_t io_service::run_one() +{ + return impl_.run_one(); +} + +inline size_t io_service::poll() +{ + return impl_.poll(); +} + +inline size_t io_service::poll_one() +{ + return impl_.poll_one(); } inline void io_service::interrupt() @@ -70,24 +85,29 @@ io_service::wrap(Handler handler) return detail::wrapped_handler(*this, handler); } -inline io_service::work::work(io_service& io_service) - : impl_(io_service.impl_) +inline io_service::work::work(boost::asio::io_service& io_service) + : io_service_(io_service) { - impl_.work_started(); + io_service_.impl_.work_started(); } inline io_service::work::work(const work& other) - : impl_(other.impl_) + : io_service_(other.io_service_) { - impl_.work_started(); + io_service_.impl_.work_started(); } inline io_service::work::~work() { - impl_.work_finished(); + io_service_.impl_.work_finished(); } -inline io_service::service::service(io_service& owner) +inline boost::asio::io_service& io_service::work::io_service() +{ + return io_service_; +} + +inline io_service::service::service(boost::asio::io_service& owner) : owner_(owner), type_info_(0), next_(0) @@ -98,7 +118,7 @@ inline io_service::service::~service() { } -inline io_service& io_service::service::owner() +inline boost::asio::io_service& io_service::service::io_service() { return owner_; } @@ -112,7 +132,7 @@ inline Service& use_service(io_service& ios) template void add_service(io_service& ios, Service* svc) { - if (&ios != &svc->owner()) + if (&ios != &svc->io_service()) boost::throw_exception(invalid_service_owner()); if (!ios.service_registry_.template add_service(svc)) boost::throw_exception(service_already_exists()); diff --git a/include/boost/asio/impl/read.ipp b/include/boost/asio/impl/read.ipp index f1c86891..cfcda8be 100644 --- a/include/boost/asio/impl/read.ipp +++ b/include/boost/asio/impl/read.ipp @@ -23,7 +23,7 @@ #include #include #include -#include +#include namespace boost { namespace asio { @@ -167,14 +167,14 @@ namespace detail pointer, size, &this_handler->handler_); } - template - inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, + inline void asio_handler_invoke(const Function& function, read_handler* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->handler_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); } } // namespace detail @@ -258,14 +258,14 @@ namespace detail pointer, size, &this_handler->handler_); } - template - inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, + inline void asio_handler_invoke(const Function& function, read_streambuf_handler* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->handler_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); } } // namespace detail diff --git a/include/boost/asio/impl/read_until.ipp b/include/boost/asio/impl/read_until.ipp index e97532cf..d48ad959 100644 --- a/include/boost/asio/impl/read_until.ipp +++ b/include/boost/asio/impl/read_until.ipp @@ -29,7 +29,7 @@ #include #include #include -#include +#include namespace boost { namespace asio { @@ -312,14 +312,14 @@ namespace detail pointer, size, &this_handler->handler_); } - template - inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, + template + inline void asio_handler_invoke(const Function& function, read_until_delim_handler* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->handler_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); } } // namespace detail @@ -448,14 +448,14 @@ namespace detail pointer, size, &this_handler->handler_); } - template - inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, + inline void asio_handler_invoke(const Function& function, read_until_delim_string_handler* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->handler_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); } } // namespace detail @@ -601,14 +601,14 @@ namespace detail pointer, size, &this_handler->handler_); } - template - inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, + template + inline void asio_handler_invoke(const Function& function, read_until_expr_handler* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->handler_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); } } // namespace detail diff --git a/include/boost/asio/impl/write.ipp b/include/boost/asio/impl/write.ipp index 038244ab..0edb64c4 100644 --- a/include/boost/asio/impl/write.ipp +++ b/include/boost/asio/impl/write.ipp @@ -23,7 +23,7 @@ #include #include #include -#include +#include namespace boost { namespace asio { @@ -159,14 +159,14 @@ namespace detail pointer, size, &this_handler->handler_); } - template - inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, + inline void asio_handler_invoke(const Function& function, write_handler* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->handler_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); } } // namespace detail @@ -231,14 +231,14 @@ namespace detail pointer, size, &this_handler->handler_); } - template - inline void asio_handler_dispatch(const Handler_To_Dispatch& handler, + template + inline void asio_handler_invoke(const Function& function, write_streambuf_handler* this_handler) { - boost_asio_handler_dispatch_helpers::dispatch_handler( - handler, &this_handler->handler_); + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); } } // namespace detail diff --git a/include/boost/asio/io_service.hpp b/include/boost/asio/io_service.hpp index 08386d3f..a3cfaffd 100644 --- a/include/boost/asio/io_service.hpp +++ b/include/boost/asio/io_service.hpp @@ -83,6 +83,8 @@ public: class service; + class strand; + /// Default constructor. io_service(); @@ -97,14 +99,44 @@ public: * * The run() function may be safely called again once it has completed only * after a call to reset(). + * + * @return The number of handlers that were executed. */ - void run(); + size_t run(); + + /// Run the io_service's event processing loop to execute at most one handler. + /** + * The run_one() function blocks until one handler has been dispatched, or + * until the io_service has been interrupted. + * + * @return The number of handlers that were executed. + */ + size_t run_one(); + + /// Run the io_service's event processing loop to execute ready handlers. + /** + * The poll() function runs handlers that are ready to run, without blocking, + * until the io_service has been interrupted or there are no more ready + * handlers. + * + * @return The number of handlers that were executed. + */ + size_t poll(); + + /// Run the io_service's event processing loop to execute one ready handler. + /** + * The poll_one() function runs at most one handler that is ready to run, + * without blocking. + * + * @return The number of handlers that were executed. + */ + size_t poll_one(); /// Interrupt the io_service's event processing loop. /** * This function does not block, but instead simply signals to the io_service - * that all invocations of its run() member function should return as soon as - * possible. + * that all invocations of its run() or run_one() member functions should + * return as soon as possible. * * Note that if the run() function is interrupted and is not called again * later then its work may not have finished and handlers may not be @@ -117,11 +149,12 @@ public: /// Reset the io_service in preparation for a subsequent run() invocation. /** * This function must be called prior to any second or later set of - * invocations of the run() function. It allows the io_service to reset any - * internal state, such as an interrupt flag. + * invocations of the run(), run_one(), poll() or poll_one() functions. It + * allows the io_service to reset any internal state, such as an interrupt + * flag. * * This function must not be called while there are any unfinished calls to - * the run() function. + * the run(), run_one(), poll() or poll_one() functions. */ void reset(); @@ -130,8 +163,9 @@ public: * This function is used to ask the io_service to execute the given handler. * * The io_service guarantees that the handler will only be called in a thread - * in which the run() member function is currently being invoked. The handler - * may be executed inside this function if the guarantee can be met. + * in which the run(), run_one(), poll() or poll_one() member functions is + * currently being invoked. The handler may be executed inside this function + * if the guarantee can be met. * * @param handler The handler to be called. The io_service will make * a copy of the handler object as required. The function signature of the @@ -147,7 +181,8 @@ public: * function. * * The io_service guarantees that the handler will only be called in a thread - * in which the run() member function is currently being invoked. + * in which the run(), run_one(), poll() or poll_one() member functions is + * currently being invoked. * * @param handler The handler to be called. The io_service will make * a copy of the handler object as required. The function signature of the @@ -265,7 +300,7 @@ public: * This ensures that the io_service's run() function will not exit while the * work is underway. */ - explicit work(io_service& io_service); + explicit work(boost::asio::io_service& io_service); /// Copy constructor notifies the io_service that work is starting. /** @@ -283,12 +318,15 @@ public: */ ~work(); + /// Get the io_service associated with the work. + boost::asio::io_service& io_service(); + private: // Prevent assignment. void operator=(const work& other); - // The io_service's implementation. - io_service::impl_type& impl_; + // The io_service. + boost::asio::io_service& io_service_; }; /// Base class for all io_service services. @@ -297,14 +335,14 @@ class io_service::service { public: /// Get the io_service object that owns the service. - io_service& owner(); + boost::asio::io_service& io_service(); protected: /// Constructor. /** * @param owner The io_service object that owns the service. */ - service(io_service& owner); + service(boost::asio::io_service& owner); /// Destructor. virtual ~service(); @@ -313,8 +351,8 @@ private: /// Destroy all user-defined handler objects owned by the service. virtual void shutdown_service() = 0; - friend class detail::service_registry; - io_service& owner_; + friend class detail::service_registry; + boost::asio::io_service& owner_; const std::type_info* type_info_; service* next_; }; @@ -347,15 +385,17 @@ public: * * If an exception is thrown from a handler, the exception is allowed to * propagate through the throwing thread's invocation of - * boost::asio::io_service::run(). No other threads that are calling - * boost::asio::io_service::run() are affected. It is then the responsibility of - * the application to catch the exception. + * boost::asio::io_service::run(), boost::asio::io_service::run_one(), + * boost::asio::io_service::poll() or boost::asio::io_service::poll_one(). + * No other threads that are calling any of these functions are affected. It is + * then the responsibility of the application to catch the exception. * - * After the exception has been caught, the boost::asio::io_service::run() call - * may be restarted @em without the need for an intervening call to + * After the exception has been caught, the + * boost::asio::io_service::run(), boost::asio::io_service::run_one(), + * boost::asio::io_service::poll() or boost::asio::io_service::poll_one() + * call may be restarted @em without the need for an intervening call to * boost::asio::io_service::reset(). This allows the thread to rejoin the - * io_service's thread pool without impacting any other threads in the - * pool. + * io_service's thread pool without impacting any other threads in the pool. * * @par Example: * @code diff --git a/include/boost/asio/ip/address_v4.hpp b/include/boost/asio/ip/address_v4.hpp index 4061ba90..bc683a08 100644 --- a/include/boost/asio/ip/address_v4.hpp +++ b/include/boost/asio/ip/address_v4.hpp @@ -79,13 +79,6 @@ public: return *this; } - /// Assign from an unsigned long. - address_v4& operator=(unsigned long addr) - { - addr_.s_addr = boost::asio::detail::socket_ops::host_to_network_long(addr); - return *this; - } - /// Get the address in bytes. bytes_type to_bytes() const { @@ -164,29 +157,23 @@ public: } /// Determine whether the address is a class A address. - bool is_class_A() const + bool is_class_a() const { return IN_CLASSA(to_ulong()); } /// Determine whether the address is a class B address. - bool is_class_B() const + bool is_class_b() const { return IN_CLASSB(to_ulong()); } /// Determine whether the address is a class C address. - bool is_class_C() const + bool is_class_c() const { return IN_CLASSC(to_ulong()); } - /// Determine whether the address is a class D address. - bool is_class_D() const - { - return IN_CLASSD(to_ulong()); - } - /// Determine whether the address is a multicast address. bool is_multicast() const { @@ -211,6 +198,24 @@ public: return a1.to_ulong() < a2.to_ulong(); } + /// Compare addresses for ordering. + friend bool operator>(const address_v4& a1, const address_v4& a2) + { + return a1.to_ulong() > a2.to_ulong(); + } + + /// Compare addresses for ordering. + friend bool operator<=(const address_v4& a1, const address_v4& a2) + { + return a1.to_ulong() <= a2.to_ulong(); + } + + /// Compare addresses for ordering. + friend bool operator>=(const address_v4& a1, const address_v4& a2) + { + return a1.to_ulong() >= a2.to_ulong(); + } + /// Obtain an address object that represents any address. static address_v4 any() { @@ -229,6 +234,26 @@ public: return address_v4(static_cast(INADDR_BROADCAST)); } + /// Obtain an address object that represents the broadcast address that + /// corresponds to the specified address and netmask. + static address_v4 broadcast(const address_v4& addr, const address_v4& mask) + { + return address_v4(addr.to_ulong() | ~mask.to_ulong()); + } + + /// Obtain the netmask that corresponds to the address, based on its address + /// class. + static address_v4 netmask(const address_v4& addr) + { + if (addr.is_class_a()) + return address_v4(0xFF000000); + if (addr.is_class_b()) + return address_v4(0xFFFF0000); + if (addr.is_class_c()) + return address_v4(0xFFFFFF00); + return address_v4(0xFFFFFFFF); + } + private: // The underlying IPv4 address. boost::asio::detail::in4_addr_type addr_; @@ -250,7 +275,13 @@ template std::basic_ostream& operator<<( std::basic_ostream& os, const address_v4& addr) { - os << addr.to_string(); + boost::asio::error e; + std::string s = addr.to_string(boost::asio::assign_error(e)); + if (e) + os.setstate(std::ios_base::failbit); + else + for (std::string::iterator i = s.begin(); i != s.end(); ++i) + os << os.widen(*i); return os; } diff --git a/include/boost/asio/ip/address_v6.hpp b/include/boost/asio/ip/address_v6.hpp index 2da8a184..31197e70 100644 --- a/include/boost/asio/ip/address_v6.hpp +++ b/include/boost/asio/ip/address_v6.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include namespace boost { namespace asio { @@ -163,18 +165,50 @@ public: return from_string(str.c_str(), error_handler); } + /// Converts an IPv4-mapped or IPv4-compatible address to an IPv4 address. + address_v4 to_v4() const + { + if (!is_v4_mapped() && !is_v4_compatible()) + throw std::bad_cast(); + address_v4::bytes_type v4_bytes = { addr_.s6_addr[12], + addr_.s6_addr[13], addr_.s6_addr[14], addr_.s6_addr[15] }; + return address_v4(v4_bytes); + } + /// Determine whether the address is a loopback address. bool is_loopback() const { +#if defined(__BORLANDC__) + return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) + && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) + && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) + && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) + && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) + && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) + && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0) + && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 1)); +#else using namespace boost::asio::detail; return IN6_IS_ADDR_LOOPBACK(&addr_) != 0; +#endif } /// Determine whether the address is unspecified. bool is_unspecified() const { +#if defined(__BORLANDC__) + return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) + && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) + && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) + && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) + && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) + && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) + && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0) + && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 0)); +#else using namespace boost::asio::detail; return IN6_IS_ADDR_UNSPECIFIED(&addr_) != 0; +#endif } /// Determine whether the address is link local. @@ -192,14 +226,14 @@ public: } /// Determine whether the address is a mapped IPv4 address. - bool is_ipv4_mapped() const + bool is_v4_mapped() const { using namespace boost::asio::detail; return IN6_IS_ADDR_V4MAPPED(&addr_) != 0; } /// Determine whether the address is an IPv4-compatible address. - bool is_ipv4_compatible() const + bool is_v4_compatible() const { using namespace boost::asio::detail; return IN6_IS_ADDR_V4COMPAT(&addr_) != 0; @@ -278,6 +312,24 @@ public: return a1.scope_id_ < a2.scope_id_; } + /// Compare addresses for ordering. + friend bool operator>(const address_v6& a1, const address_v6& a2) + { + return a2 < a1; + } + + /// Compare addresses for ordering. + friend bool operator<=(const address_v6& a1, const address_v6& a2) + { + return !(a2 < a1); + } + + /// Compare addresses for ordering. + friend bool operator>=(const address_v6& a1, const address_v6& a2) + { + return !(a1 < a2); + } + /// Obtain an address object that represents any address. static address_v6 any() { @@ -293,6 +345,24 @@ public: return tmp; } + /// Create an IPv4-mapped IPv6 address. + static address_v6 v4_mapped(const address_v4& addr) + { + address_v4::bytes_type v4_bytes = addr.to_bytes(); + bytes_type v6_bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, + v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] }; + return address_v6(v6_bytes); + } + + /// Create an IPv4-compatible IPv6 address. + static address_v6 v4_compatible(const address_v4& addr) + { + address_v4::bytes_type v4_bytes = addr.to_bytes(); + bytes_type v6_bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] }; + return address_v6(v6_bytes); + } + private: // The underlying IPv6 address. boost::asio::detail::in6_addr_type addr_; @@ -317,7 +387,13 @@ template std::basic_ostream& operator<<( std::basic_ostream& os, const address_v6& addr) { - os << addr.to_string(); + boost::asio::error e; + std::string s = addr.to_string(boost::asio::assign_error(e)); + if (e) + os.setstate(std::ios_base::failbit); + else + for (std::string::iterator i = s.begin(); i != s.end(); ++i) + os << os.widen(*i); return os; } diff --git a/include/boost/asio/ip/basic_endpoint.hpp b/include/boost/asio/ip/basic_endpoint.hpp index 54adcf3a..17f82d27 100644 --- a/include/boost/asio/ip/basic_endpoint.hpp +++ b/include/boost/asio/ip/basic_endpoint.hpp @@ -72,6 +72,7 @@ public: /// Default constructor. basic_endpoint() + : data_() { boost::asio::detail::sockaddr_in4_type& data = reinterpret_cast(data_); @@ -97,6 +98,7 @@ public: * @endcode */ basic_endpoint(const Protocol& protocol, unsigned short port_num) + : data_() { using namespace std; // For memcpy. if (protocol.family() == PF_INET) @@ -126,6 +128,7 @@ public: /// constructor may be used for accepting connections on a specific interface /// or for making a connection to a remote endpoint. basic_endpoint(const boost::asio::ip::address& addr, unsigned short port_num) + : data_() { using namespace std; // For memcpy. if (addr.is_v4()) diff --git a/include/boost/asio/basic_resolver.hpp b/include/boost/asio/ip/basic_resolver.hpp similarity index 97% rename from include/boost/asio/basic_resolver.hpp rename to include/boost/asio/ip/basic_resolver.hpp index 3c321189..60c9326c 100644 --- a/include/boost/asio/basic_resolver.hpp +++ b/include/boost/asio/ip/basic_resolver.hpp @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_ASIO_BASIC_RESOLVER_HPP -#define BOOST_ASIO_BASIC_RESOLVER_HPP +#ifndef BOOST_ASIO_IP_BASIC_RESOLVER_HPP +#define BOOST_ASIO_IP_BASIC_RESOLVER_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once @@ -20,10 +20,11 @@ #include #include #include -#include +#include namespace boost { namespace asio { +namespace ip { /// Provides endpoint resolution functionality. /** @@ -246,9 +247,10 @@ public: } }; +} // namespace ip } // namespace asio } // namespace boost #include -#endif // BOOST_ASIO_BASIC_RESOLVER_HPP +#endif // BOOST_ASIO_IP_BASIC_RESOLVER_HPP diff --git a/include/boost/asio/ip/basic_resolver_iterator.hpp b/include/boost/asio/ip/basic_resolver_iterator.hpp index e5b4330a..a2fbe9b5 100644 --- a/include/boost/asio/ip/basic_resolver_iterator.hpp +++ b/include/boost/asio/ip/basic_resolver_iterator.hpp @@ -80,7 +80,9 @@ public: { using namespace std; // For memcpy. typename Protocol::endpoint endpoint; - endpoint.resize(address_info->ai_addrlen); + endpoint.resize( + static_cast( + address_info->ai_addrlen)); memcpy(endpoint.data(), address_info->ai_addr, address_info->ai_addrlen); iter.values_->push_back( diff --git a/include/boost/asio/resolver_service.hpp b/include/boost/asio/ip/resolver_service.hpp similarity index 93% rename from include/boost/asio/resolver_service.hpp rename to include/boost/asio/ip/resolver_service.hpp index cd7e7544..7c3bcf89 100644 --- a/include/boost/asio/resolver_service.hpp +++ b/include/boost/asio/ip/resolver_service.hpp @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_ASIO_RESOLVER_SERVICE_HPP -#define BOOST_ASIO_RESOLVER_SERVICE_HPP +#ifndef BOOST_ASIO_IP_RESOLVER_SERVICE_HPP +#define BOOST_ASIO_IP_RESOLVER_SERVICE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once @@ -22,6 +22,7 @@ namespace boost { namespace asio { +namespace ip { /// Default service implementation for a resolver. template @@ -43,7 +44,7 @@ public: private: // The type of the platform-specific implementation. - typedef detail::resolver_service service_impl_type; + typedef boost::asio::detail::resolver_service service_impl_type; public: /// The type of a resolver implementation. @@ -120,9 +121,10 @@ private: service_impl_type& service_impl_; }; +} // namespace ip } // namespace asio } // namespace boost #include -#endif // BOOST_ASIO_RESOLVER_SERVICE_HPP +#endif // BOOST_ASIO_IP_RESOLVER_SERVICE_HPP diff --git a/include/boost/asio/ip/tcp.hpp b/include/boost/asio/ip/tcp.hpp index 0cacd1c1..37c609ae 100644 --- a/include/boost/asio/ip/tcp.hpp +++ b/include/boost/asio/ip/tcp.hpp @@ -17,11 +17,11 @@ #include -#include #include #include #include #include +#include #include #include #include diff --git a/include/boost/asio/ip/udp.hpp b/include/boost/asio/ip/udp.hpp index 4f53c0c2..59e2a0ee 100644 --- a/include/boost/asio/ip/udp.hpp +++ b/include/boost/asio/ip/udp.hpp @@ -18,8 +18,8 @@ #include #include -#include #include +#include #include #include #include diff --git a/include/boost/asio/socket_acceptor_service.hpp b/include/boost/asio/socket_acceptor_service.hpp index aaa868d0..f6bab3eb 100644 --- a/include/boost/asio/socket_acceptor_service.hpp +++ b/include/boost/asio/socket_acceptor_service.hpp @@ -110,6 +110,13 @@ public: service_impl_.assign(impl, protocol, native_acceptor, error_handler); } + /// Cancel all asynchronous operations associated with the acceptor. + template + void cancel(implementation_type& impl, Error_Handler error_handler) + { + service_impl_.cancel(impl, error_handler); + } + /// Bind the socket acceptor to the specified local endpoint. template void bind(implementation_type& impl, const endpoint_type& endpoint, diff --git a/include/boost/asio/socket_base.hpp b/include/boost/asio/socket_base.hpp index 611ae838..02e90ed5 100644 --- a/include/boost/asio/socket_base.hpp +++ b/include/boost/asio/socket_base.hpp @@ -430,7 +430,7 @@ public: #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined non_blocking_io; #else - typedef boost::asio::detail::io_control::boolean non_blocking_io; + typedef boost::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 @@ -453,7 +453,14 @@ public: #if defined(GENERATING_DOCUMENTATION) typedef implementation_defined bytes_readable; #else - typedef boost::asio::detail::io_control::size bytes_readable; + typedef boost::asio::detail::io_control::bytes_readable bytes_readable; +#endif + + /// The maximum length of the queue of pending incoming connections. +#if defined(GENERATING_DOCUMENTATION) + static const int max_connections = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, max_connections = SOMAXCONN); #endif protected: diff --git a/include/boost/asio/ssl/basic_context.hpp b/include/boost/asio/ssl/basic_context.hpp index 2998f4a0..76e161af 100644 --- a/include/boost/asio/ssl/basic_context.hpp +++ b/include/boost/asio/ssl/basic_context.hpp @@ -404,6 +404,54 @@ public: service_.use_tmp_dh_file(impl_, filename, error_handler); } + /// Set the password callback. + /** + * This function is used to specify a callback function to obtain password + * information about an encrypted key in PEM format. + * + * @param callback The function object to be used for obtaining the password. + * The function signature of the handler must be: + * @code std::string password_callback( + * std::size_t max_length, // The maximum size for a password. + * password_purpose purpose // Whether password is for reading or writing. + * ); @endcode + * The return value of the callback is a string containing the password. + * + * @throws boost::asio::error Thrown on failure. + */ + template + void set_password_callback(Password_Callback callback) + { + service_.set_password_callback(impl_, callback, throw_error()); + } + + /// Set the password callback. + /** + * This function is used to specify a callback function to obtain password + * information about an encrypted key in PEM format. + * + * @param callback The function object to be used for obtaining the password. + * The function signature of the handler must be: + * @code std::string password_callback( + * std::size_t max_length, // The maximum size for a password. + * password_purpose purpose // Whether password is for reading or writing. + * ); @endcode + * The return value of the callback is a string containing the password. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const boost::asio::error& error // Result of operation + * ); @endcode + */ + template + void set_password_callback(Password_Callback callback, + Error_Handler error_handler) + { + service_.set_password_callback(impl_, callback, error_handler); + } + private: /// The backend service implementation. service_type& service_; diff --git a/include/boost/asio/ssl/context_base.hpp b/include/boost/asio/ssl/context_base.hpp index cb94a76d..7e38eb1b 100644 --- a/include/boost/asio/ssl/context_base.hpp +++ b/include/boost/asio/ssl/context_base.hpp @@ -134,6 +134,16 @@ public: BOOST_STATIC_CONSTANT(int, verify_client_once = SSL_VERIFY_CLIENT_ONCE); #endif + /// Purpose of PEM password. + enum password_purpose + { + /// The password is needed for reading/decryption. + for_reading, + + /// The password is needed for writing/encryption. + for_writing + }; + protected: /// Protected destructor to prevent deletion through this type. ~context_base() diff --git a/include/boost/asio/ssl/context_service.hpp b/include/boost/asio/ssl/context_service.hpp index a9acb0de..6d66fdaa 100644 --- a/include/boost/asio/ssl/context_service.hpp +++ b/include/boost/asio/ssl/context_service.hpp @@ -151,6 +151,14 @@ public: service_impl_.use_tmp_dh_file(impl, filename, error_handler); } + /// Set the password callback. + template + void set_password_callback(impl_type& impl, Password_Callback callback, + Error_Handler error_handler) + { + service_impl_.set_password_callback(impl, callback, error_handler); + } + private: // The service that provides the platform-specific implementation. service_impl_type& service_impl_; diff --git a/include/boost/asio/ssl/detail/openssl_context_service.hpp b/include/boost/asio/ssl/detail/openssl_context_service.hpp index 123cae55..ddad7d1b 100644 --- a/include/boost/asio/ssl/detail/openssl_context_service.hpp +++ b/include/boost/asio/ssl/detail/openssl_context_service.hpp @@ -19,7 +19,9 @@ #include #include +#include #include +#include #include #include @@ -40,6 +42,10 @@ public: // The native type of the context. typedef ::SSL_CTX* impl_type; + // The type for the password callback function object. + typedef boost::function password_callback_type; + // Constructor. openssl_context_service(boost::asio::io_service& io_service) : boost::asio::io_service::service(io_service) @@ -110,6 +116,15 @@ public: { if (impl != null()) { + if (impl->default_passwd_callback_userdata) + { + password_callback_type* callback = + static_cast( + impl->default_passwd_callback_userdata); + delete callback; + impl->default_passwd_callback_userdata = 0; + } + ::SSL_CTX_free(impl); impl = null(); } @@ -322,6 +337,52 @@ public: error_handler(e); } + static int password_callback(char* buf, int size, int purpose, void* data) + { + using namespace std; // For strncat and strlen. + + if (data) + { + password_callback_type* callback = + static_cast(data); + std::string passwd = (*callback)(static_cast(size), + purpose ? context_base::for_writing : context_base::for_reading); + *buf = '\0'; + strncat(buf, passwd.c_str(), size); + return strlen(buf); + } + + return 0; + } + + // Set the password callback. + template + void set_password_callback(impl_type& impl, Password_Callback callback, + Error_Handler error_handler) + { + // Allocate callback function object if not already present. + if (impl->default_passwd_callback_userdata) + { + password_callback_type* callback_function = + static_cast( + impl->default_passwd_callback_userdata); + *callback_function = callback; + } + else + { + password_callback_type* callback_function = + new password_callback_type(callback); + impl->default_passwd_callback_userdata = callback_function; + } + + // Set the password callback. + SSL_CTX_set_default_passwd_cb(impl, + &openssl_context_service::password_callback); + + boost::asio::error e; + error_handler(e); + } + private: // Ensure openssl is initialised. openssl_init<> init_; diff --git a/include/boost/asio/ssl/detail/openssl_init.hpp b/include/boost/asio/ssl/detail/openssl_init.hpp index c2d5e387..4e07277c 100644 --- a/include/boost/asio/ssl/detail/openssl_init.hpp +++ b/include/boost/asio/ssl/detail/openssl_init.hpp @@ -61,6 +61,12 @@ private: if (Do_Init) { ::CRYPTO_set_locking_callback(0); + ::ERR_free_strings(); + ::ERR_remove_state(0); + ::EVP_cleanup(); + ::CRYPTO_cleanup_all_ex_data(); + ::CONF_modules_unload(1); + ::ENGINE_cleanup(); } } diff --git a/include/boost/asio/ssl/detail/openssl_operation.hpp b/include/boost/asio/ssl/detail/openssl_operation.hpp index e14a3ecf..056f8667 100644 --- a/include/boost/asio/ssl/detail/openssl_operation.hpp +++ b/include/boost/asio/ssl/detail/openssl_operation.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace boost { @@ -133,6 +134,7 @@ public: int start() { int rc = primitive_( session_ ); + int sys_error_code = ERR_get_error(); bool is_operation_done = (rc > 0); // For connect/accept/shutdown, the operation // is done, when return code is 1 @@ -166,9 +168,14 @@ public: if (!is_operation_done && !is_read_needed && !is_write_needed && !is_shut_down_sent) + { // The operation has failed... It is not completed and does // not want network communication nor does want to send shutdown out... - return handler_(boost::asio::error(error_code), rc); + if (error_code == SSL_ERROR_SYSCALL) + return handler_(boost::asio::error(sys_error_code), rc); + else + return handler_(boost::asio::error(error_code + 1000000), rc); + } if (!is_operation_done && !is_write_needed) { diff --git a/include/boost/asio/ssl/detail/openssl_stream_service.hpp b/include/boost/asio/ssl/detail/openssl_stream_service.hpp index 68058e98..8d40b2fd 100644 --- a/include/boost/asio/ssl/detail/openssl_stream_service.hpp +++ b/include/boost/asio/ssl/detail/openssl_stream_service.hpp @@ -236,7 +236,7 @@ public: typedef handshake_handler connect_handler; connect_handler* local_handler = - new connect_handler(handler, owner()); + new connect_handler(handler, io_service()); openssl_operation* op = new openssl_operation ( @@ -257,7 +257,7 @@ public: ); local_handler->set_operation(op); - owner().post(boost::bind(&openssl_operation::start, op)); + io_service().post(boost::bind(&openssl_operation::start, op)); } // Shut down SSL on the stream. @@ -292,7 +292,7 @@ public: typedef shutdown_handler disconnect_handler; disconnect_handler* local_handler = - new disconnect_handler(handler, owner()); + new disconnect_handler(handler, io_service()); openssl_operation* op = new openssl_operation ( @@ -311,7 +311,7 @@ public: ); local_handler->set_operation(op); - owner().post(boost::bind(&openssl_operation::start, op)); + io_service().post(boost::bind(&openssl_operation::start, op)); } // Write some data to the stream. @@ -353,7 +353,7 @@ public: { typedef io_handler send_handler; - send_handler* local_handler = new send_handler(handler, owner()); + send_handler* local_handler = new send_handler(handler, io_service()); boost::function send_func = boost::bind(&::SSL_write, boost::arg<1>(), @@ -377,7 +377,7 @@ public: ); local_handler->set_operation(op); - owner().post(boost::bind(&openssl_operation::start, op)); + io_service().post(boost::bind(&openssl_operation::start, op)); } // Read some data from the stream. @@ -419,7 +419,7 @@ public: { typedef io_handler recv_handler; - recv_handler* local_handler = new recv_handler(handler, owner()); + recv_handler* local_handler = new recv_handler(handler, io_service()); boost::function recv_func = boost::bind(&::SSL_read, boost::arg<1>(), @@ -443,7 +443,7 @@ public: ); local_handler->set_operation(op); - owner().post(boost::bind(&openssl_operation::start, op)); + io_service().post(boost::bind(&openssl_operation::start, op)); } // Peek at the incoming data on the stream. diff --git a/include/boost/asio/ssl/detail/openssl_types.hpp b/include/boost/asio/ssl/detail/openssl_types.hpp index c96ee2ef..2681cc16 100644 --- a/include/boost/asio/ssl/detail/openssl_types.hpp +++ b/include/boost/asio/ssl/detail/openssl_types.hpp @@ -18,7 +18,10 @@ #include #include +#include #include +#include +#include #include #include diff --git a/include/boost/asio/strand.hpp b/include/boost/asio/strand.hpp index 383110a1..48f9307e 100644 --- a/include/boost/asio/strand.hpp +++ b/include/boost/asio/strand.hpp @@ -17,13 +17,148 @@ #include -#include +#include +#include +#include namespace boost { namespace asio { -/// Typedef for the typical usage of strand. -typedef basic_strand<> strand; +/// Provides serialised handler execution. +/** + * The io_service::strand class provides the ability to post and dispatch + * handlers with the guarantee that none of those handlers will execute + * concurrently. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Dispatcher. + */ +class io_service::strand +{ +public: + /// Constructor. + /** + * Constructs the strand. + * + * @param io_service The io_service object that the strand will use to + * dispatch handlers that are ready to be run. + */ + explicit strand(boost::asio::io_service& io_service) + : service_(boost::asio::use_service< + boost::asio::detail::strand_service>(io_service)) + { + service_.construct(impl_); + } + + /// Destructor. + ~strand() + { + service_.destroy(impl_); + } + + /// Get the io_service associated with the strand. + /** + * This function may be used to obtain the io_service object that the strand + * uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_service object that the strand will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + boost::asio::io_service& io_service() + { + return service_.io_service(); + } + + /// Request the strand to invoke the given handler. + /** + * This function is used to ask the strand to execute the given handler. + * + * The strand object guarantees that handlers posted or dispatched through + * the strand will not be executed concurrently. The handler may be executed + * inside this function if the guarantee can be met. If this function is + * called from within a handler that was posted or dispatched through the same + * strand, then the new handler will be executed immediately. + * + * The strand's guarantee is in addition to the guarantee provided by the + * underlying io_service. The io_service guarantees that the handler will only + * be called in a thread in which the io_service's run member function is + * currently being invoked. + * + * @param handler The handler to be called. The strand will make a copy of the + * handler object as required. The function signature of the handler must be: + * @code void handler(); @endcode + */ + template + void dispatch(Handler handler) + { + service_.dispatch(impl_, handler); + } + + /// Request the strand to invoke the given handler and return + /// immediately. + /** + * This function is used to ask the strand to execute the given handler, but + * without allowing the strand to call the handler from inside this function. + * + * The strand object guarantees that handlers posted or dispatched through + * the strand will not be executed concurrently. The strand's guarantee is in + * addition to the guarantee provided by the underlying io_service. The + * io_service guarantees that the handler will only be called in a thread in + * which the io_service's run member function is currently being invoked. + * + * @param handler The handler to be called. The strand will make a copy of the + * handler object as required. The function signature of the handler must be: + * @code void handler(); @endcode + */ + template + void post(Handler handler) + { + service_.post(impl_, handler); + } + + /// Create a new handler that automatically dispatches the wrapped handler + /// on the strand. + /** + * This function is used to create a new handler function object that, when + * invoked, will automatically pass the wrapped handler to the strand's + * dispatch function. + * + * @param handler The handler to be wrapped. The strand will make a copy of + * the handler object as required. The function signature of the handler must + * be: @code void handler(A1 a1, ... An an); @endcode + * + * @return A function object that, when invoked, passes the wrapped handler to + * the strand's dispatch function. Given a function object with the signature: + * @code R f(A1 a1, ... An an); @endcode + * If this function object is passed to the wrap function like so: + * @code strand.wrap(f); @endcode + * then the return value is a function object with the signature + * @code void g(A1 a1, ... An an); @endcode + * that, when invoked, executes code equivalent to: + * @code strand.dispatch(boost::bind(f, a1, ... an)); @endcode + */ + template +#if defined(GENERATING_DOCUMENTATION) + unspecified +#else + detail::wrapped_handler +#endif + wrap(Handler handler) + { + return detail::wrapped_handler(*this, handler); + } + +private: + boost::asio::detail::strand_service& service_; + boost::asio::detail::strand_service::implementation_type impl_; +}; + +/// Typedef for backwards compatibility. +typedef boost::asio::io_service::strand strand; } // namespace asio } // namespace boost diff --git a/include/boost/asio/strand_service.hpp b/include/boost/asio/strand_service.hpp deleted file mode 100644 index 4bfe056e..00000000 --- a/include/boost/asio/strand_service.hpp +++ /dev/null @@ -1,90 +0,0 @@ -// -// strand_service.hpp -// ~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_ASIO_STRAND_SERVICE_HPP -#define BOOST_ASIO_STRAND_SERVICE_HPP - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -#include - -#include -#include - -namespace boost { -namespace asio { - -/// Default service implementation for a strand. -class strand_service - : public boost::asio::io_service::service -{ -private: - // The type of the platform-specific implementation. - typedef detail::strand_service service_impl_type; - -public: - /// The implementation type of the strand. -#if defined(GENERATING_DOCUMENTATION) - typedef implementation_defined implementation_type; -#else - typedef service_impl_type::implementation_type implementation_type; -#endif - - /// Construct a new timer service for the specified io_service. - explicit strand_service(boost::asio::io_service& io_service) - : boost::asio::io_service::service(io_service), - service_impl_(boost::asio::use_service(io_service)) - { - } - - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - } - - /// Construct a new timer implementation. - void construct(implementation_type& impl) - { - service_impl_.construct(impl); - } - - /// Destroy a timer implementation. - void destroy(implementation_type& impl) - { - service_impl_.destroy(impl); - } - - /// Request the io_service to invoke the given handler. - template - void dispatch(implementation_type& impl, Handler handler) - { - service_impl_.dispatch(impl, handler); - } - - /// Request the io_service to invoke the given handler and return immediately. - template - void post(implementation_type& impl, Handler handler) - { - service_impl_.post(impl, handler); - } - -private: - // The service that provides the platform-specific implementation. - service_impl_type& service_impl_; -}; - -} // namespace asio -} // namespace boost - -#include - -#endif // BOOST_ASIO_STRAND_SERVICE_HPP diff --git a/include/boost/asio/stream_socket_service.hpp b/include/boost/asio/stream_socket_service.hpp index 592de1c7..b61073d5 100644 --- a/include/boost/asio/stream_socket_service.hpp +++ b/include/boost/asio/stream_socket_service.hpp @@ -130,6 +130,13 @@ public: return service_impl_.native(impl); } + /// Cancel all asynchronous operations associated with the socket. + template + void cancel(implementation_type& impl, Error_Handler error_handler) + { + service_impl_.cancel(impl, error_handler); + } + /// Bind the stream socket to the specified local endpoint. template void bind(implementation_type& impl, const endpoint_type& endpoint, diff --git a/include/boost/asio/system_exception.hpp b/include/boost/asio/system_exception.hpp index cb96a242..e7e25879 100644 --- a/include/boost/asio/system_exception.hpp +++ b/include/boost/asio/system_exception.hpp @@ -30,7 +30,7 @@ #endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) #include -#include +#include namespace boost { namespace asio { @@ -83,7 +83,7 @@ public: | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, code_, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); - detail::win_local_free_on_block_exit local_free_obj(msg); + detail::local_free_on_block_exit local_free_obj(msg); if (length && msg[length - 1] == '\n') msg[--length] = '\0'; if (length && msg[length - 1] == '\r') @@ -108,7 +108,8 @@ public: } #elif defined(__sun) || defined(__QNX__) return strerror(code_); -#elif defined(__MACH__) && defined(__APPLE__) || defined(__NetBSD__) +#elif defined(__MACH__) && defined(__APPLE__) \ + || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) try { char buf[256] = ""; diff --git a/test/Jamfile b/test/Jamfile index ee519026..3870944c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -33,45 +33,44 @@ template asio_unit_test test-suite "asio" : - [ link basic_datagram_socket_test.cpp