Bug 1059813: Wrap socket I/O operations in |UnixSocketRawData|, r=qdot
☠☠ backed out by 927da4815ef9 ☠ ☠
authorThomas Zimmermann <tdz@users.sourceforge.net>
Wed, 03 Sep 2014 16:43:12 +0200
changeset 203192 0b7a62175398573e7551cc39a14f91d32278fc29
parent 203191 fd3644eefeed145c14231232cf95f0edb3aaab1c
child 203193 7bd0ecf96e592fc7050c06236d340decdd65668e
push id27423
push userryanvm@gmail.com
push dateWed, 03 Sep 2014 18:53:23 +0000
treeherdermozilla-central@117271830c4d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot
bugs1059813
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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,104 @@
 /* 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);
 }
 
+nsresult
+UnixSocketRawData::Receive(int aFd)
+{
+  if (!GetTrailingSpace()) {
+    if (!GetLeadingSpace()) {
+      return NS_ERROR_OUT_OF_MEMORY; /* 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) {
+    if (errno == EAGAIN || errno == EWOULDBLOCK) {
+      return NS_OK; /* no more data available; try again later */
+    }
+    return NS_ERROR_FAILURE;
+  } else if (!res) {
+    /* EOF or peer shutdown sending */
+    return NS_OK;
+  }
+
+  mSize += res;
+
+  return NS_OK;
+}
+
+nsresult
+UnixSocketRawData::Send(int aFd)
+{
+  if (!GetSize()) {
+    return NS_OK;
+  }
+
+  ssize_t res = TEMP_FAILURE_RETRY(write(aFd, GetData(), GetSize()));
+
+  if (res < 0) {
+    if (errno == EAGAIN || errno == EWOULDBLOCK) {
+      return NS_OK; /* socket is blocked; try again later */
+    }
+    return NS_ERROR_FAILURE;
+  } else if (!res) {
+    /* nothing written */
+    return NS_OK;
+  }
+
+  Consume(res);
+
+  return NS_OK;
+}
+
 //
 // 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,61 @@ 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);
+
+  nsresult Receive(int aFd);
+  nsresult 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
 };
@@ -323,91 +366,67 @@ public:
   bool HasPendingData() const;
 
   template <typename T>
   nsresult ReceiveData(int aFd, T* aIO)
   {
     MOZ_ASSERT(aFd >= 0);
     MOZ_ASSERT(aIO);
 
-    do {
+    while (true) {
       nsAutoPtr<UnixSocketRawData> incoming(
         new UnixSocketRawData(mMaxReadSize));
 
-      ssize_t res =
-        TEMP_FAILURE_RETRY(read(aFd, incoming->mData, incoming->mSize));
+      nsresult rv = incoming->Receive(aFd);
 
-      if (res < 0) {
-        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-          return NS_OK; /* no more data available */
-        }
-        /* an error occored */
+      if (NS_WARN_IF(NS_FAILED(rv))) {
         nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
         NS_DispatchToMainThread(r);
-        return NS_ERROR_FAILURE;
-      } else if (!res) {
-        /* EOF or peer shut down sending */
-        nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
-        NS_DispatchToMainThread(r);
+        return rv;
+      }
+      if (!incoming->GetSize()) {
+        /* no data available; try again later */
         return NS_OK;
       }
 
 #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
 
-      incoming->mSize = res;
       nsRefPtr<nsRunnable> r =
         new SocketIOReceiveRunnable<T>(aIO, incoming.forget());
       NS_DispatchToMainThread(r);
-    } while (true);
+    }
 
     return NS_OK;
   }
 
   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));
+      nsresult rv = outgoing->Send(aFd);
 
-      if (res < 0) {
-        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-          return NS_OK; /* no more data available */
-        }
-        /* an error occored */
+      if (NS_WARN_IF(NS_FAILED(rv))) {
         nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
         NS_DispatchToMainThread(r);
-        return NS_ERROR_FAILURE;
-      } else if (!res) {
-        return NS_OK; /* nothing written */
+        return rv;
       }
-
-      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: