Boost logo

Boost-Commit :

From: chris_at_[hidden]
Date: 2008-08-31 05:02:00


Author: chris_kohlhoff
Date: 2008-08-31 05:01:59 EDT (Sun, 31 Aug 2008)
New Revision: 48491
URL: http://svn.boost.org/trac/boost/changeset/48491

Log:
Refactor reactive socket implementation so that synchronous read, write,
accept and connect operations don't modify data associated with the socket.

Text files modified:
   trunk/boost/asio/detail/reactive_socket_service.hpp | 225 ++++++++++++++++++++-------------------
   trunk/boost/asio/detail/socket_ops.hpp | 26 ++++
   2 files changed, 141 insertions(+), 110 deletions(-)

Modified: trunk/boost/asio/detail/reactive_socket_service.hpp
==============================================================================
--- trunk/boost/asio/detail/reactive_socket_service.hpp (original)
+++ trunk/boost/asio/detail/reactive_socket_service.hpp 2008-08-31 05:01:59 EDT (Sun, 31 Aug 2008)
@@ -74,10 +74,21 @@
 
     enum
     {
- 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.
- user_set_linger = 8 // The user set the linger option.
+ // The user wants a non-blocking socket.
+ user_set_non_blocking = 1,
+
+ // The implementation wants a non-blocking socket (in order to be able to
+ // perform asynchronous read and write operations).
+ internal_non_blocking = 2,
+
+ // Helper "flag" used to determine whether the socket is non-blocking.
+ non_blocking = user_set_non_blocking | internal_non_blocking,
+
+ // User wants connection_aborted errors, which are disabled by default.
+ enable_connection_aborted = 4,
+
+ // The user set the linger option. Needs to be checked when closing.
+ user_set_linger = 8
     };
 
     // Flags indicating the current state of the socket.
@@ -120,12 +131,12 @@
     {
       reactor_.close_descriptor(impl.socket_, impl.reactor_data_);
 
- if (impl.flags_ & implementation_type::internal_non_blocking)
+ if (impl.flags_ & implementation_type::non_blocking)
       {
         ioctl_arg_type non_blocking = 0;
         boost::system::error_code ignored_ec;
         socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec);
- impl.flags_ &= ~implementation_type::internal_non_blocking;
+ impl.flags_ &= ~implementation_type::non_blocking;
       }
 
       if (impl.flags_ & implementation_type::user_set_linger)
@@ -214,12 +225,12 @@
     {
       reactor_.close_descriptor(impl.socket_, impl.reactor_data_);
 
- if (impl.flags_ & implementation_type::internal_non_blocking)
+ if (impl.flags_ & implementation_type::non_blocking)
       {
         ioctl_arg_type non_blocking = 0;
         boost::system::error_code ignored_ec;
         socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec);
- impl.flags_ &= ~implementation_type::internal_non_blocking;
+ impl.flags_ &= ~implementation_type::non_blocking;
       }
 
       if (socket_ops::close(impl.socket_, ec) == socket_error_retval)
@@ -433,11 +444,35 @@
 
     if (command.name() == static_cast<int>(FIONBIO))
     {
+ // Flags are manipulated in a temporary variable so that the socket
+ // implementation is not updated unless the ioctl operation succeeds.
+ unsigned char new_flags = impl.flags_;
       if (command.get())
- impl.flags_ |= implementation_type::user_set_non_blocking;
+ new_flags |= implementation_type::user_set_non_blocking;
       else
- impl.flags_ &= ~implementation_type::user_set_non_blocking;
- ec = boost::system::error_code();
+ new_flags &= ~implementation_type::user_set_non_blocking;
+
+ // Perform ioctl on socket if the non-blocking state has changed.
+ if (!(impl.flags_ & implementation_type::non_blocking)
+ && (new_flags & implementation_type::non_blocking))
+ {
+ ioctl_arg_type non_blocking = 1;
+ socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec);
+ }
+ else if ((impl.flags_ & implementation_type::non_blocking)
+ && !(new_flags & implementation_type::non_blocking))
+ {
+ ioctl_arg_type non_blocking = 0;
+ socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec);
+ }
+ else
+ {
+ ec = boost::system::error_code();
+ }
+
+ // Update socket implementation's flags only if successful.
+ if (!ec)
+ impl.flags_ = new_flags;
     }
     else
     {
@@ -530,18 +565,6 @@
       return 0;
     }
 
