Parcourir la source

timeout in asio by using a call to async_read() and a deadline_timer

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac296@2632 e5f2f494-b856-4b98-b285-d166d9295462
Jelte Jansen il y a 14 ans
Parent
commit
09dadcdcdd
2 fichiers modifiés avec 50 ajouts et 3 suppressions
  1. 41 3
      src/lib/cc/session.cc
  2. 9 0
      src/lib/cc/session.h

+ 41 - 3
src/lib/cc/session.cc

@@ -28,6 +28,7 @@
 #include <unistd.h>             // for some IPC/network system calls
 #include <asio.hpp>
 #include <asio/error_code.hpp>
+#include <asio/deadline_timer.hpp>
 #include <asio/system_error.hpp>
 
 #include <cstdio>
@@ -39,6 +40,7 @@
 
 #include <boost/bind.hpp>
 #include <boost/function.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
 
 #include <exceptions/exceptions.h>
 
@@ -65,7 +67,9 @@ public:
     void disconnect();
     void writeData(const void* data, size_t datalen);
     size_t readDataLength();
-    void readData(void* data, size_t datalen);
+    // Blocking read. Will throw a SessionTimeout if the timeout value
+    // (in seconds) is thrown. If timeout is 0 it will block forever
+    void readData(void* data, size_t datalen, size_t timeout = 5);
     void startRead(boost::function<void()> user_handler);
 
     long int sequence_; // the next sequence number to use
@@ -75,6 +79,10 @@ public:
 private:
     void internalRead(const asio::error_code& error,
                       size_t bytes_transferred);
+    // Sets the boolean pointed to by result to true, unless
+    // the given error code is operation_aborted
+    // Used as a callback for emulating sync reads with async calls
+    void setResult(bool* result, asio::error_code b);
 
 private:
     io_service& io_service_;
@@ -130,9 +138,39 @@ SessionImpl::readDataLength() {
 }
 
 void
-SessionImpl::readData(void* data, size_t datalen) {
+SessionImpl::setResult(bool* result, asio::error_code b)
+{
+    if (b != asio::error::operation_aborted) {
+        *result = true;
+    }
+}
+
+void
+SessionImpl::readData(void* data, size_t datalen, size_t timeout) {
+    bool timer_result = false;
+    bool read_result = false;
     try {
-        asio::read(socket_, asio::buffer(data, datalen));
+        async_read(socket_, asio::buffer(data, datalen), asio::transfer_at_least( datalen ),
+            boost::bind(&SessionImpl::setResult, this, &read_result, _1));
+        asio::deadline_timer timer(socket_.io_service());
+    
+        if (timeout != 0) {
+            timer.expires_from_now(boost::posix_time::seconds(timeout));
+            timer.async_wait(boost::bind(&SessionImpl::setResult, this, &timer_result, _1));
+        }
+    
+        while (!read_result && !timer_result) {
+            socket_.io_service().run_one();
+            if (read_result) {
+                timer.cancel();
+            } else if (timer_result) {
+                socket_.cancel();
+            }
+        }
+    
+        if (!read_result) {
+            isc_throw(SessionTimeout, "Timeout or error on ");
+        }
     } catch (const asio::system_error& asio_ex) {
         // to hide boost specific exceptions, we catch them explicitly
         // and convert it to SessionError.

+ 9 - 0
src/lib/cc/session.h

@@ -40,6 +40,15 @@ namespace isc {
                 isc::Exception(file, line, what) {}
         };
 
+        /// \brief A standard Exception class that is thrown when a
+        /// blocking readData call does not read the given number of
+        /// bytes before the timeout expires
+        class SessionTimeout : public isc::Exception {
+        public:
+            SessionTimeout(const char* file, size_t line, const char* what) :
+                isc::Exception(file, line, what) {}
+        };
+
         /// \brief The AbstractSession class is an abstract base class that
         /// defines the interfaces of Session.
         /// The intended primary usage of abstraction is to allow tests for the