diff options
Diffstat (limited to 'ipc/unixfd')
-rw-r--r-- | ipc/unixfd/UnixFdWatcher.cpp | 128 | ||||
-rw-r--r-- | ipc/unixfd/UnixFdWatcher.h | 69 | ||||
-rw-r--r-- | ipc/unixfd/UnixFileWatcher.cpp | 45 | ||||
-rw-r--r-- | ipc/unixfd/UnixFileWatcher.h | 33 | ||||
-rw-r--r-- | ipc/unixfd/UnixSocketWatcher.cpp | 136 | ||||
-rw-r--r-- | ipc/unixfd/UnixSocketWatcher.h | 72 | ||||
-rw-r--r-- | ipc/unixfd/moz.build | 21 |
7 files changed, 504 insertions, 0 deletions
diff --git a/ipc/unixfd/UnixFdWatcher.cpp b/ipc/unixfd/UnixFdWatcher.cpp new file mode 100644 index 000000000..41289a90b --- /dev/null +++ b/ipc/unixfd/UnixFdWatcher.cpp @@ -0,0 +1,128 @@ +/* -*- 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 <fcntl.h> +#include "UnixFdWatcher.h" + +#ifdef CHROMIUM_LOG +#undef CHROMIUM_LOG +#endif + +#if defined(MOZ_WIDGET_GONK) +#include <android/log.h> +#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "I/O", args); +#else +#include <stdio.h> +#define IODEBUG true +#define CHROMIUM_LOG(args...) if (IODEBUG) printf(args); +#endif + +namespace mozilla { +namespace ipc { + +UnixFdWatcher::~UnixFdWatcher() +{ + NS_WARNING_ASSERTION(!IsOpen(), "mFd should have been closed already"); +} + +void +UnixFdWatcher::Close() +{ + MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop); + + if (NS_WARN_IF(!IsOpen())) { + /* mFd should have been open */ + return; + } + OnClose(); + RemoveWatchers(READ_WATCHER|WRITE_WATCHER); + mFd.dispose(); +} + +void +UnixFdWatcher::AddWatchers(unsigned long aWatchers, bool aPersistent) +{ + MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop); + MOZ_ASSERT(IsOpen()); + + // Before we add a watcher, we need to remove it! Removing is always + // safe, but adding the same watcher twice can lead to endless loops + // inside libevent. + RemoveWatchers(aWatchers); + + if (aWatchers & READ_WATCHER) { + MessageLoopForIO::current()->WatchFileDescriptor( + mFd, + aPersistent, + MessageLoopForIO::WATCH_READ, + &mReadWatcher, + this); + } + if (aWatchers & WRITE_WATCHER) { + MessageLoopForIO::current()->WatchFileDescriptor( + mFd, + aPersistent, + MessageLoopForIO::WATCH_WRITE, + &mWriteWatcher, + this); + } +} + +void +UnixFdWatcher::RemoveWatchers(unsigned long aWatchers) +{ + MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop); + MOZ_ASSERT(IsOpen()); + + if (aWatchers & READ_WATCHER) { + mReadWatcher.StopWatchingFileDescriptor(); + } + if (aWatchers & WRITE_WATCHER) { + mWriteWatcher.StopWatchingFileDescriptor(); + } +} + +void +UnixFdWatcher::OnError(const char* aFunction, int aErrno) +{ + MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop); + + CHROMIUM_LOG("%s failed with error %d (%s)", + aFunction, aErrno, strerror(aErrno)); +} + +UnixFdWatcher::UnixFdWatcher(MessageLoop* aIOLoop) +: mIOLoop(aIOLoop) +{ + MOZ_ASSERT(mIOLoop); +} + +UnixFdWatcher::UnixFdWatcher(MessageLoop* aIOLoop, int aFd) +: mIOLoop(aIOLoop) +, mFd(aFd) +{ + MOZ_ASSERT(mIOLoop); +} + +void +UnixFdWatcher::SetFd(int aFd) +{ + MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop); + MOZ_ASSERT(!IsOpen()); + MOZ_ASSERT(FdIsNonBlocking(aFd)); + + mFd = aFd; +} + +bool +UnixFdWatcher::FdIsNonBlocking(int aFd) +{ + int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL)); + return (flags > 0) && (flags & O_NONBLOCK); +} + +} +} diff --git a/ipc/unixfd/UnixFdWatcher.h b/ipc/unixfd/UnixFdWatcher.h new file mode 100644 index 000000000..676b1dbf0 --- /dev/null +++ b/ipc/unixfd/UnixFdWatcher.h @@ -0,0 +1,69 @@ +/* -*- 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_UnixFdWatcher_h +#define mozilla_ipc_UnixFdWatcher_h + +#include "base/message_loop.h" +#include "mozilla/FileUtils.h" + +namespace mozilla { +namespace ipc { + +class UnixFdWatcher : public MessageLoopForIO::Watcher +{ +public: + enum { + READ_WATCHER = 1<<0, + WRITE_WATCHER = 1<<1 + }; + + virtual ~UnixFdWatcher(); + + MessageLoop* GetIOLoop() const + { + return mIOLoop; + } + + int GetFd() const + { + return mFd; + } + + bool IsOpen() const + { + return GetFd() >= 0; + } + + virtual void Close(); + + void AddWatchers(unsigned long aWatchers, bool aPersistent); + void RemoveWatchers(unsigned long aWatchers); + + // Callback method that's run before closing the file descriptor + virtual void OnClose() {}; + + // Callback method that's run on POSIX errors + virtual void OnError(const char* aFunction, int aErrno); + +protected: + UnixFdWatcher(MessageLoop* aIOLoop); + UnixFdWatcher(MessageLoop* aIOLoop, int aFd); + void SetFd(int aFd); + +private: + static bool FdIsNonBlocking(int aFd); + + MessageLoop* mIOLoop; + ScopedClose mFd; + MessageLoopForIO::FileDescriptorWatcher mReadWatcher; + MessageLoopForIO::FileDescriptorWatcher mWriteWatcher; +}; + +} +} + +#endif diff --git a/ipc/unixfd/UnixFileWatcher.cpp b/ipc/unixfd/UnixFileWatcher.cpp new file mode 100644 index 000000000..67e247b5b --- /dev/null +++ b/ipc/unixfd/UnixFileWatcher.cpp @@ -0,0 +1,45 @@ +/* -*- 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 <fcntl.h> +#include "UnixFileWatcher.h" + +namespace mozilla { +namespace ipc { + +UnixFileWatcher::~UnixFileWatcher() +{ +} + +nsresult +UnixFileWatcher::Open(const char* aFilename, int aFlags, mode_t aMode) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(aFlags & O_NONBLOCK); + + int fd = TEMP_FAILURE_RETRY(open(aFilename, aFlags, aMode)); + if (fd < 0) { + OnError("open", errno); + return NS_ERROR_FAILURE; + } + SetFd(fd); + OnOpened(); + + return NS_OK; +} + +UnixFileWatcher::UnixFileWatcher(MessageLoop* aIOLoop) +: UnixFdWatcher(aIOLoop) +{ +} + +UnixFileWatcher::UnixFileWatcher(MessageLoop* aIOLoop, int aFd) +: UnixFdWatcher(aIOLoop, aFd) +{ +} + +} +} diff --git a/ipc/unixfd/UnixFileWatcher.h b/ipc/unixfd/UnixFileWatcher.h new file mode 100644 index 000000000..5a19b1005 --- /dev/null +++ b/ipc/unixfd/UnixFileWatcher.h @@ -0,0 +1,33 @@ +/* -*- 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_UnixFileWatcher_h +#define mozilla_ipc_UnixFileWatcher_h + +#include "UnixFdWatcher.h" + +namespace mozilla { +namespace ipc { + +class UnixFileWatcher : public UnixFdWatcher +{ +public: + virtual ~UnixFileWatcher(); + + nsresult Open(const char* aFilename, int aFlags, mode_t aMode = 0); + + // Callback method for successful open requests + virtual void OnOpened() {}; + +protected: + UnixFileWatcher(MessageLoop* aIOLoop); + UnixFileWatcher(MessageLoop* aIOLoop, int aFd); +}; + +} +} + +#endif diff --git a/ipc/unixfd/UnixSocketWatcher.cpp b/ipc/unixfd/UnixSocketWatcher.cpp new file mode 100644 index 000000000..2ce32e93d --- /dev/null +++ b/ipc/unixfd/UnixSocketWatcher.cpp @@ -0,0 +1,136 @@ +/* -*- 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 <fcntl.h> +#include "UnixSocketWatcher.h" + +namespace mozilla { +namespace ipc { + +UnixSocketWatcher::~UnixSocketWatcher() +{ +} + +void UnixSocketWatcher::Close() +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + + mConnectionStatus = SOCKET_IS_DISCONNECTED; + UnixFdWatcher::Close(); +} + +nsresult +UnixSocketWatcher::Connect(const struct sockaddr* aAddr, socklen_t aAddrLen) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(IsOpen()); + MOZ_ASSERT(aAddr || !aAddrLen); + + if (TEMP_FAILURE_RETRY(connect(GetFd(), aAddr, aAddrLen) < 0)) { + if (errno == EINPROGRESS) { + mConnectionStatus = SOCKET_IS_CONNECTING; + // Set up a write watch to receive the connect signal + AddWatchers(WRITE_WATCHER, false); + return NS_OK; + } + OnError("connect", errno); + return NS_ERROR_FAILURE; + } + + mConnectionStatus = SOCKET_IS_CONNECTED; + OnConnected(); + + return NS_OK; +} + +nsresult +UnixSocketWatcher::Listen(const struct sockaddr* aAddr, socklen_t aAddrLen) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(IsOpen()); + MOZ_ASSERT(aAddr || !aAddrLen); + + if (mConnectionStatus == SOCKET_IS_DISCONNECTED) { + // We init the socket descriptor when we listen for the first time. + if (bind(GetFd(), aAddr, aAddrLen) < 0) { + OnError("bind", errno); + return NS_ERROR_FAILURE; + } + if (listen(GetFd(), 1) < 0) { + OnError("listen", errno); + return NS_ERROR_FAILURE; + } + } + mConnectionStatus = SOCKET_IS_LISTENING; + OnListening(); + + return NS_OK; +} + +UnixSocketWatcher::UnixSocketWatcher(MessageLoop* aIOLoop) +: UnixFdWatcher(aIOLoop) +, mConnectionStatus(SOCKET_IS_DISCONNECTED) +{ +} + +UnixSocketWatcher::UnixSocketWatcher(MessageLoop* aIOLoop, int aFd, + ConnectionStatus aConnectionStatus) +: UnixFdWatcher(aIOLoop, aFd) +, mConnectionStatus(aConnectionStatus) +{ +} + +void +UnixSocketWatcher::SetSocket(int aFd, ConnectionStatus aConnectionStatus) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + + SetFd(aFd); + mConnectionStatus = aConnectionStatus; +} + +void +UnixSocketWatcher::OnFileCanReadWithoutBlocking(int aFd) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(aFd == GetFd()); + + if (mConnectionStatus == SOCKET_IS_CONNECTED) { + OnSocketCanReceiveWithoutBlocking(); + } else if (mConnectionStatus == SOCKET_IS_LISTENING) { + OnSocketCanAcceptWithoutBlocking(); + } else { + NS_NOTREACHED("invalid connection state for reading"); + } +} + +void +UnixSocketWatcher::OnFileCanWriteWithoutBlocking(int aFd) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(aFd == GetFd()); + + if (mConnectionStatus == SOCKET_IS_CONNECTED) { + OnSocketCanSendWithoutBlocking(); + } else if (mConnectionStatus == SOCKET_IS_CONNECTING) { + RemoveWatchers(WRITE_WATCHER); + int error = 0; + socklen_t len = sizeof(error); + if (getsockopt(GetFd(), SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + OnError("getsockopt", errno); + } else if (error) { + OnError("connect", error); + } else { + mConnectionStatus = SOCKET_IS_CONNECTED; + OnConnected(); + } + } else { + NS_NOTREACHED("invalid connection state for writing"); + } +} + +} +} diff --git a/ipc/unixfd/UnixSocketWatcher.h b/ipc/unixfd/UnixSocketWatcher.h new file mode 100644 index 000000000..37a0acc0d --- /dev/null +++ b/ipc/unixfd/UnixSocketWatcher.h @@ -0,0 +1,72 @@ +/* -*- 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_UnixSocketWatcher_h +#define mozilla_ipc_UnixSocketWatcher_h + +#include <sys/socket.h> +#include "UnixFdWatcher.h" + +namespace mozilla { +namespace ipc { + +class UnixSocketWatcher : public UnixFdWatcher +{ +public: + enum ConnectionStatus { + SOCKET_IS_DISCONNECTED = 0, + SOCKET_IS_LISTENING, + SOCKET_IS_CONNECTING, + SOCKET_IS_CONNECTED + }; + + virtual ~UnixSocketWatcher(); + + virtual void Close() override; + + ConnectionStatus GetConnectionStatus() const + { + return mConnectionStatus; + } + + // Connect to a peer + nsresult Connect(const struct sockaddr* aAddr, socklen_t aAddrLen); + + // Listen on socket for incoming connection requests + nsresult Listen(const struct sockaddr* aAddr, socklen_t aAddrLen); + + // Callback method for successful connection requests + virtual void OnConnected() {}; + + // Callback method for successful listen requests + virtual void OnListening() {}; + + // Callback method for accepting from a listening socket + virtual void OnSocketCanAcceptWithoutBlocking() {}; + + // Callback method for receiving from socket + virtual void OnSocketCanReceiveWithoutBlocking() {}; + + // Callback method for sending on socket + virtual void OnSocketCanSendWithoutBlocking() {}; + +protected: + UnixSocketWatcher(MessageLoop* aIOLoop); + UnixSocketWatcher(MessageLoop* aIOLoop, int aFd, + ConnectionStatus aConnectionStatus); + void SetSocket(int aFd, ConnectionStatus aConnectionStatus); + +private: + void OnFileCanReadWithoutBlocking(int aFd) override; + void OnFileCanWriteWithoutBlocking(int aFd) override; + + ConnectionStatus mConnectionStatus; +}; + +} +} + +#endif diff --git a/ipc/unixfd/moz.build b/ipc/unixfd/moz.build new file mode 100644 index 000000000..fdc8a8709 --- /dev/null +++ b/ipc/unixfd/moz.build @@ -0,0 +1,21 @@ +# -*- Mode: python; 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 += [ + 'UnixFdWatcher.h', + 'UnixFileWatcher.h', + 'UnixSocketWatcher.h' +] + +SOURCES += [ + 'UnixFdWatcher.cpp', + 'UnixFileWatcher.cpp', + 'UnixSocketWatcher.cpp' +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' |