- // Make socket non-blocking if user wants non-blocking.
- if (impl.flags_ & implementation_type::user_set_non_blocking)
- {
- if (!(impl.flags_ & implementation_type::internal_non_blocking))
- {
- ioctl_arg_type non_blocking = 1;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
- return 0;
- impl.flags_ |= implementation_type::internal_non_blocking;
- }
- }
-
     // Send the data.
     for (;;)
     {
@@ -684,12 +707,15 @@
       // Make socket non-blocking.
       if (!(impl.flags_ & implementation_type::internal_non_blocking))
       {
- ioctl_arg_type non_blocking = 1;
- boost::system::error_code ec;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
+ if (!(impl.flags_ & implementation_type::non_blocking))
         {
- this->get_io_service().post(bind_handler(handler, ec, 0));
- return;
+ ioctl_arg_type non_blocking = 1;
+ boost::system::error_code ec;
+ if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
+ {
+ this->get_io_service().post(bind_handler(handler, ec, 0));
+ return;
+ }
         }
         impl.flags_ |= implementation_type::internal_non_blocking;
       }
@@ -773,18 +799,6 @@
           boost::asio::buffer_size(buffer));
     }
 
- // Make socket non-blocking if user wants non-blocking.
- if (impl.flags_ & implementation_type::user_set_non_blocking)
- {
- if (!(impl.flags_ & implementation_type::internal_non_blocking))
- {
- ioctl_arg_type non_blocking = 1;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
- return 0;
- impl.flags_ |= implementation_type::internal_non_blocking;
- }
- }
-
     // Send the data.
     for (;;)
     {
@@ -912,12 +926,15 @@
       // Make socket non-blocking.
       if (!(impl.flags_ & implementation_type::internal_non_blocking))
       {
- ioctl_arg_type non_blocking = 1;
- boost::system::error_code ec;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
+ if (!(impl.flags_ & implementation_type::non_blocking))
         {
- this->get_io_service().post(bind_handler(handler, ec, 0));
- return;
+ ioctl_arg_type non_blocking = 1;
+ boost::system::error_code ec;
+ if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
+ {
+ this->get_io_service().post(bind_handler(handler, ec, 0));
+ return;
+ }
         }
         impl.flags_ |= implementation_type::internal_non_blocking;
       }
@@ -981,18 +998,6 @@
       return 0;
     }
 
- // Make socket non-blocking if user wants non-blocking.
- if (impl.flags_ & implementation_type::user_set_non_blocking)
- {
- if (!(impl.flags_ & implementation_type::internal_non_blocking))
- {
- ioctl_arg_type non_blocking = 1;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
- return 0;
- impl.flags_ |= implementation_type::internal_non_blocking;
- }
- }
-
     // Receive some data.
     for (;;)
     {
@@ -1148,12 +1153,15 @@
       // Make socket non-blocking.
       if (!(impl.flags_ & implementation_type::internal_non_blocking))
       {
- ioctl_arg_type non_blocking = 1;
- boost::system::error_code ec;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
+ if (!(impl.flags_ & implementation_type::non_blocking))
         {
- this->get_io_service().post(bind_handler(handler, ec, 0));
- return;
+ ioctl_arg_type non_blocking = 1;
+ boost::system::error_code ec;
+ if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
+ {
+ this->get_io_service().post(bind_handler(handler, ec, 0));
+ return;
+ }
         }
         impl.flags_ |= implementation_type::internal_non_blocking;
       }
@@ -1225,18 +1233,6 @@
           boost::asio::buffer_size(buffer));
     }
 
- // Make socket non-blocking if user wants non-blocking.
- if (impl.flags_ & implementation_type::user_set_non_blocking)
- {
- if (!(impl.flags_ & implementation_type::internal_non_blocking))
- {
- ioctl_arg_type non_blocking = 1;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
- return 0;
- impl.flags_ |= implementation_type::internal_non_blocking;
- }
- }
-
     // Receive some data.
     for (;;)
     {
@@ -1385,12 +1381,15 @@
       // Make socket non-blocking.
       if (!(impl.flags_ & implementation_type::internal_non_blocking))
       {
- ioctl_arg_type non_blocking = 1;
- boost::system::error_code ec;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
+ if (!(impl.flags_ & implementation_type::non_blocking))
         {
- this->get_io_service().post(bind_handler(handler, ec, 0));
- return;
+ ioctl_arg_type non_blocking = 1;
+ boost::system::error_code ec;
+ if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
+ {
+ this->get_io_service().post(bind_handler(handler, ec, 0));
+ return;
+ }
         }
         impl.flags_ |= implementation_type::internal_non_blocking;
       }
@@ -1450,18 +1449,6 @@
       return ec;
     }
 
- // Make socket non-blocking if user wants non-blocking.
- if (impl.flags_ & implementation_type::user_set_non_blocking)
- {
- if (!(impl.flags_ & implementation_type::internal_non_blocking))
- {
- ioctl_arg_type non_blocking = 1;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
- return ec;
- impl.flags_ |= implementation_type::internal_non_blocking;
- }
- }
-
     // Accept a socket.
     for (;;)
     {
@@ -1623,12 +1610,15 @@
       // Make socket non-blocking.
       if (!(impl.flags_ & implementation_type::internal_non_blocking))
       {
- ioctl_arg_type non_blocking = 1;
- boost::system::error_code ec;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
+ if (!(impl.flags_ & implementation_type::non_blocking))
         {
- this->get_io_service().post(bind_handler(handler, ec));
- return;
+ ioctl_arg_type non_blocking = 1;
+ boost::system::error_code ec;
+ if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
+ {
+ this->get_io_service().post(bind_handler(handler, ec));
+ return;
+ }
         }
         impl.flags_ |= implementation_type::internal_non_blocking;
       }
@@ -1652,18 +1642,30 @@
       return ec;
     }
 
- if (impl.flags_ & implementation_type::internal_non_blocking)
- {
- // Mark the socket as blocking while we perform the connect.
- ioctl_arg_type non_blocking = 0;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
- return ec;
- impl.flags_ &= ~implementation_type::internal_non_blocking;
- }
-
     // Perform the connect operation.
     socket_ops::connect(impl.socket_,
         peer_endpoint.data(), peer_endpoint.size(), ec);
+ if (ec != boost::asio::error::in_progress
+ && ec != boost::asio::error::would_block)
+ {
+ // The connect operation finished immediately.
+ return ec;
+ }
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_connect(impl.socket_, ec) < 0)
+ return ec;
+
+ // Get the error code from the connect operation.
+ int connect_error = 0;
+ size_t connect_error_len = sizeof(connect_error);
+ if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_ERROR,
+ &connect_error, &connect_error_len, ec) == socket_error_retval)
+ return ec;
+
+ // Return the result of the connect operation.
+ ec = boost::system::error_code(connect_error,
+ boost::asio::error::get_system_category());
     return ec;
   }
 
@@ -1731,12 +1733,15 @@
     // Make socket non-blocking.
     if (!(impl.flags_ & implementation_type::internal_non_blocking))
     {
- ioctl_arg_type non_blocking = 1;
- boost::system::error_code ec;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
+ if (!(impl.flags_ & implementation_type::non_blocking))
       {
- this->get_io_service().post(bind_handler(handler, ec));
- return;
+ ioctl_arg_type non_blocking = 1;
+ boost::system::error_code ec;
+ if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
+ {
+ this->get_io_service().post(bind_handler(handler, ec));
+ return;
+ }
       }
       impl.flags_ |= implementation_type::internal_non_blocking;
     }

Modified: trunk/boost/asio/detail/socket_ops.hpp
==============================================================================
--- trunk/boost/asio/detail/socket_ops.hpp (original)
+++ trunk/boost/asio/detail/socket_ops.hpp 2008-08-31 05:01:59 EDT (Sun, 31 Aug 2008)
@@ -702,6 +702,32 @@
 #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
 }
 
+inline int poll_connect(socket_type s, boost::system::error_code& ec)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ FD_SET write_fds;
+ FD_ZERO(&write_fds);
+ FD_SET(s, &write_fds);
+ FD_SET except_fds;
+ FD_ZERO(&except_fds);
+ FD_SET(s, &except_fds);
+ clear_error(ec);
+ int result = error_wrapper(::select(s, 0, &write_fds, &except_fds, 0), ec);
+# if defined(UNDER_CE)
+ if (result >= 0)
+ clear_error(ec);
+# endif
+ return result;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ pollfd fds;
+ fds.fd = s;
+ fds.events = POLLOUT;
+ fds.revents = 0;
+ clear_error(ec);
+ return error_wrapper(::poll(&fds, 1, -1), ec);
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
 inline const char* inet_ntop(int af, const void* src, char* dest, size_t length,
     unsigned long scope_id, boost::system::error_code& ec)
 {


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk