Bug 1059813: Wrap socket I/O operations in |UnixSocketRawData|, r=qdot
authorThomas Zimmermann <tdz@users.sourceforge.net>
Mon, 08 Sep 2014 11:44:00 +0200
changeset 203944 5e1f40db0989b40877432a1cb92028e28f6817ff
parent 203943 d5745cea7a60c23901cba15a478632cd3214fb25
child 203945 26df995c1141c3c67ed8d2a3b803ff88a69500f0
push id10451
push usertdz@users.sourceforge.net
push dateMon, 08 Sep 2014 09:46:56 +0000
treeherderb2g-inbound@e8ba4019d6a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot
bugs1059813
milestone35.0a1
Bug 1059813: Wrap socket I/O operations in |UnixSocketRawData|, r=qdot This patch moves the I/O operations for sending and receiving data in |SocketIOBase| into |UnixSocketRawData|. This change allows to add a clean interface to |UnixSocketRawData| and later replace the class by other implementations.
ipc/unixsocket/SocketBase.cpp
ipc/unixsocket/SocketBase.h
--- a/ipc/unixsocket/SocketBase.cpp
+++ b/ipc/unixsocket/SocketBase.cpp
@@ -2,43 +2,102 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 #include "SocketBase.h"
+#include <errno.h>
 #include <string.h>
