Browse Source

Block HttpRequest::Read() if not all data is received and connection is still open. This is to allow the other ReadXXX() functions to work properly. Only call Read() with size at or below GetAvailableSize() to ensure no blocking.

Lasse Öörni 12 years ago
parent
commit
0210a049b2
2 changed files with 50 additions and 27 deletions
  1. 47 24
      Source/Engine/Network/HttpRequest.cpp
  2. 3 3
      Source/Engine/Network/HttpRequest.h

+ 47 - 24
Source/Engine/Network/HttpRequest.cpp

@@ -198,60 +198,83 @@ unsigned HttpRequest::Read(void* dest, unsigned size)
     mutex_.Acquire();
     
     unsigned char* destPtr = (unsigned char*)dest;
-    unsigned bytesAvailable = (writePosition_ - readPosition_) & (READ_BUFFER_SIZE - 1);
-    if (size > bytesAvailable)
-        size = bytesAvailable;
-    
-    if (size)
+    unsigned sizeLeft = size;
+    unsigned totalRead = 0;
+
+    for (;;)
     {
-        if (readPosition_ + size <= READ_BUFFER_SIZE)
-            memcpy(destPtr, readBuffer_.Get() + readPosition_, size);
-        else
+        unsigned bytesAvailable;
+        
+        for (;;)
         {
-            // Handle ring buffer wrap
-            unsigned part1 = READ_BUFFER_SIZE - readPosition_;
-            unsigned part2 = size - part1;
-            memcpy(destPtr, readBuffer_.Get() + readPosition_, part1);
-            memcpy(destPtr + part1, readBuffer_.Get(), part2);
+            bytesAvailable = CheckEofAndAvailableSize();
+            if (bytesAvailable || IsEof())
+                break;
+            // While no bytes and connection is still open, block until has some data
+            mutex_.Release();
+            Time::Sleep(5);
+            mutex_.Acquire();
         }
         
-        readPosition_ += size;
-        readPosition_ &= READ_BUFFER_SIZE - 1;
+        if (bytesAvailable)
+        {
+            if (bytesAvailable > sizeLeft)
+                bytesAvailable = sizeLeft;
+            
+            if (readPosition_ + bytesAvailable <= READ_BUFFER_SIZE)
+                memcpy(destPtr, readBuffer_.Get() + readPosition_, bytesAvailable);
+            else
+            {
+                // Handle ring buffer wrap
+                unsigned part1 = READ_BUFFER_SIZE - readPosition_;
+                unsigned part2 = bytesAvailable - part1;
+                memcpy(destPtr, readBuffer_.Get() + readPosition_, part1);
+                memcpy(destPtr + part1, readBuffer_.Get(), part2);
+            }
+            
+            readPosition_ += bytesAvailable;
+            readPosition_ &= READ_BUFFER_SIZE - 1;
+            sizeLeft -= bytesAvailable;
+            totalRead += bytesAvailable;
+            destPtr += bytesAvailable;
+        }
+        
+        if (!sizeLeft || !bytesAvailable)
+            break;
     }
     
-    // If connection has been closed and there is no more buffered data, we are at the end
-    CheckEof();
-    
+    // Check for end-of-file once more after reading the bytes
+    CheckEofAndAvailableSize();
     mutex_.Release();
-    return size;
+    return totalRead;
 }
 
 String HttpRequest::GetError() const
 {
     MutexLock lock(mutex_);
-    const_cast<HttpRequest*>(this)->CheckEof();
+    const_cast<HttpRequest*>(this)->CheckEofAndAvailableSize();
     return error_;
 }
 
 HttpRequestState HttpRequest::GetState() const
 {
     MutexLock lock(mutex_);
-    const_cast<HttpRequest*>(this)->CheckEof();
+    const_cast<HttpRequest*>(this)->CheckEofAndAvailableSize();
     return state_;
 }
 
 unsigned HttpRequest::GetAvailableSize() const
 {
     MutexLock lock(mutex_);
-    const_cast<HttpRequest*>(this)->CheckEof();
-    return (writePosition_ - readPosition_) & (READ_BUFFER_SIZE - 1);
+    return const_cast<HttpRequest*>(this)->CheckEofAndAvailableSize();
 }
 
-void HttpRequest::CheckEof()
+unsigned HttpRequest::CheckEofAndAvailableSize()
 {
     unsigned bytesAvailable = (writePosition_ - readPosition_) & (READ_BUFFER_SIZE - 1);
     if (state_ == HTTP_ERROR || (state_ == HTTP_CLOSED && !bytesAvailable))
         position_ = M_MAX_UNSIGNED;
+    return bytesAvailable;
 }
 
 }

+ 3 - 3
Source/Engine/Network/HttpRequest.h

@@ -52,7 +52,7 @@ public:
     /// Process the connection in the worker thread until closed.
     virtual void ThreadFunction();
     
-    /// Read response data from the HTTP connection. Return bytes actually read or 0 on error or end of data.
+    /// Read response data from the HTTP connection and return number of bytes actually read. While the connection is open, will block while trying to read the specified size. To avoid blocking, only read up to as many bytes as GetAvailableSize() returns.
     virtual unsigned Read(void* dest, unsigned size);
     /// Set position from the beginning of the stream. Not supported.
     virtual unsigned Seek(unsigned position) { return position_; }
@@ -71,8 +71,8 @@ public:
     bool IsOpen() const { return GetState() == HTTP_OPEN; }
     
 private:
-    /// Check for end of the data stream. Must only be called when the mutex is held by the main thread.
-    void CheckEof();
+    /// Check for end of the data stream and return available size in buffer. Must only be called when the mutex is held by the main thread.
+    unsigned CheckEofAndAvailableSize();
     
     /// URL.
     String url_;