$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
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)
 {