Bug 854846: Make UnixSocket's connect function non-block; r=tzimmermann
authorKyle Machulis <kyle@nonpolynomial.com>
Mon, 13 May 2013 20:51:21 -0700
changeset 144199 6c52203a47b86c43a80baa5459ac45d6a1a8000e
parent 144198 314a0cf582c44375a6eee4372c11314e2db3b441
child 144200 7189ea1fb7baca78aa1230dbd55db3292eedb3ab
push id368
push userbbajaj@mozilla.com
push dateMon, 09 Sep 2013 22:57:58 +0000
treeherdermozilla-release@5a4f47ae1217 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstzimmermann
bugs854846
milestone24.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 854846: Make UnixSocket's connect function non-block; r=tzimmermann
ipc/unixsocket/UnixSocket.cpp
--- a/ipc/unixsocket/UnixSocket.cpp
+++ b/ipc/unixsocket/UnixSocket.cpp
@@ -545,19 +545,59 @@ UnixSocketImpl::Connect()
 
   int ret;
 
   if (!mConnector->CreateAddr(false, mAddrSize, mAddr, mAddress.get())) {
     NS_WARNING("Cannot create socket address!");
     return;
   }
 
+  // Select non-blocking IO.
+  if (-1 == fcntl(mFd.get(), F_SETFL, O_NONBLOCK)) {
+    nsRefPtr<OnSocketEventTask> t =
+      new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
+    NS_DispatchToMainThread(t);
+    return;
+  }
+
   ret = connect(mFd.get(), (struct sockaddr*)&mAddr, mAddrSize);
 
   if (ret) {
+    if (errno == EINPROGRESS) {
+      // Select blocking IO again, since we've now at least queue'd the connect
+      // as nonblock.
+      int current_opts = fcntl(mFd.get(), F_GETFL, 0);
+      if (-1 == current_opts) {
+        NS_WARNING("Cannot get socket opts!");
+        nsRefPtr<OnSocketEventTask> t =
+          new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
+        NS_DispatchToMainThread(t);
+        return;
+      }
+      if (-1 == fcntl(mFd.get(), F_SETFL, current_opts & ~O_NONBLOCK)) {
+        NS_WARNING("Cannot set socket opts to blocking!");
+        nsRefPtr<OnSocketEventTask> t =
+          new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
+        NS_DispatchToMainThread(t);
+        return;
+      }
+
+      // Set up a write watch to make sure we receive the connect signal
+      MessageLoopForIO::current()->WatchFileDescriptor(
+        mFd.get(),
+        false,
+        MessageLoopForIO::WATCH_WRITE,
+        &mWriteWatcher,
+        this);
+
+#ifdef DEBUG
+      LOG("UnixSocket Connection delayed!");
+#endif
+      return;
+    }
 #if DEBUG
     LOG("Socket connect errno=%d\n", errno);
 #endif
     mFd.reset(-1);
     nsRefPtr<OnSocketEventTask> t =
       new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
     NS_DispatchToMainThread(t);
     return;
@@ -747,56 +787,92 @@ UnixSocketImpl::OnFileCanReadWithoutBloc
 }
 
 void
 UnixSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!mShuttingDownOnIOThread);
 
-  // Try to write the bytes of mCurrentRilRawData.  If all were written, continue.
-  //
-  // Otherwise, save the byte position of the next byte to write
-  // within mCurrentRilRawData, and request another write when the
-  // system won't block.
-  //
   MOZ_ASSERT(aFd >= 0);
-  while (true) {
-    UnixSocketRawData* data;
-    if (mOutgoingQ.IsEmpty()) {
+  SocketConnectionStatus status = mConsumer->GetConnectionStatus();
+  if (status == SOCKET_CONNECTED) {
+    // Try to write the bytes of mCurrentRilRawData.  If all were written, continue.
+    //
+    // Otherwise, save the byte position of the next byte to write
+    // within mCurrentWriteOffset, and request another write when the
+    // system won't block.
+    //
+    while (true) {
+      UnixSocketRawData* data;
+      if (mOutgoingQ.IsEmpty()) {
+        return;
+      }
+      data = mOutgoingQ.ElementAt(0);
+      const uint8_t *toWrite;
+      toWrite = data->mData;
+
+      while (data->mCurrentWriteOffset < data->mSize) {
+        ssize_t write_amount = data->mSize - data->mCurrentWriteOffset;
+        ssize_t written;
+        written = write (aFd, toWrite + data->mCurrentWriteOffset,
+                         write_amount);
+        if (written > 0) {
+          data->mCurrentWriteOffset += written;
+        }
+        if (written != write_amount) {
+          break;
+        }
+      }
+
+      if (data->mCurrentWriteOffset != data->mSize) {
+        MessageLoopForIO::current()->WatchFileDescriptor(
+          aFd,
+          false,
+          MessageLoopForIO::WATCH_WRITE,
+          &mWriteWatcher,
+          this);
+        return;
+      }
+      mOutgoingQ.RemoveElementAt(0);
+      delete data;
+    }
+  } else if (status == SOCKET_CONNECTING) {
+    int error, ret;
+    socklen_t len = sizeof(error);
+    ret = getsockopt(mFd.get(), SOL_SOCKET, SO_ERROR, &error, &len);
+
+    if (ret || error) {
+      NS_WARNING("getsockopt failure on async socket connect!");
+      nsRefPtr<OnSocketEventTask> t =
+        new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
+      NS_DispatchToMainThread(t);
       return;
     }
-    data = mOutgoingQ.ElementAt(0);
-    const uint8_t *toWrite;
-    toWrite = data->mData;
 
-    while (data->mCurrentWriteOffset < data->mSize) {
-      ssize_t write_amount = data->mSize - data->mCurrentWriteOffset;
-      ssize_t written;
-      written = write (aFd, toWrite + data->mCurrentWriteOffset,
-                       write_amount);
-      if (written > 0) {
-        data->mCurrentWriteOffset += written;
-      }
-      if (written != write_amount) {
-        break;
-      }
+    if (!SetSocketFlags()) {
+      nsRefPtr<OnSocketEventTask> t =
+        new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
+      NS_DispatchToMainThread(t);
+      return;
     }
 
-    if (data->mCurrentWriteOffset != data->mSize) {
-      MessageLoopForIO::current()->WatchFileDescriptor(
-        aFd,
-        false,
-        MessageLoopForIO::WATCH_WRITE,
-        &mWriteWatcher,
-        this);
+    if (!mConnector->SetUp(mFd)) {
+      NS_WARNING("Could not set up socket!");
+      nsRefPtr<OnSocketEventTask> t =
+        new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
+      NS_DispatchToMainThread(t);
       return;
     }
-    mOutgoingQ.RemoveElementAt(0);
-    delete data;
+
+    nsRefPtr<OnSocketEventTask> t =
+      new OnSocketEventTask(this, OnSocketEventTask::CONNECT_SUCCESS);
+    NS_DispatchToMainThread(t);
+
+    SetUpIO();
   }
 }
 
 void
 UnixSocketConsumer::GetSocketAddr(nsAString& aAddrStr)
 {
   aAddrStr.Truncate();
   if (!mImpl || mConnectionStatus != SOCKET_CONNECTED) {