Bug 1029387: Introduce connection status for Bluedroid sockets, r=shuang
authorThomas Zimmermann <tdz@users.sourceforge.net>
Thu, 10 Jul 2014 15:10:36 +0200
changeset 193328 b56d8177948c5227d12781017dd655ff77c6b0ea
parent 193327 786aa9a4e62deb86f72bad84897191428a0b606f
child 193329 9834dab5eef5180b8c1a66ddba46ea76b222b8d4
push id27116
push userryanvm@gmail.com
push dateThu, 10 Jul 2014 22:09:47 +0000
treeherdermozilla-central@0a0348d3f0c8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshuang
bugs1029387
milestone33.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 1029387: Introduce connection status for Bluedroid sockets, r=shuang In preparaton of moving the Bluedroid socket setup to BluetoothInterface, this patch introduces connection states for Bluetooth sockets. There are 4 states, - Disconnected, - Listening, - Connecting, and - Connected. All sockets start in Disconnected and transition to Connected via one of the other states. Server socket transition through Listening, Client sockets transition through Connecting. There is currently a lot of code duplication in read and write methods. This will be cleaned up when the connection setup is handled by BluetoothInterface.
dom/bluetooth/bluedroid/BluetoothSocket.cpp
--- a/dom/bluetooth/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.cpp
@@ -78,49 +78,58 @@ ReadBdAddress(const uint8_t* aData, size
 
   aDeviceAddress.AssignLiteral(bdstr);
   *aOffset += 6;
 }
 
 class mozilla::dom::bluetooth::DroidSocketImpl : public ipc::UnixFdWatcher
 {
 public:
+  enum ConnectionStatus {
+    SOCKET_IS_DISCONNECTED = 0,
+    SOCKET_IS_LISTENING,
+    SOCKET_IS_CONNECTING,
+    SOCKET_IS_CONNECTED
+  };
+
   DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer, int aFd)
     : ipc::UnixFdWatcher(aIOLoop, aFd)
     , mConsumer(aConsumer)
     , mReadMsgForClientFd(false)
     , mShuttingDownOnIOThread(false)
     , mChannel(0)
     , mAuth(false)
     , mEncrypt(false)
-  {
-  }
+    , mConnectionStatus(SOCKET_IS_DISCONNECTED)
+  { }
 
   DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer,
                   int aChannel, bool aAuth, bool aEncrypt)
     : ipc::UnixFdWatcher(aIOLoop)
     , mConsumer(aConsumer)
     , mReadMsgForClientFd(false)
     , mShuttingDownOnIOThread(false)
     , mChannel(aChannel)
     , mAuth(aAuth)
     , mEncrypt(aEncrypt)
+    , mConnectionStatus(SOCKET_IS_DISCONNECTED)
   { }
 
   DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer,
                   const nsAString& aDeviceAddress,
                   int aChannel, bool aAuth, bool aEncrypt)
     : ipc::UnixFdWatcher(aIOLoop)
     , mConsumer(aConsumer)
     , mReadMsgForClientFd(false)
     , mShuttingDownOnIOThread(false)
     , mDeviceAddress(aDeviceAddress)
     , mChannel(aChannel)
     , mAuth(aAuth)
     , mEncrypt(aEncrypt)
+    , mConnectionStatus(SOCKET_IS_DISCONNECTED)
   {
     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
   }
 
   ~DroidSocketImpl()
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
@@ -157,31 +166,26 @@ public:
     RemoveWatchers(READ_WATCHER | WRITE_WATCHER);
 
     mShuttingDownOnIOThread = true;
   }
 
   void Connect(int aFd);
   void Listen(int aFd);
 
-  void SetUpIO(bool aWrite)
-  {
-    AddWatchers(READ_WATCHER, true);
-    if (aWrite) {
-        AddWatchers(WRITE_WATCHER, false);
-    }
-  }
-
   void ConnectClientFd()
   {
     // Stop current read watch
     RemoveWatchers(READ_WATCHER);
 
+    mConnectionStatus = SOCKET_IS_CONNECTED;
+
     // Restart read & write watch on client fd
-    SetUpIO(true);
+    AddWatchers(READ_WATCHER, true);
+    AddWatchers(WRITE_WATCHER, false);
   }
 
   /**
    * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
    * directly from main thread. All non-main-thread accesses should happen with
    * mImpl as container.
    */
   RefPtr<BluetoothSocket> mConsumer;
@@ -203,16 +207,21 @@ private:
   /**
    * libevent or developer triggered functions that writes data to socket when
    * available and guarenteed non-blocking. Only to be called on IO thread.
    *
    * @param aFd [in] File descriptor to read from
    */
   virtual void OnFileCanWriteWithoutBlocking(int aFd);
 
+  void OnSocketCanReceiveWithoutBlocking(int aFd);
+  void OnSocketCanAcceptWithoutBlocking(int aFd);
+  void OnSocketCanSendWithoutBlocking(int aFd);
+  void OnSocketCanConnectWithoutBlocking(int aFd);
+
   /**
    * Read message to get data and client fd wrapped in message header
    *
    * @param aFd     [in]  File descriptor to read message from
    * @param aBuffer [out] Data buffer read
    * @param aLength [out] Number of bytes read
    */
   ssize_t ReadMsg(int aFd, void *aBuffer, size_t aLength);
@@ -227,16 +236,17 @@ private:
    * If true, do not requeue whatever task we're running
    */
   bool mShuttingDownOnIOThread;
 
   nsString mDeviceAddress;
   int mChannel;
   bool mAuth;
   bool mEncrypt;
+  ConnectionStatus mConnectionStatus;
 };
 
 template<class T>
 class DeleteInstanceRunnable : public nsRunnable
 {
 public:
   DeleteInstanceRunnable(T* aInstance)
   : mInstance(aInstance)
@@ -450,16 +460,17 @@ DroidSocketImpl::Connect(int aFd)
   NS_ENSURE_TRUE_VOID(flags >= 0);
 
   if (!(flags & O_NONBLOCK)) {
     int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags | O_NONBLOCK));
     NS_ENSURE_TRUE_VOID(!res);
   }
 
   SetFd(aFd);
+  mConnectionStatus = SOCKET_IS_CONNECTING;
 
   AddWatchers(READ_WATCHER, true);
 }
 
 void
 DroidSocketImpl::Listen(int aFd)
 {
   MOZ_ASSERT(aFd >= 0);
@@ -468,16 +479,18 @@ DroidSocketImpl::Listen(int aFd)
   NS_ENSURE_TRUE_VOID(flags >= 0);
 
   if (!(flags & O_NONBLOCK)) {
     int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags | O_NONBLOCK));
     NS_ENSURE_TRUE_VOID(!res);
   }
 
   SetFd(aFd);
+  mConnectionStatus = SOCKET_IS_LISTENING;
+
   AddWatchers(READ_WATCHER, true);
 }
 
 ssize_t
 DroidSocketImpl::ReadMsg(int aFd, void *aBuffer, size_t aLength)
 {
   ssize_t ret;
   struct msghdr msg;
@@ -528,16 +541,79 @@ DroidSocketImpl::ReadMsg(int aFd, void *
   }
 
   return ret;
 }
 
 void
 DroidSocketImpl::OnFileCanReadWithoutBlocking(int aFd)
 {
+  if (mConnectionStatus == SOCKET_IS_CONNECTED) {
+    OnSocketCanReceiveWithoutBlocking(aFd);
+  } else if (mConnectionStatus == SOCKET_IS_LISTENING) {
+    OnSocketCanAcceptWithoutBlocking(aFd);
+  } else if (mConnectionStatus == SOCKET_IS_CONNECTING) {
+    /* TODO: remove this branch when connect is handled by BluetoothInterface */
+    OnSocketCanConnectWithoutBlocking(aFd);
+  } else {
+    NS_NOTREACHED("invalid connection state for reading");
+  }
+}
+
+void
+DroidSocketImpl::OnSocketCanReceiveWithoutBlocking(int aFd)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!mShuttingDownOnIOThread);
+
+  // Read all of the incoming data.
+  while (true) {
+    nsAutoPtr<UnixSocketRawData> incoming(new UnixSocketRawData(MAX_READ_SIZE));
+
+    ssize_t ret = read(aFd, incoming->mData, incoming->mSize);
+
+    if (ret <= 0) {
+      if (ret == -1) {
+        if (errno == EINTR) {
+          continue; // retry system call when interrupted
+        }
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+          return; // no data available: return and re-poll
+        }
+
+        BT_WARNING("Cannot read from network");
+        // else fall through to error handling on other errno's
+      }
+
+      // We're done with our descriptors. Ensure that spurious events don't
+      // cause us to end up back here.
+      RemoveWatchers(READ_WATCHER | WRITE_WATCHER);
+      nsRefPtr<RequestClosingSocketTask> t = new RequestClosingSocketTask(this);
+      NS_DispatchToMainThread(t);
+      return;
+    }
+
+    incoming->mSize = ret;
+    nsRefPtr<SocketReceiveTask> t =
+      new SocketReceiveTask(this, incoming.forget());
+    NS_DispatchToMainThread(t);
+
+    // If ret is less than MAX_READ_SIZE, there's no
+    // more data in the socket for us to read now.
+    if (ret < ssize_t(MAX_READ_SIZE)) {
+      return;
+    }
+  }
+
+  MOZ_CRASH("We returned early");
+}
+
+void
+DroidSocketImpl::OnSocketCanAcceptWithoutBlocking(int aFd)
+{
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!mShuttingDownOnIOThread);
 
   // Read all of the incoming data.
   while (true) {
     nsAutoPtr<UnixSocketRawData> incoming(new UnixSocketRawData(MAX_READ_SIZE));
 
     ssize_t ret;
@@ -581,16 +657,28 @@ DroidSocketImpl::OnFileCanReadWithoutBlo
   }
 
   MOZ_CRASH("We returned early");
 }
 
 void
 DroidSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
 {
+  if (mConnectionStatus == SOCKET_IS_CONNECTED) {
+    OnSocketCanSendWithoutBlocking(aFd);
+  } else if (mConnectionStatus == SOCKET_IS_CONNECTING) {
+    OnSocketCanConnectWithoutBlocking(aFd);
+  } else {
+    NS_NOTREACHED("invalid connection state for writing");
+  }
+}
+
+void
+DroidSocketImpl::OnSocketCanSendWithoutBlocking(int aFd)
+{
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!mShuttingDownOnIOThread);
   MOZ_ASSERT(aFd >= 0);
 
   // 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
