Bug 974410: Added watcher classes for Unix file descriptors, r=kyle

These new classes encasulate file-descriptor watchers, basic file
descriptors, and socket connections. Each class contains callback
methods for it's implemented functionality. Users should inherit
from the classes and overload the callback with their own code.
This commit is contained in:
Thomas Zimmermann 2014-02-26 17:51:52 +01:00
parent c71b7cc20d
commit 51ec78121a
8 changed files with 499 additions and 2 deletions

View File

@ -21,9 +21,9 @@ if CONFIG['MOZ_NFC']:
DIRS += ['nfc']
if CONFIG['MOZ_B2G_RIL'] or CONFIG['MOZ_B2G_BT'] or CONFIG['MOZ_NFC'] or CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
DIRS += ['unixsocket']
DIRS += ['unixfd', 'unixsocket']
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
DIRS += ['netd', 'keystore']
DIRS += ['keystore', 'netd']
TOOL_DIRS += ['app']

View File

@ -0,0 +1,119 @@
/* -*- 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 "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_WARN_IF(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());
mFd = aFd;
}
}
}

View File

@ -0,0 +1,62 @@
/* -*- 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 "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:
MessageLoop* mIOLoop;
ScopedClose mFd;
MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
MessageLoopForIO::FileDescriptorWatcher mWriteWatcher;
};
}
}

View File

@ -0,0 +1,44 @@
/* -*- 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());
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)
{
}
}
}

View File

@ -0,0 +1,28 @@
/* -*- 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 "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);
};
}
}

View File

@ -0,0 +1,155 @@
/* -*- 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);
// Select non-blocking IO.
if (TEMP_FAILURE_RETRY(fcntl(GetFd(), F_SETFL, O_NONBLOCK)) < 0) {
OnError("fcntl", errno);
return NS_ERROR_FAILURE;
}
if (connect(GetFd(), aAddr, aAddrLen) < 0) {
if (errno == EINPROGRESS) {
// Select blocking IO again, since we've now at least queue'd the connect
// as nonblock.
int flags = TEMP_FAILURE_RETRY(fcntl(GetFd(), F_GETFL, 0));
if (flags < 0) {
OnError("fcntl", errno);
return NS_ERROR_FAILURE;
}
if (TEMP_FAILURE_RETRY(fcntl(GetFd(), F_SETFL, flags&~O_NONBLOCK)) < 0) {
OnError("fcntl", errno);
return NS_ERROR_FAILURE;
}
mConnectionStatus = SOCKET_IS_CONNECTING;
// Set up a write watch to receive the connect signal
AddWatchers(WRITE_WATCHER, false);
} else {
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 (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) {
int fd = TEMP_FAILURE_RETRY(accept(GetFd(), NULL, NULL));
if (fd < 0) {
OnError("accept", errno);
} else {
OnAccepted(fd);
}
} 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");
}
}
}
}

View File

@ -0,0 +1,66 @@
/* -*- 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 "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() MOZ_OVERRIDE;
ConnectionStatus GetConnectionStatus() const
{
return mConnectionStatus;
}
// Connect to a peer
nsresult Connect(const struct sockaddr* aAddr, socklen_t aAddrLen);
// Listen on socket for incomming connection requests
nsresult Listen(const struct sockaddr* aAddr, socklen_t aAddrLen);
// Callback method for accepted connections
virtual void OnAccepted(int aFd) {};
// Callback method for successful connection requests
virtual void OnConnected() {};
// Callback method for successful listen requests
virtual void OnListening() {};
// 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) MOZ_OVERRIDE;
void OnFileCanWriteWithoutBlocking(int aFd) MOZ_OVERRIDE;
ConnectionStatus mConnectionStatus;
};
}
}

23
ipc/unixfd/moz.build Normal file
View File

@ -0,0 +1,23 @@
# -*- 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 += [
'UnixFdWatcher.h',
'UnixFileWatcher.h',
'UnixSocketWatcher.h'
]
SOURCES += [
'UnixFdWatcher.cpp',
'UnixFileWatcher.cpp',
'UnixSocketWatcher.cpp'
]
FAIL_ON_WARNINGS = True
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'