-#include "nsThreadUtils.h"
+#include <unistd.h>
 
 namespace mozilla {
 namespace ipc {
 
 //
 // UnixSocketRawData
 //
 
 UnixSocketRawData::UnixSocketRawData(size_t aSize)
-: mSize(aSize)
+: mSize(0)
 , mCurrentWriteOffset(0)
+, mAvailableSpace(aSize)
 {
-  mData = new uint8_t[mSize];
+  mData = new uint8_t[mAvailableSpace];
 }
 
 UnixSocketRawData::UnixSocketRawData(const void* aData, size_t aSize)
 : mSize(aSize)
 , mCurrentWriteOffset(0)
+, mAvailableSpace(aSize)
 {
   MOZ_ASSERT(aData || !mSize);
 
-  mData = new uint8_t[mSize];
+  mData = new uint8_t[mAvailableSpace];
   memcpy(mData, aData, mSize);
 }
 
+ssize_t
+UnixSocketRawData::Receive(int aFd)
+{
+  if (!GetTrailingSpace()) {
+    if (!GetLeadingSpace()) {
+      return -1; /* buffer is full */
+    }
+    /* free up space at the end of data buffer */
+    if (GetSize() <= GetLeadingSpace()) {
+      memcpy(mData, GetData(), GetSize());
+    } else {
+      memmove(mData, GetData(), GetSize());
+    }
+    mCurrentWriteOffset = 0;
+  }
+
+  ssize_t res =
+    TEMP_FAILURE_RETRY(read(aFd, GetTrailingBytes(), GetTrailingSpace()));
+
+  if (res < 0) {
+    /* I/O error */
+    return -1;
+  } else if (!res) {
+    /* EOF or peer shutdown sending */
+    return 0;
+  }
+
+  mSize += res;
+
+  return res;
+}
+
+ssize_t
+UnixSocketRawData::Send(int aFd)
+{
+  if (!GetSize()) {
+    return 0;
+  }
+
+  ssize_t res = TEMP_FAILURE_RETRY(write(aFd, GetData(), GetSize()));
+
+  if (res < 0) {
+    if (errno == EAGAIN || errno == EWOULDBLOCK) {
+      return 0; /* socket is blocked; try again later */
+    }
+    return -1;
+  } else if (!res) {
+    /* nothing written */
+    return 0;
+  }
+
+  Consume(res);
+
+  return res;
+}
+
 //
 // SocketConsumerBase
 //
 
 SocketConsumerBase::~SocketConsumerBase()
 {
   MOZ_ASSERT(mConnectionStatus == SOCKET_DISCONNECTED);
 }
--- a/ipc/unixsocket/SocketBase.h
+++ b/ipc/unixsocket/SocketBase.h
@@ -4,18 +4,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 #ifndef mozilla_ipc_SocketBase_h
 #define mozilla_ipc_SocketBase_h
 
-#include <errno.h>
-#include <unistd.h>
 #include "base/message_loop.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 using namespace mozilla::tasktracer;
@@ -42,16 +40,71 @@ public:
    */
   UnixSocketRawData(size_t aSize);
 
   /**
    * Constructor for situations where size and data is known
    * beforehand (for example, when being assigned strings)
    */
   UnixSocketRawData(const void* aData, size_t aSize);
+
+  /**
+   * Receives data from aFd at the end of the buffer. The returned value
+   * is the number of newly received bytes, or 0 if the peer shut down
+   * its connection, or a negative value on errors.
+   */
+  ssize_t Receive(int aFd);
+
+  /**
+   * Sends data to aFd from the beginning of the buffer. The returned value
+   * is the number of bytes written, or a negative value on error.
+   */
+  ssize_t Send(int aFd);
+
+  const uint8_t* GetData() const
+  {
+    return mData + mCurrentWriteOffset;
+  }
+
+  size_t GetSize() const
+  {
+    return mSize;
+  }
+
+  void Consume(size_t aSize)
+  {
+    MOZ_ASSERT(aSize <= mSize);
+
+    mSize -= aSize;
+    mCurrentWriteOffset += aSize;
+  }
+
+protected:
+  size_t GetLeadingSpace() const
+  {
+    return mCurrentWriteOffset;
+  }
+
+  size_t GetTrailingSpace() const
+  {
+    return mAvailableSpace - (mCurrentWriteOffset + mSize);
+  }
+
+  size_t GetAvailableSpace() const
+  {
+    return mAvailableSpace;
+  }
+
+  void* GetTrailingBytes()
+  {
+    return mData + mCurrentWriteOffset + mSize;
+  }
+
+private:
+  size_t mAvailableSpace;
 };
 
 enum SocketConnectionStatus {
   SOCKET_DISCONNECTED = 0,
   SOCKET_LISTENING = 1,
   SOCKET_CONNECTING = 2,
   SOCKET_CONNECTED = 3
 };
@@ -326,33 +379,29 @@ public:
   ssize_t ReceiveData(int aFd, T* aIO)
   {
     MOZ_ASSERT(aFd >= 0);
     MOZ_ASSERT(aIO);
 
     nsAutoPtr<UnixSocketRawData> incoming(
       new UnixSocketRawData(mMaxReadSize));
 
-    ssize_t res =
-      TEMP_FAILURE_RETRY(read(aFd, incoming->mData, incoming->mSize));
-
+    ssize_t res = incoming->Receive(aFd);
     if (res < 0) {
       /* an I/O error occured */
       nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
       NS_DispatchToMainThread(r);
       return -1;
     } else if (!res) {
       /* EOF or peer shut down sending */
       nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
       NS_DispatchToMainThread(r);
       return 0;
     }
 
-    incoming->mSize = res;
-
 #ifdef MOZ_TASK_TRACER
     // Make unix socket creation events to be the source events of TaskTracer,
     // and originate the rest correlation tasks from here.
     AutoSourceEvent taskTracerEvent(SourceEventType::UNIXSOCKET);
 #endif
 
     nsRefPtr<nsRunnable> r =
       new SocketIOReceiveRunnable<T>(aIO, incoming.forget());
@@ -362,48 +411,34 @@ public:
   }
 
   template <typename T>
   nsresult SendPendingData(int aFd, T* aIO)
   {
     MOZ_ASSERT(aFd >= 0);
     MOZ_ASSERT(aIO);
 
-    do {
-      if (!HasPendingData()) {
-        return NS_OK;
-      }
-
+    while (HasPendingData()) {
       UnixSocketRawData* outgoing = mOutgoingQ.ElementAt(0);
-      MOZ_ASSERT(outgoing->mSize);
 
-      const uint8_t* data = outgoing->mData + outgoing->mCurrentWriteOffset;
-      size_t size = outgoing->mSize - outgoing->mCurrentWriteOffset;
-
-      ssize_t res = TEMP_FAILURE_RETRY(write(aFd, data, size));
-
+      ssize_t res = outgoing->Send(aFd);
       if (res < 0) {
-        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-          return NS_OK; /* no more data available */
-        }
-        /* an error occored */
+        /* an I/O error occured */
         nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
         NS_DispatchToMainThread(r);
         return NS_ERROR_FAILURE;
-      } else if (!res) {
-        return NS_OK; /* nothing written */
+      } else if (!res && outgoing->GetSize()) {
+        /* I/O is currently blocked; try again later */
+        return NS_OK;
       }
-
-      outgoing->mCurrentWriteOffset += res;
-
-      if (outgoing->mCurrentWriteOffset == outgoing->mSize) {
+      if (!outgoing->GetSize()) {
         mOutgoingQ.RemoveElementAt(0);
         delete outgoing;
       }
-    } while (true);
+    }
 
     return NS_OK;
   }
 
 protected:
   SocketIOBase(size_t aMaxReadSize);
 
 private: