author | Thomas Zimmermann <tdz@users.sourceforge.net> |
Tue, 02 Dec 2014 15:00:36 -0800 | |
changeset 218245 | 1840df9fd1906227e7d782e1e964e5ee333d77c3 |
parent 218244 | 62f3c073833a0dc39c02b62e5cc8eaaa7c7bdd3c |
child 218246 | 73e1dda14ec9fcdc43523bace69ac6cda2c86661 |
push id | 27924 |
push user | cbook@mozilla.com |
push date | Wed, 03 Dec 2014 12:26:46 +0000 |
treeherder | mozilla-central@7ea9f701465a [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | kyle |
bugs | 1093025 |
milestone | 37.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
|
new file mode 100644 --- /dev/null +++ b/ipc/unixsocket/ConnectionOrientedSocket.cpp @@ -0,0 +1,19 @@ +/* -*- 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 "ConnectionOrientedSocket.h" + +namespace mozilla { +namespace ipc { + +ConnectionOrientedSocketIO::~ConnectionOrientedSocketIO() +{ } + +ConnectionOrientedSocket::~ConnectionOrientedSocket() +{ } + +} +}
new file mode 100644 --- /dev/null +++ b/ipc/unixsocket/ConnectionOrientedSocket.h @@ -0,0 +1,47 @@ +/* -*- 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/. */ + +#ifndef mozilla_ipc_connectionorientedsocket_h +#define mozilla_ipc_connectionorientedsocket_h + +#include <sys/socket.h> +#include "nsError.h" + +namespace mozilla { +namespace ipc { + +union sockaddr_any; + +/* + * |ConnectionOrientedSocketIO| and |ConnectionOrientedSocket| define + * interfaces for implementing stream sockets on I/O and main thread. + * |ListenSocket| uses these classes to handle accepted sockets. + */ + +class ConnectionOrientedSocketIO +{ +public: + virtual nsresult Accept(int aFd, + const union sockaddr_any* aAddr, + socklen_t aAddrLen) = 0; + +protected: + virtual ~ConnectionOrientedSocketIO(); +}; + +class ConnectionOrientedSocket +{ +public: + virtual ConnectionOrientedSocketIO* GetIO() = 0; + +protected: + virtual ~ConnectionOrientedSocket(); +}; + +} +} + +#endif
new file mode 100644 --- /dev/null +++ b/ipc/unixsocket/ListenSocket.cpp @@ -0,0 +1,439 @@ +/* -*- 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 "mozilla/RefPtr.h" +#include "nsXULAppAPI.h" +#include "UnixSocketConnector.h" + +static const size_t MAX_READ_SIZE = 1; /* any small constant */ + +namespace mozilla { +namespace ipc { + +// +// ListenSocketIO +// + +class ListenSocketIO MOZ_FINAL : public UnixSocketWatcher + , protected SocketIOBase +{ +public: + class ListenTask; + + ListenSocketIO(MessageLoop* mIOLoop, + ListenSocket* aListenSocket, + UnixSocketConnector* aConnector, + const nsACString& aAddress); + ~ListenSocketIO(); + + void GetSocketAddr(nsAString& aAddrStr) const; + SocketConsumerBase* GetConsumer(); + SocketBase* GetSocketBase(); + + // Shutdown state + // + + bool IsShutdownOnMainThread() const; + void ShutdownOnMainThread(); + + bool IsShutdownOnIOThread() const; + void ShutdownOnIOThread(); + + // Task callback methods + // + + /** + * Run bind/listen to prepare for further runs of accept() + */ + void Listen(ConnectionOrientedSocketIO* aCOSocketIO); + + // I/O callback methods + // + + void OnAccepted(int aFd, const sockaddr_any* aAddr, + socklen_t aAddrLen) MOZ_OVERRIDE; + void OnConnected() MOZ_OVERRIDE; + void OnError(const char* aFunction, int aErrno) MOZ_OVERRIDE; + void OnListening() MOZ_OVERRIDE; + +private: + void FireSocketError(); + + // Set up flags on file descriptor. + static bool SetSocketFlags(int aFd); + + /** + * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated + * directly from main thread. All non-main-thread accesses should happen with + * mIO as container. + */ + RefPtr<ListenSocket> mListenSocket; + + /** + * Connector object used to create the connection we are currently using. + */ + nsAutoPtr<UnixSocketConnector> mConnector; + + /** + * If true, do not requeue whatever task we're running + */ + bool mShuttingDownOnIOThread; + + /** + * Address we are connecting to, assuming we are creating a client connection. + */ + nsCString mAddress; + + /** + * Size of the socket address struct + */ + socklen_t mAddrSize; + + /** + * Address struct of the socket currently in use + */ + sockaddr_any mAddr; + + ConnectionOrientedSocketIO* mCOSocketIO; +}; + +ListenSocketIO::ListenSocketIO(MessageLoop* mIOLoop, + ListenSocket* aListenSocket, + UnixSocketConnector* aConnector, + const nsACString& aAddress) +: UnixSocketWatcher(mIOLoop) +, SocketIOBase(MAX_READ_SIZE) +, mListenSocket(aListenSocket) +, mConnector(aConnector) +, mShuttingDownOnIOThread(false) +, mAddress(aAddress) +, mCOSocketIO(nullptr) +{ + MOZ_ASSERT(mListenSocket); + MOZ_ASSERT(mConnector); +} + +ListenSocketIO::~ListenSocketIO() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(IsShutdownOnMainThread()); +} + +void +ListenSocketIO::GetSocketAddr(nsAString& aAddrStr) const +{ + if (!mConnector) { + NS_WARNING("No connector to get socket address from!"); + aAddrStr.Truncate(); + return; + } + mConnector->GetSocketAddr(mAddr, aAddrStr); +} + +SocketBase* +ListenSocketIO::GetSocketBase() +{ + return mListenSocket.get(); +} + +bool +ListenSocketIO::IsShutdownOnMainThread() const +{ + MOZ_ASSERT(NS_IsMainThread()); + + return mListenSocket == nullptr; +} + +void +ListenSocketIO::ShutdownOnMainThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IsShutdownOnMainThread()); + + mListenSocket = nullptr; +} + +bool +ListenSocketIO::IsShutdownOnIOThread() const +{ + return mShuttingDownOnIOThread; +} + +void +ListenSocketIO::ShutdownOnIOThread() +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!mShuttingDownOnIOThread); + + Close(); // will also remove fd from I/O loop + mShuttingDownOnIOThread = true; +} + +void +ListenSocketIO::Listen(ConnectionOrientedSocketIO* aCOSocketIO) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(mConnector); + MOZ_ASSERT(aCOSocketIO); + + if (!IsOpen()) { + int fd = mConnector->Create(); + if (fd < 0) { + NS_WARNING("Cannot create socket fd!"); + FireSocketError(); + return; + } + if (!SetSocketFlags(fd)) { + NS_WARNING("Cannot set socket flags!"); + FireSocketError(); + return; + } + SetFd(fd); + } + + mCOSocketIO = aCOSocketIO; + + // This will set things we don't particularly care about, but + // it will hand back the correct structure size which is what + // we do care about. + if (!mConnector->CreateAddr(true, mAddrSize, mAddr, nullptr)) { + NS_WARNING("Cannot create socket address!"); + FireSocketError(); + return; + } + + // calls OnListening on success, or OnError otherwise + nsresult rv = UnixSocketWatcher::Listen( + reinterpret_cast<struct sockaddr*>(&mAddr), mAddrSize); + NS_WARN_IF(NS_FAILED(rv)); +} + +void +ListenSocketIO::OnAccepted(int aFd, + const sockaddr_any* aAddr, + socklen_t aAddrLen) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING); + MOZ_ASSERT(mCOSocketIO); + + RemoveWatchers(READ_WATCHER|WRITE_WATCHER); + + nsRefPtr<nsRunnable> runnable; + + if (NS_SUCCEEDED(mCOSocketIO->Accept(aFd, aAddr, aAddrLen))) { + runnable = + new SocketIOEventRunnable<ListenSocketIO>( + this, SocketIOEventRunnable<ListenSocketIO>::CONNECT_SUCCESS); + return; + } else { + runnable = + new SocketIOEventRunnable<ListenSocketIO>( + this, SocketIOEventRunnable<ListenSocketIO>::CONNECT_ERROR); + } + + NS_DispatchToMainThread(runnable); +} + +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); + + if (!mConnector->SetUpListenSocket(GetFd())) { + NS_WARNING("Could not set up listen socket!"); + FireSocketError(); + return; + } + + AddWatchers(READ_WATCHER, true); +} + +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 main thread we've errored + nsRefPtr<nsRunnable> r = + new SocketIOEventRunnable<ListenSocketIO>( + this, SocketIOEventRunnable<ListenSocketIO>::CONNECT_ERROR); + + NS_DispatchToMainThread(r); +} + +bool +ListenSocketIO::SetSocketFlags(int aFd) +{ + static const int reuseaddr = 1; + + // Set socket addr to be reused even if kernel is still waiting to close + int res = setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, + &reuseaddr, sizeof(reuseaddr)); + if (res < 0) { + return false; + } + + // Set close-on-exec bit. + int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFD)); + if (-1 == flags) { + return false; + } + flags |= FD_CLOEXEC; + if (-1 == TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFD, flags))) { + return false; + } + + // Set non-blocking status flag. + flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL)); + if (-1 == flags) { + return false; + } + flags |= O_NONBLOCK; + if (-1 == TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags))) { + return false; + } + + return true; +} + +// +// Socket tasks +// + +class ListenSocketIO::ListenTask MOZ_FINAL + : public SocketIOTask<ListenSocketIO> +{ +public: + ListenTask(ListenSocketIO* aIO, ConnectionOrientedSocketIO* aCOSocketIO) + : SocketIOTask<ListenSocketIO>(aIO) + , mCOSocketIO(aCOSocketIO) + { + MOZ_ASSERT(mCOSocketIO); + } + + void Run() MOZ_OVERRIDE + { + MOZ_ASSERT(!NS_IsMainThread()); + + if (!IsCanceled()) { + GetIO()->Listen(mCOSocketIO); + } + } + +private: + ConnectionOrientedSocketIO* mCOSocketIO; +}; + +// +// UnixSocketConsumer +// + +ListenSocket::ListenSocket() +: mIO(nullptr) +{ } + +ListenSocket::~ListenSocket() +{ + MOZ_ASSERT(!mIO); +} + +void +ListenSocket::Close() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!mIO) { + return; + } + + // 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->ShutdownOnMainThread(); + + XRE_GetIOMessageLoop()->PostTask( + FROM_HERE, new SocketIOShutdownTask<ListenSocketIO>(mIO)); + + mIO = nullptr; + + NotifyDisconnect(); +} + +bool +ListenSocket::Listen(UnixSocketConnector* aConnector, + ConnectionOrientedSocket* aCOSocket) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aConnector); + MOZ_ASSERT(aCOSocket); + + nsAutoPtr<UnixSocketConnector> connector(aConnector); + + if (mIO) { + NS_WARNING("Socket already connecting/connected!"); + return false; + } + + mIO = new ListenSocketIO( + XRE_GetIOMessageLoop(), this, connector.forget(), EmptyCString()); + + // Prepared I/O object, now start listening. + return Listen(aCOSocket); +} + +bool +ListenSocket::Listen(ConnectionOrientedSocket* aCOSocket) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mIO); + MOZ_ASSERT(aCOSocket); + + SetConnectionStatus(SOCKET_LISTENING); + + XRE_GetIOMessageLoop()->PostTask( + FROM_HERE, new ListenSocketIO::ListenTask(mIO, aCOSocket->GetIO())); + + return true; +} + +void +ListenSocket::GetSocketAddr(nsAString& aAddrStr) +{ + aAddrStr.Truncate(); + if (!mIO || GetConnectionStatus() != SOCKET_CONNECTED) { + NS_WARNING("No socket currently open!"); + return; + } + mIO->GetSocketAddr(aAddrStr); +} + +} // namespace ipc +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/ipc/unixsocket/ListenSocket.h @@ -0,0 +1,78 @@ +/* -*- 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/. */ + +#ifndef mozilla_ipc_listensocket_h +#define mozilla_ipc_listensocket_h + +#include "nsString.h" +#include "mozilla/ipc/SocketBase.h" + +namespace mozilla { +namespace ipc { + +class ConnectionOrientedSocket; +class ListenSocketIO; +class UnixSocketConnector; + +class ListenSocket : public SocketBase +{ +protected: + virtual ~ListenSocket(); + +public: + ListenSocket(); + + /** + * Starts a task on the socket that will try to accept a new connection + * in a non-blocking manner. + * + * @param aConnector Connector object for socket-type-specific functions + * @param aCOSocket The connection-oriented socket for handling the + * accepted connection. + * + * @return true on listen started, false otherwise + */ + bool Listen(UnixSocketConnector* aConnector, + ConnectionOrientedSocket* aCOSocket); + + /** + * Starts a task on the socket that will try to accept a new connection + * in a non-blocking manner. This method re-uses a previously created + * listen socket. + * + * @param aCOSocket The connection-oriented socket for handling the + * accepted connection. + * + * @return true on listen started, false otherwise + */ + bool Listen(ConnectionOrientedSocket* aCOSocket); + + /** + * Queues the internal representation of socket for deletion. Can be called + * from main thread. + */ + void Close(); + + /** + * Get the current sockaddr for the socket + */ + void GetSocketAddr(nsAString& aAddrStr); + +private: + + // Legacy interface from |SocketBase|; should be replaced by |Close|. + void CloseSocket() MOZ_OVERRIDE + { + Close(); + } + + ListenSocketIO* mIO; +}; + +} // namespace ipc +} // namepsace mozilla + +#endif // mozilla_ipc_listensocket_h
--- a/ipc/unixsocket/moz.build +++ b/ipc/unixsocket/moz.build @@ -1,21 +1,25 @@ # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # 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/. EXPORTS.mozilla.ipc += [ + 'ConnectionOrientedSocket.h', + 'ListenSocket.h', 'SocketBase.h', 'UnixSocket.h', 'UnixSocketConnector.h' ] SOURCES += [ + 'ConnectionOrientedSocket.cpp', + 'ListenSocket.cpp', 'SocketBase.cpp', 'UnixSocket.cpp', 'UnixSocketConnector.cpp' ] FAIL_ON_WARNINGS = True include('/ipc/chromium/chromium-config.mozbuild')