summaryrefslogtreecommitdiffstats
path: root/ipc/unixsocket/ListenSocket.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /ipc/unixsocket/ListenSocket.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'ipc/unixsocket/ListenSocket.cpp')
-rw-r--r--ipc/unixsocket/ListenSocket.cpp432
1 files changed, 432 insertions, 0 deletions
diff --git a/ipc/unixsocket/ListenSocket.cpp b/ipc/unixsocket/ListenSocket.cpp
new file mode 100644
index 000000000..c05a4d701
--- /dev/null
+++ b/ipc/unixsocket/ListenSocket.cpp
@@ -0,0 +1,432 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* 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 "ListenSocket.h"
+#include <fcntl.h>
+#include "ConnectionOrientedSocket.h"
+#include "DataSocket.h"
+#include "ListenSocketConsumer.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Unused.h"
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
+#include "nsXULAppAPI.h"
+#include "UnixSocketConnector.h"
+
+namespace mozilla {
+namespace ipc {
+
+//
+// ListenSocketIO
+//
+
+class ListenSocketIO final
+ : public UnixSocketWatcher
+ , public SocketIOBase
+{
+public:
+ class ListenTask;
+
+ ListenSocketIO(MessageLoop* aConsumerLoop,
+ MessageLoop* aIOLoop,
+ ListenSocket* aListenSocket,
+ UnixSocketConnector* aConnector);
+ ~ListenSocketIO();
+
+ UnixSocketConnector* GetConnector() const;
+
+ // Task callback methods
+ //
+
+ /**
+ * Run bind/listen to prepare for further runs of accept()
+ */
+ void Listen(ConnectionOrientedSocketIO* aCOSocketIO);
+
+ // I/O callback methods
+ //
+
+ void OnConnected() override;
+ void OnError(const char* aFunction, int aErrno) override;
+ void OnListening() override;
+ void OnSocketCanAcceptWithoutBlocking() override;
+
+ // Methods for |SocketIOBase|
+ //
+
+ SocketBase* GetSocketBase() override;
+
+ bool IsShutdownOnConsumerThread() const override;
+ bool IsShutdownOnIOThread() const override;
+
+ void ShutdownOnConsumerThread() override;
+ void ShutdownOnIOThread() override;
+
+private:
+ void FireSocketError();
+
+ /**
+ * Consumer pointer. Non-thread-safe pointer, so should only be manipulated
+ * directly from consumer thread. All non-consumer-thread accesses should
+ * happen with mIO as container.
+ */
+ ListenSocket* mListenSocket;
+
+ /**
+ * Connector object used to create the connection we are currently using.
+ */
+ UniquePtr<UnixSocketConnector> mConnector;
+
+ /**
+ * If true, do not requeue whatever task we're running
+ */
+ bool mShuttingDownOnIOThread;
+
+ /**
+ * Number of valid bytes in |mAddress|
+ */
+ socklen_t mAddressLength;
+
+ /**
+ * Address structure of the socket currently in use
+ */
+ struct sockaddr_storage mAddress;
+
+ ConnectionOrientedSocketIO* mCOSocketIO;
+};
+
+ListenSocketIO::ListenSocketIO(MessageLoop* aConsumerLoop,
+ MessageLoop* aIOLoop,
+ ListenSocket* aListenSocket,
+ UnixSocketConnector* aConnector)
+ : UnixSocketWatcher(aIOLoop)
+ , SocketIOBase(aConsumerLoop)
+ , mListenSocket(aListenSocket)
+ , mConnector(aConnector)
+ , mShuttingDownOnIOThread(false)
+ , mAddressLength(0)
+ , mCOSocketIO(nullptr)
+{
+ MOZ_ASSERT(mListenSocket);
+ MOZ_ASSERT(mConnector);
+
+ MOZ_COUNT_CTOR_INHERITED(ListenSocketIO, SocketIOBase);
+}
+
+ListenSocketIO::~ListenSocketIO()
+{
+ MOZ_ASSERT(IsConsumerThread());
+ MOZ_ASSERT(IsShutdownOnConsumerThread());
+
+ MOZ_COUNT_DTOR_INHERITED(ListenSocketIO, SocketIOBase);
+}
+
+UnixSocketConnector*
+ListenSocketIO::GetConnector() const
+{
+ return mConnector.get();
+}
+
+void
+ListenSocketIO::Listen(ConnectionOrientedSocketIO* aCOSocketIO)
+{
+ MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+ MOZ_ASSERT(mConnector);
+ MOZ_ASSERT(aCOSocketIO);
+
+ struct sockaddr* address = reinterpret_cast<struct sockaddr*>(&mAddress);
+ mAddressLength = sizeof(mAddress);
+
+ if (!IsOpen()) {
+ int fd;
+ nsresult rv = mConnector->CreateListenSocket(address, &mAddressLength,
+ fd);
+ if (NS_FAILED(rv)) {
+ FireSocketError();
+ return;
+ }
+ SetFd(fd);
+ }
+
+ mCOSocketIO = aCOSocketIO;
+
+ // calls OnListening on success, or OnError otherwise
+ DebugOnly<nsresult> rv = UnixSocketWatcher::Listen(address, mAddressLength);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Listen failed");
+}
+
+void
+ListenSocketIO::OnConnected()
+{
+ MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+
+ NS_NOTREACHED("Invalid call to |ListenSocketIO::OnConnected|");
+}
+
+void
+ListenSocketIO::OnListening()
+{
+ MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+ MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING);
+
+ AddWatchers(READ_WATCHER, true);
+
+ /* We signal a successful 'connection' to a local address for listening. */
+ GetConsumerThread()->PostTask(
+ MakeAndAddRef<SocketEventTask>(this, SocketEventTask::CONNECT_SUCCESS));
+}
+
+void
+ListenSocketIO::OnError(const char* aFunction, int aErrno)
+{
+ MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+
+ UnixFdWatcher::OnError(aFunction, aErrno);
+ FireSocketError();
+}
+
+void
+ListenSocketIO::FireSocketError()
+{
+ MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+
+ // Clean up watchers, statuses, fds
+ Close();
+
+ // Tell the consumer thread we've errored
+ GetConsumerThread()->PostTask(
+ MakeAndAddRef<SocketEventTask>(this, SocketEventTask::CONNECT_ERROR));
+}
+
+void
+ListenSocketIO::OnSocketCanAcceptWithoutBlocking()
+{
+ MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+ MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING);
+ MOZ_ASSERT(mCOSocketIO);
+
+ RemoveWatchers(READ_WATCHER|WRITE_WATCHER);
+
+ struct sockaddr_storage storage;
+ socklen_t addressLength = sizeof(storage);
+
+ int fd;
+ nsresult rv = mConnector->AcceptStreamSocket(
+ GetFd(),
+ reinterpret_cast<struct sockaddr*>(&storage), &addressLength,
+ fd);
+ if (NS_FAILED(rv)) {
+ FireSocketError();
+ return;
+ }
+
+ mCOSocketIO->Accept(fd,
+ reinterpret_cast<struct sockaddr*>(&storage),
+ addressLength);
+}
+
+// |SocketIOBase|
+
+SocketBase*
+ListenSocketIO::GetSocketBase()
+{
+ return mListenSocket;
+}
+
+bool
+ListenSocketIO::IsShutdownOnConsumerThread() const
+{
+ MOZ_ASSERT(IsConsumerThread());
+
+ return mListenSocket == nullptr;
+}
+
+bool
+ListenSocketIO::IsShutdownOnIOThread() const
+{
+ return mShuttingDownOnIOThread;
+}
+
+void
+ListenSocketIO::ShutdownOnConsumerThread()
+{
+ MOZ_ASSERT(IsConsumerThread());
+ MOZ_ASSERT(!IsShutdownOnConsumerThread());
+
+ mListenSocket = nullptr;
+}
+
+void
+ListenSocketIO::ShutdownOnIOThread()
+{
+ MOZ_ASSERT(!IsConsumerThread());
+ MOZ_ASSERT(!mShuttingDownOnIOThread);
+
+ Close(); // will also remove fd from I/O loop
+ mShuttingDownOnIOThread = true;
+}
+
+//
+// Socket tasks
+//
+
+class ListenSocketIO::ListenTask final : public SocketIOTask<ListenSocketIO>
+{
+public:
+ ListenTask(ListenSocketIO* aIO, ConnectionOrientedSocketIO* aCOSocketIO)
+ : SocketIOTask<ListenSocketIO>(aIO)
+ , mCOSocketIO(aCOSocketIO)
+ {
+ MOZ_ASSERT(mCOSocketIO);
+
+ MOZ_COUNT_CTOR(ListenTask);
+ }
+
+ ~ListenTask()
+ {
+ MOZ_COUNT_DTOR(ListenTask);
+ }
+
+ NS_IMETHOD Run() override
+ {
+ MOZ_ASSERT(!GetIO()->IsConsumerThread());
+
+ if (!IsCanceled()) {
+ GetIO()->Listen(mCOSocketIO);
+ }
+ return NS_OK;
+ }
+
+private:
+ ConnectionOrientedSocketIO* mCOSocketIO;
+};
+
+//
+// ListenSocket
+//
+
+ListenSocket::ListenSocket(ListenSocketConsumer* aConsumer, int aIndex)
+ : mIO(nullptr)
+ , mConsumer(aConsumer)
+ , mIndex(aIndex)
+{
+ MOZ_ASSERT(mConsumer);
+
+ MOZ_COUNT_CTOR_INHERITED(ListenSocket, SocketBase);
+}
+
+ListenSocket::~ListenSocket()
+{
+ MOZ_ASSERT(!mIO);
+
+ MOZ_COUNT_DTOR_INHERITED(ListenSocket, SocketBase);
+}
+
+nsresult
+ListenSocket::Listen(UnixSocketConnector* aConnector,
+ MessageLoop* aConsumerLoop,
+ MessageLoop* aIOLoop,
+ ConnectionOrientedSocket* aCOSocket)
+{
+ MOZ_ASSERT(!mIO);
+
+ mIO = new ListenSocketIO(aConsumerLoop, aIOLoop, this, aConnector);
+
+ // Prepared I/O object, now start listening.
+ nsresult rv = Listen(aCOSocket);
+ if (NS_FAILED(rv)) {
+ delete mIO;
+ mIO = nullptr;
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+ListenSocket::Listen(UnixSocketConnector* aConnector,
+ ConnectionOrientedSocket* aCOSocket)
+{
+ return Listen(aConnector, MessageLoop::current(), XRE_GetIOMessageLoop(),
+ aCOSocket);
+}
+
+nsresult
+ListenSocket::Listen(ConnectionOrientedSocket* aCOSocket)
+{
+ MOZ_ASSERT(aCOSocket);
+ MOZ_ASSERT(mIO);
+
+ // We first prepare the connection-oriented socket with a
+ // socket connector and a socket I/O class.
+
+ UniquePtr<UnixSocketConnector> connector;
+ nsresult rv = mIO->GetConnector()->Duplicate(connector);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ ConnectionOrientedSocketIO* io;
+ rv = aCOSocket->PrepareAccept(connector.get(),
+ mIO->GetConsumerThread(), mIO->GetIOLoop(),
+ io);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ Unused << connector.release(); // now owned by |io|
+
+ // Then we start listening for connection requests.
+
+ SetConnectionStatus(SOCKET_LISTENING);
+
+ mIO->GetIOLoop()->PostTask(
+ MakeAndAddRef<ListenSocketIO::ListenTask>(mIO, io));
+
+ return NS_OK;
+}
+
+// |SocketBase|
+
+void
+ListenSocket::Close()
+{
+ if (!mIO) {
+ return;
+ }
+
+ MOZ_ASSERT(mIO->IsConsumerThread());
+
+ // From this point on, we consider mIO as being deleted. We sever
+ // the relationship here so any future calls to listen or connect
+ // will create a new implementation.
+ mIO->ShutdownOnConsumerThread();
+ mIO->GetIOLoop()->PostTask(MakeAndAddRef<SocketIOShutdownTask>(mIO));
+ mIO = nullptr;
+
+ NotifyDisconnect();
+}
+
+void
+ListenSocket::OnConnectSuccess()
+{
+ mConsumer->OnConnectSuccess(mIndex);
+}
+
+void
+ListenSocket::OnConnectError()
+{
+ mConsumer->OnConnectError(mIndex);
+}
+
+void
+ListenSocket::OnDisconnect()
+{
+ mConsumer->OnDisconnect(mIndex);
+}
+
+} // namespace ipc
+} // namespace mozilla