@@ -621,16 +709,69 @@ DroidSocketImpl::OnFileCanWriteWithoutBl
     if (data->mCurrentWriteOffset != data->mSize) {
       AddWatchers(WRITE_WATCHER, false);
     }
     mOutgoingQ.RemoveElementAt(0);
     delete data;
   }
 }
 
+void
+DroidSocketImpl::OnSocketCanConnectWithoutBlocking(int aFd)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!mShuttingDownOnIOThread);
+
+  // Read all of the incoming data.
+  while (true) {
+    nsAutoPtr<UnixSocketRawData> incoming(new UnixSocketRawData(MAX_READ_SIZE));
+
+    ssize_t ret;
+    if (!mReadMsgForClientFd) {
+      ret = read(aFd, incoming->mData, incoming->mSize);
+    } else {
+      ret = ReadMsg(aFd, incoming->mData, incoming->mSize);
+    }
+
+    if (ret <= 0) {
+      if (ret == -1) {
+        if (errno == EINTR) {
+          continue; // retry system call when interrupted
+        }
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+          return; // no data available: return and re-poll
+        }
+
+        BT_WARNING("Cannot read from network");
+        // else fall through to error handling on other errno's
+      }
+
+      // We're done with our descriptors. Ensure that spurious events don't
+      // cause us to end up back here.
+      RemoveWatchers(READ_WATCHER | WRITE_WATCHER);
+      nsRefPtr<RequestClosingSocketTask> t = new RequestClosingSocketTask(this);
+      NS_DispatchToMainThread(t);
+      return;
+    }
+
+    incoming->mSize = ret;
+    nsRefPtr<SocketReceiveTask> t =
+      new SocketReceiveTask(this, incoming.forget());
+    NS_DispatchToMainThread(t);
+
+    // If ret is less than MAX_READ_SIZE, there's no
+    // more data in the socket for us to read now.
+    if (ret < ssize_t(MAX_READ_SIZE)) {
+      return;
+    }
+  }
+
+  MOZ_CRASH("We returned early");
+}
+
 BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
                                  BluetoothSocketType aType,
                                  bool aAuth,
                                  bool aEncrypt)
   : mObserver(aObserver)
   , mImpl(nullptr)
   , mAuth(aAuth)
   , mEncrypt(aEncrypt)
@@ -653,17 +794,17 @@ BluetoothSocket::CloseDroidSocket()
   // From this point on, we consider mImpl as being deleted.
   // We sever the relationship here so any future calls to listen or connect
   // will create a new implementation.
   mImpl->ShutdownOnMainThread();
   XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
                                    new ShutdownSocketTask(mImpl));
   mImpl = nullptr;
 
-  OnDisconnect();
+  NotifyDisconnect();
 }
 
 class ConnectResultHandler MOZ_FINAL : public BluetoothSocketResultHandler
 {
 public:
   ConnectResultHandler(DroidSocketImpl* aImpl)
   : mImpl(aImpl)
   {
@@ -801,27 +942,25 @@ BluetoothSocket::ReceiveSocketInfo(nsAut
     ReadBdAddress(aMessage->mData, &offset, mDeviceAddress);
     int32_t channel = ReadInt32(aMessage->mData, &offset);
     int32_t connectionStatus = ReadInt32(aMessage->mData, &offset);
 
     BT_LOGR("size %d channel %d remote addr %s status %d",
       size, channel, NS_ConvertUTF16toUTF8(mDeviceAddress).get(), connectionStatus);
 
     if (connectionStatus != 0) {
-      OnConnectError();
+      NotifyError();
       return true;
     }
 
-    if (mIsServer) {
-      mImpl->mReadMsgForClientFd = false;
-      // Connect client fd on IO thread
-      XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-                                       new SocketConnectClientFdTask(mImpl));
-    }
-    OnConnectSuccess();
+    mImpl->mReadMsgForClientFd = false;
+    // Connect client fd on IO thread
+    XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                     new SocketConnectClientFdTask(mImpl));
+    NotifySuccess();
   }
 
   return true;
 }
 
 void
 BluetoothSocket::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage)
 {