mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-14 03:07:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
202
ipc/unixsocket/ConnectionOrientedSocket.cpp
Normal file
202
ipc/unixsocket/ConnectionOrientedSocket.cpp
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* -*- 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"
|
||||
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
|
||||
#include "UnixSocketConnector.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
//
|
||||
// ConnectionOrientedSocketIO
|
||||
//
|
||||
|
||||
ConnectionOrientedSocketIO::ConnectionOrientedSocketIO(
|
||||
MessageLoop* aConsumerLoop,
|
||||
MessageLoop* aIOLoop,
|
||||
int aFd, ConnectionStatus aConnectionStatus,
|
||||
UnixSocketConnector* aConnector)
|
||||
: DataSocketIO(aConsumerLoop)
|
||||
, UnixSocketWatcher(aIOLoop, aFd, aConnectionStatus)
|
||||
, mConnector(aConnector)
|
||||
, mPeerAddressLength(0)
|
||||
{
|
||||
MOZ_ASSERT(mConnector);
|
||||
|
||||
MOZ_COUNT_CTOR_INHERITED(ConnectionOrientedSocketIO, DataSocketIO);
|
||||
}
|
||||
|
||||
ConnectionOrientedSocketIO::ConnectionOrientedSocketIO(
|
||||
MessageLoop* aConsumerLoop,
|
||||
MessageLoop* aIOLoop,
|
||||
UnixSocketConnector* aConnector)
|
||||
: DataSocketIO(aConsumerLoop)
|
||||
, UnixSocketWatcher(aIOLoop)
|
||||
, mConnector(aConnector)
|
||||
, mPeerAddressLength(0)
|
||||
{
|
||||
MOZ_ASSERT(mConnector);
|
||||
|
||||
MOZ_COUNT_CTOR_INHERITED(ConnectionOrientedSocketIO, DataSocketIO);
|
||||
}
|
||||
|
||||
ConnectionOrientedSocketIO::~ConnectionOrientedSocketIO()
|
||||
{
|
||||
MOZ_COUNT_DTOR_INHERITED(ConnectionOrientedSocketIO, DataSocketIO);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ConnectionOrientedSocketIO::Accept(int aFd,
|
||||
const struct sockaddr* aPeerAddress,
|
||||
socklen_t aPeerAddressLength)
|
||||
{
|
||||
MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
|
||||
MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTING);
|
||||
|
||||
SetSocket(aFd, SOCKET_IS_CONNECTED);
|
||||
|
||||
// Address setup
|
||||
mPeerAddressLength = aPeerAddressLength;
|
||||
memcpy(&mPeerAddress, aPeerAddress, mPeerAddressLength);
|
||||
|
||||
// Signal success and start data transfer
|
||||
OnConnected();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ConnectionOrientedSocketIO::Connect()
|
||||
{
|
||||
MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
|
||||
MOZ_ASSERT(!IsOpen());
|
||||
|
||||
struct sockaddr* peerAddress =
|
||||
reinterpret_cast<struct sockaddr*>(&mPeerAddress);
|
||||
mPeerAddressLength = sizeof(mPeerAddress);
|
||||
|
||||
int fd;
|
||||
nsresult rv = mConnector->CreateStreamSocket(peerAddress,
|
||||
&mPeerAddressLength,
|
||||
fd);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Tell the consumer thread we've errored
|
||||
GetConsumerThread()->PostTask(
|
||||
MakeAndAddRef<SocketEventTask>(this, SocketEventTask::CONNECT_ERROR));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
SetFd(fd);
|
||||
|
||||
// calls OnConnected() on success, or OnError() otherwise
|
||||
rv = UnixSocketWatcher::Connect(peerAddress, mPeerAddressLength);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionOrientedSocketIO::Send(UnixSocketIOBuffer* aBuffer)
|
||||
{
|
||||
MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
|
||||
|
||||
EnqueueData(aBuffer);
|
||||
AddWatchers(WRITE_WATCHER, false);
|
||||
}
|
||||
|
||||
// |UnixSocketWatcher|
|
||||
|
||||
void
|
||||
ConnectionOrientedSocketIO::OnSocketCanReceiveWithoutBlocking()
|
||||
{
|
||||
MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
|
||||
MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); // see bug 990984
|
||||
|
||||
ssize_t res = ReceiveData(GetFd());
|
||||
if (res < 0) {
|
||||
/* I/O error */
|
||||
RemoveWatchers(READ_WATCHER|WRITE_WATCHER);
|
||||
} else if (!res) {
|
||||
/* EOF or peer shutdown */
|
||||
RemoveWatchers(READ_WATCHER);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionOrientedSocketIO::OnSocketCanSendWithoutBlocking()
|
||||
{
|
||||
MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
|
||||
MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); // see bug 990984
|
||||
MOZ_ASSERT(!IsShutdownOnIOThread());
|
||||
|
||||
nsresult rv = SendPendingData(GetFd());
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasPendingData()) {
|
||||
AddWatchers(WRITE_WATCHER, false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionOrientedSocketIO::OnConnected()
|
||||
{
|
||||
MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
|
||||
MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED);
|
||||
|
||||
GetConsumerThread()->PostTask(
|
||||
MakeAndAddRef<SocketEventTask>(this, SocketEventTask::CONNECT_SUCCESS));
|
||||
|
||||
AddWatchers(READ_WATCHER, true);
|
||||
if (HasPendingData()) {
|
||||
AddWatchers(WRITE_WATCHER, false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionOrientedSocketIO::OnListening()
|
||||
{
|
||||
MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
|
||||
|
||||
NS_NOTREACHED("Invalid call to |ConnectionOrientedSocketIO::OnListening|");
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionOrientedSocketIO::OnError(const char* aFunction, int aErrno)
|
||||
{
|
||||
MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
|
||||
|
||||
UnixFdWatcher::OnError(aFunction, aErrno);
|
||||
|
||||
// Clean up watchers, status, fd
|
||||
Close();
|
||||
|
||||
// Tell the consumer thread we've errored
|
||||
GetConsumerThread()->PostTask(
|
||||
MakeAndAddRef<SocketEventTask>(this, SocketEventTask::CONNECT_ERROR));
|
||||
}
|
||||
|
||||
//
|
||||
// ConnectionOrientedSocket
|
||||
//
|
||||
|
||||
ConnectionOrientedSocket::ConnectionOrientedSocket()
|
||||
{
|
||||
MOZ_COUNT_CTOR_INHERITED(ConnectionOrientedSocket, DataSocket);
|
||||
}
|
||||
|
||||
ConnectionOrientedSocket::~ConnectionOrientedSocket()
|
||||
{
|
||||
MOZ_COUNT_DTOR_INHERITED(ConnectionOrientedSocket, DataSocket);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
122
ipc/unixsocket/ConnectionOrientedSocket.h
Normal file
122
ipc/unixsocket/ConnectionOrientedSocket.h
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/* -*- 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 "DataSocket.h"
|
||||
#include "mozilla/ipc/UnixSocketWatcher.h"
|
||||
|
||||
class MessageLoop;
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
class UnixSocketConnector;
|
||||
|
||||
/*
|
||||
* |ConnectionOrientedSocketIO| and |ConnectionOrientedSocket| define
|
||||
* interfaces for implementing stream sockets on I/O and consumer thread.
|
||||
* |ListenSocket| uses these classes to handle accepted sockets.
|
||||
*/
|
||||
|
||||
class ConnectionOrientedSocketIO
|
||||
: public DataSocketIO
|
||||
, public UnixSocketWatcher
|
||||
{
|
||||
public:
|
||||
virtual ~ConnectionOrientedSocketIO();
|
||||
|
||||
nsresult Accept(int aFd,
|
||||
const struct sockaddr* aAddress,
|
||||
socklen_t aAddressLength);
|
||||
|
||||
nsresult Connect();
|
||||
|
||||
void Send(UnixSocketIOBuffer* aBuffer);
|
||||
|
||||
// Methods for |UnixSocketWatcher|
|
||||
//
|
||||
|
||||
void OnSocketCanReceiveWithoutBlocking() final;
|
||||
void OnSocketCanSendWithoutBlocking() final;
|
||||
|
||||
void OnListening() final;
|
||||
void OnConnected() final;
|
||||
void OnError(const char* aFunction, int aErrno) final;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructs an instance of |ConnectionOrientedSocketIO|
|
||||
*
|
||||
* @param aConsumerLoop The socket's consumer thread.
|
||||
* @param aIOLoop The socket's I/O loop.
|
||||
* @param aFd The socket file descriptor.
|
||||
* @param aConnectionStatus The connection status for |aFd|.
|
||||
* @param aConnector Connector object for socket-type-specific methods.
|
||||
*/
|
||||
ConnectionOrientedSocketIO(MessageLoop* aConsumerLoop,
|
||||
MessageLoop* aIOLoop,
|
||||
int aFd, ConnectionStatus aConnectionStatus,
|
||||
UnixSocketConnector* aConnector);
|
||||
|
||||
/**
|
||||
* Constructs an instance of |ConnectionOrientedSocketIO|
|
||||
*
|
||||
* @param aConsumerLoop The socket's consumer thread.
|
||||
* @param aIOLoop The socket's I/O loop.
|
||||
* @param aConnector Connector object for socket-type-specific methods.
|
||||
*/
|
||||
ConnectionOrientedSocketIO(MessageLoop* aConsumerLoop,
|
||||
MessageLoop* aIOLoop,
|
||||
UnixSocketConnector* aConnector);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Connector object used to create the connection we are currently using.
|
||||
*/
|
||||
UniquePtr<UnixSocketConnector> mConnector;
|
||||
|
||||
/**
|
||||
* Number of valid bytes in |mPeerAddress|.
|
||||
*/
|
||||
socklen_t mPeerAddressLength;
|
||||
|
||||
/**
|
||||
* Address of the socket's current peer.
|
||||
*/
|
||||
struct sockaddr_storage mPeerAddress;
|
||||
};
|
||||
|
||||
class ConnectionOrientedSocket : public DataSocket
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Prepares an instance of |ConnectionOrientedSocket| in DISCONNECTED
|
||||
* state for accepting a connection. Consumer-thread only.
|
||||
*
|
||||
* @param aConnector The new connector object, owned by the
|
||||
* connection-oriented socket.
|
||||
* @param aConsumerLoop The socket's consumer thread.
|
||||
* @param aIOLoop The socket's I/O thread.
|
||||
* @param[out] aIO, Returns an instance of |ConnectionOrientedSocketIO|.
|
||||
* @return NS_OK on success, or an XPCOM error code otherwise.
|
||||
*/
|
||||
virtual nsresult PrepareAccept(UnixSocketConnector* aConnector,
|
||||
MessageLoop* aConsumerLoop,
|
||||
MessageLoop* aIOLoop,
|
||||
ConnectionOrientedSocketIO*& aIO) = 0;
|
||||
|
||||
protected:
|
||||
ConnectionOrientedSocket();
|
||||
virtual ~ConnectionOrientedSocket();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // mozilla_ipc_ConnectionOrientedSocket
|
||||
136
ipc/unixsocket/DataSocket.cpp
Normal file
136
ipc/unixsocket/DataSocket.cpp
Normal file
|
|
@ -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 "DataSocket.h"
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
#include "GeckoTaskTracer.h"
|
||||
#endif
|
||||
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
using namespace mozilla::tasktracer;
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
//
|
||||
// DataSocketIO
|
||||
//
|
||||
|
||||
DataSocketIO::~DataSocketIO()
|
||||
{
|
||||
MOZ_COUNT_DTOR_INHERITED(DataSocketIO, SocketIOBase);
|
||||
}
|
||||
|
||||
void
|
||||
DataSocketIO::EnqueueData(UnixSocketIOBuffer* aBuffer)
|
||||
{
|
||||
if (!aBuffer->GetSize()) {
|
||||
delete aBuffer; // delete empty data immediately
|
||||
return;
|
||||
}
|
||||
mOutgoingQ.AppendElement(aBuffer);
|
||||
}
|
||||
|
||||
bool
|
||||
DataSocketIO::HasPendingData() const
|
||||
{
|
||||
return !mOutgoingQ.IsEmpty();
|
||||
}
|
||||
|
||||
ssize_t
|
||||
DataSocketIO::ReceiveData(int aFd)
|
||||
{
|
||||
MOZ_ASSERT(aFd >= 0);
|
||||
|
||||
UnixSocketIOBuffer* incoming;
|
||||
nsresult rv = QueryReceiveBuffer(&incoming);
|
||||
if (NS_FAILED(rv)) {
|
||||
/* an error occured */
|
||||
GetConsumerThread()->PostTask(
|
||||
MakeAndAddRef<SocketRequestClosingTask>(this));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t res = incoming->Receive(aFd);
|
||||
if (res < 0) {
|
||||
/* an I/O error occured */
|
||||
DiscardBuffer();
|
||||
GetConsumerThread()->PostTask(
|
||||
MakeAndAddRef<SocketRequestClosingTask>(this));
|
||||
return -1;
|
||||
} else if (!res) {
|
||||
/* EOF or peer shut down sending */
|
||||
DiscardBuffer();
|
||||
GetConsumerThread()->PostTask(
|
||||
MakeAndAddRef<SocketRequestClosingTask>(this));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
/* Make unix socket creation events to be the source events of TaskTracer,
|
||||
* and originate the rest correlation tasks from here.
|
||||
*/
|
||||
AutoSourceEvent taskTracerEvent(SourceEventType::Unixsocket);
|
||||
#endif
|
||||
|
||||
ConsumeBuffer();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DataSocketIO::SendPendingData(int aFd)
|
||||
{
|
||||
MOZ_ASSERT(aFd >= 0);
|
||||
|
||||
while (HasPendingData()) {
|
||||
UnixSocketIOBuffer* outgoing = mOutgoingQ.ElementAt(0);
|
||||
|
||||
ssize_t res = outgoing->Send(aFd);
|
||||
if (res < 0) {
|
||||
/* an I/O error occured */
|
||||
GetConsumerThread()->PostTask(
|
||||
MakeAndAddRef<SocketRequestClosingTask>(this));
|
||||
return NS_ERROR_FAILURE;
|
||||
} else if (!res && outgoing->GetSize()) {
|
||||
/* I/O is currently blocked; try again later */
|
||||
return NS_OK;
|
||||
}
|
||||
if (!outgoing->GetSize()) {
|
||||
mOutgoingQ.RemoveElementAt(0);
|
||||
delete outgoing;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DataSocketIO::DataSocketIO(MessageLoop* aConsumerLoop)
|
||||
: SocketIOBase(aConsumerLoop)
|
||||
{
|
||||
MOZ_COUNT_CTOR_INHERITED(DataSocketIO, SocketIOBase);
|
||||
}
|
||||
|
||||
//
|
||||
// DataSocket
|
||||
//
|
||||
|
||||
DataSocket::DataSocket()
|
||||
{
|
||||
MOZ_COUNT_CTOR_INHERITED(DataSocket, SocketBase);
|
||||
}
|
||||
|
||||
DataSocket::~DataSocket()
|
||||
{
|
||||
MOZ_COUNT_DTOR_INHERITED(DataSocket, SocketBase);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
145
ipc/unixsocket/DataSocket.h
Normal file
145
ipc/unixsocket/DataSocket.h
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/* -*- 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_datasocket_h
|
||||
#define mozilla_ipc_datasocket_h
|
||||
|
||||
#include "mozilla/ipc/SocketBase.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
//
|
||||
// DataSocket
|
||||
//
|
||||
|
||||
/**
|
||||
* |DataSocket| represents a socket that can send or receive data. This
|
||||
* can be a stream-based socket, a datagram-based socket, or any other
|
||||
* socket that transfers data.
|
||||
*/
|
||||
class DataSocket : public SocketBase
|
||||
{
|
||||
public:
|
||||
virtual ~DataSocket();
|
||||
|
||||
/**
|
||||
* Queue data to be sent to the socket on the IO thread. Can only be called on
|
||||
* originating thread.
|
||||
*
|
||||
* @param aBuffer Data to be sent to socket
|
||||
*/
|
||||
virtual void SendSocketData(UnixSocketIOBuffer* aBuffer) = 0;
|
||||
|
||||
protected:
|
||||
DataSocket();
|
||||
};
|
||||
|
||||
//
|
||||
// DataSocketIO
|
||||
//
|
||||
|
||||
/**
|
||||
* |DataSocketIO| is a base class for Socket I/O classes that
|
||||
* transfer data on the I/O thread. It provides methods for the
|
||||
* most common read and write scenarios.
|
||||
*/
|
||||
class DataSocketIO : public SocketIOBase
|
||||
{
|
||||
public:
|
||||
virtual ~DataSocketIO();
|
||||
|
||||
/**
|
||||
* Allocates a buffer for receiving data from the socket. The method
|
||||
* shall return the buffer in the arguments. The buffer is owned by the
|
||||
* I/O class. |DataSocketIO| will never ask for more than one buffer
|
||||
* at a time, so I/O classes can handout the same buffer on each invokation
|
||||
* of this method. I/O-thread only.
|
||||
*
|
||||
* @param[out] aBuffer returns a pointer to the I/O buffer
|
||||
* @return NS_OK on success, or an error code otherwise
|
||||
*/
|
||||
virtual nsresult QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer) = 0;
|
||||
|
||||
/**
|
||||
* Marks the current socket buffer to by consumed by the I/O class. The
|
||||
* class is resonsible for releasing the buffer afterwards. I/O-thread
|
||||
* only.
|
||||
*
|
||||
* @param aIndex the socket's index
|
||||
* @param[out] aBuffer the receive buffer
|
||||
* @param[out] aSize the receive buffer's size
|
||||
*/
|
||||
virtual void ConsumeBuffer() = 0;
|
||||
|
||||
/**
|
||||
* Marks the current socket buffer to be discarded. The I/O class is
|
||||
* resonsible for releasing the buffer's memory. I/O-thread only.
|
||||
*
|
||||
* @param aIndex the socket's index
|
||||
*/
|
||||
virtual void DiscardBuffer() = 0;
|
||||
|
||||
void EnqueueData(UnixSocketIOBuffer* aBuffer);
|
||||
bool HasPendingData() const;
|
||||
|
||||
ssize_t ReceiveData(int aFd);
|
||||
|
||||
nsresult SendPendingData(int aFd);
|
||||
|
||||
protected:
|
||||
DataSocketIO(MessageLoop* aConsumerLoop);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Raw data queue. Must be pushed/popped from I/O thread only.
|
||||
*/
|
||||
nsTArray<UnixSocketIOBuffer*> mOutgoingQ;
|
||||
};
|
||||
|
||||
//
|
||||
// Tasks
|
||||
//
|
||||
|
||||
/* |SocketIOSendTask| transfers an instance of |Tdata|, such as
|
||||
* |UnixSocketRawData|, to the I/O thread and queues it up for
|
||||
* sending the contained data.
|
||||
*/
|
||||
template<typename Tio, typename Tdata>
|
||||
class SocketIOSendTask final : public SocketIOTask<Tio>
|
||||
{
|
||||
public:
|
||||
SocketIOSendTask(Tio* aIO, Tdata* aData)
|
||||
: SocketIOTask<Tio>(aIO)
|
||||
, mData(aData)
|
||||
{
|
||||
MOZ_ASSERT(aData);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(!SocketIOTask<Tio>::IsCanceled());
|
||||
|
||||
Tio* io = SocketIOTask<Tio>::GetIO();
|
||||
MOZ_ASSERT(!io->IsConsumerThread());
|
||||
MOZ_ASSERT(!io->IsShutdownOnIOThread());
|
||||
|
||||
io->Send(mData);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
Tdata* mData;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
432
ipc/unixsocket/ListenSocket.cpp
Normal file
432
ipc/unixsocket/ListenSocket.cpp
Normal file
|
|
@ -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
|
||||
93
ipc/unixsocket/ListenSocket.h
Normal file
93
ipc/unixsocket/ListenSocket.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/* -*- 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 "mozilla/ipc/SocketBase.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class MessageLoop;
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
class ConnectionOrientedSocket;
|
||||
class ListenSocketConsumer;
|
||||
class ListenSocketIO;
|
||||
class UnixSocketConnector;
|
||||
|
||||
class ListenSocket final : public SocketBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs an instance of |ListenSocket|.
|
||||
*
|
||||
* @param aConsumer The consumer for the socket.
|
||||
* @param aIndex An arbitrary index.
|
||||
*/
|
||||
ListenSocket(ListenSocketConsumer* aConsumer, int aIndex);
|
||||
|
||||
/**
|
||||
* 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 aConsumerLoop The socket's consumer thread.
|
||||
* @param aIOLoop The socket's I/O thread.
|
||||
* @param aCOSocket The connection-oriented socket for handling the
|
||||
* accepted connection.
|
||||
* @return NS_OK on success, or an XPCOM error code otherwise.
|
||||
*/
|
||||
nsresult Listen(UnixSocketConnector* aConnector,
|
||||
MessageLoop* aConsumerLoop,
|
||||
MessageLoop* aIOLoop,
|
||||
ConnectionOrientedSocket* aCOSocket);
|
||||
|
||||
/**
|
||||
* 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 NS_OK on success, or an XPCOM error code otherwise.
|
||||
*/
|
||||
nsresult 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 NS_OK on success, or an XPCOM error code otherwise.
|
||||
*/
|
||||
nsresult Listen(ConnectionOrientedSocket* aCOSocket);
|
||||
|
||||
// Methods for |SocketBase|
|
||||
//
|
||||
|
||||
void Close() override;
|
||||
void OnConnectSuccess() override;
|
||||
void OnConnectError() override;
|
||||
void OnDisconnect() override;
|
||||
|
||||
protected:
|
||||
virtual ~ListenSocket();
|
||||
|
||||
private:
|
||||
ListenSocketIO* mIO;
|
||||
ListenSocketConsumer* mConsumer;
|
||||
int mIndex;
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
} // namepsace mozilla
|
||||
|
||||
#endif // mozilla_ipc_ListenSocket_h
|
||||
20
ipc/unixsocket/ListenSocketConsumer.cpp
Normal file
20
ipc/unixsocket/ListenSocketConsumer.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* -*- 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 "ListenSocketConsumer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
//
|
||||
// ListenSocketConsumer
|
||||
//
|
||||
|
||||
ListenSocketConsumer::~ListenSocketConsumer()
|
||||
{ }
|
||||
|
||||
}
|
||||
}
|
||||
46
ipc/unixsocket/ListenSocketConsumer.h
Normal file
46
ipc/unixsocket/ListenSocketConsumer.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- 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_listensocketconsumer_h
|
||||
#define mozilla_ipc_listensocketconsumer_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
/**
|
||||
* |ListenSocketConsumer| handles socket events.
|
||||
*/
|
||||
class ListenSocketConsumer
|
||||
{
|
||||
public:
|
||||
virtual ~ListenSocketConsumer();
|
||||
|
||||
/**
|
||||
* Callback for socket success. Consumer-thread only.
|
||||
*
|
||||
* @param aIndex The index that has been given to the listening socket.
|
||||
*/
|
||||
virtual void OnConnectSuccess(int aIndex) = 0;
|
||||
|
||||
/**
|
||||
* Callback for socket errors. Consumer-thread only.
|
||||
*
|
||||
* @param aIndex The index that has been given to the listening socket.
|
||||
*/
|
||||
virtual void OnConnectError(int aIndex) = 0;
|
||||
|
||||
/**
|
||||
* Callback for socket disconnect. Consumer-thread only.
|
||||
*
|
||||
* @param aIndex The index that has been given to the listeing socket.
|
||||
*/
|
||||
virtual void OnDisconnect(int aIndex) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
449
ipc/unixsocket/SocketBase.cpp
Normal file
449
ipc/unixsocket/SocketBase.cpp
Normal file
|
|
@ -0,0 +1,449 @@
|
|||
/* -*- 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 "SocketBase.h"
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
//
|
||||
// UnixSocketIOBuffer
|
||||
//
|
||||
|
||||
UnixSocketBuffer::UnixSocketBuffer()
|
||||
: mSize(0)
|
||||
, mOffset(0)
|
||||
, mAvailableSpace(0)
|
||||
, mData(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(UnixSocketBuffer);
|
||||
}
|
||||
|
||||
UnixSocketBuffer::~UnixSocketBuffer()
|
||||
{
|
||||
MOZ_COUNT_DTOR(UnixSocketBuffer);
|
||||
|
||||
// Make sure that the caller released the buffer's memory.
|
||||
MOZ_ASSERT(!GetBuffer());
|
||||
}
|
||||
|
||||
const uint8_t*
|
||||
UnixSocketBuffer::Consume(size_t aLen)
|
||||
{
|
||||
if (NS_WARN_IF(GetSize() < aLen)) {
|
||||
return nullptr;
|
||||
}
|
||||
uint8_t* data = mData + mOffset;
|
||||
mOffset += aLen;
|
||||
return data;
|
||||
}
|
||||
|
||||
nsresult
|
||||
UnixSocketBuffer::Read(void* aValue, size_t aLen)
|
||||
{
|
||||
const uint8_t* data = Consume(aLen);
|
||||
if (!data) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
memcpy(aValue, data, aLen);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
UnixSocketBuffer::Append(size_t aLen)
|
||||
{
|
||||
if (((mAvailableSpace - mSize) < aLen)) {
|
||||
size_t availableSpace = mAvailableSpace + std::max(mAvailableSpace, aLen);
|
||||
uint8_t* data = new uint8_t[availableSpace];
|
||||
memcpy(data, mData, mSize);
|
||||
mData = data;
|
||||
mAvailableSpace = availableSpace;
|
||||
}
|
||||
uint8_t* data = mData + mSize;
|
||||
mSize += aLen;
|
||||
return data;
|
||||
}
|
||||
|
||||
nsresult
|
||||
UnixSocketBuffer::Write(const void* aValue, size_t aLen)
|
||||
{
|
||||
uint8_t* data = Append(aLen);
|
||||
if (!data) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
memcpy(data, aValue, aLen);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
UnixSocketBuffer::CleanupLeadingSpace()
|
||||
{
|
||||
if (GetLeadingSpace()) {
|
||||
if (GetSize() <= GetLeadingSpace()) {
|
||||
memcpy(mData, GetData(), GetSize());
|
||||
} else {
|
||||
memmove(mData, GetData(), GetSize());
|
||||
}
|
||||
mOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// UnixSocketIOBuffer
|
||||
//
|
||||
|
||||
UnixSocketIOBuffer::UnixSocketIOBuffer()
|
||||
{
|
||||
MOZ_COUNT_CTOR_INHERITED(UnixSocketIOBuffer, UnixSocketBuffer);
|
||||
}
|
||||
|
||||
UnixSocketIOBuffer::~UnixSocketIOBuffer()
|
||||
{
|
||||
MOZ_COUNT_DTOR_INHERITED(UnixSocketIOBuffer, UnixSocketBuffer);
|
||||
}
|
||||
|
||||
//
|
||||
// UnixSocketRawData
|
||||
//
|
||||
|
||||
UnixSocketRawData::UnixSocketRawData(const void* aData, size_t aSize)
|
||||
{
|
||||
MOZ_ASSERT(aData || !aSize);
|
||||
|
||||
MOZ_COUNT_CTOR_INHERITED(UnixSocketRawData, UnixSocketIOBuffer);
|
||||
|
||||
ResetBuffer(static_cast<uint8_t*>(memcpy(new uint8_t[aSize], aData, aSize)),
|
||||
0, aSize, aSize);
|
||||
}
|
||||
|
||||
UnixSocketRawData::UnixSocketRawData(UniquePtr<uint8_t[]> aData, size_t aSize)
|
||||
{
|
||||
MOZ_ASSERT(aData || !aSize);
|
||||
|
||||
MOZ_COUNT_CTOR_INHERITED(UnixSocketRawData, UnixSocketIOBuffer);
|
||||
|
||||
ResetBuffer(aData.release(), 0, aSize, aSize);
|
||||
}
|
||||
|
||||
UnixSocketRawData::UnixSocketRawData(size_t aSize)
|
||||
{
|
||||
MOZ_COUNT_CTOR_INHERITED(UnixSocketRawData, UnixSocketIOBuffer);
|
||||
|
||||
ResetBuffer(new uint8_t[aSize], 0, 0, aSize);
|
||||
}
|
||||
|
||||
UnixSocketRawData::~UnixSocketRawData()
|
||||
{
|
||||
MOZ_COUNT_DTOR_INHERITED(UnixSocketRawData, UnixSocketIOBuffer);
|
||||
|
||||
UniquePtr<uint8_t[]> data(GetBuffer());
|
||||
ResetBuffer(nullptr, 0, 0, 0);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
UnixSocketRawData::Receive(int aFd)
|
||||
{
|
||||
if (!GetTrailingSpace()) {
|
||||
if (!GetLeadingSpace()) {
|
||||
return -1; /* buffer is full */
|
||||
}
|
||||
/* free up space at the end of data buffer */
|
||||
CleanupLeadingSpace();
|
||||
}
|
||||
|
||||
ssize_t res =
|
||||
TEMP_FAILURE_RETRY(read(aFd, GetTrailingBytes(), GetTrailingSpace()));
|
||||
|
||||
if (res < 0) {
|
||||
/* I/O error */
|
||||
return -1;
|
||||
} else if (!res) {
|
||||
/* EOF or peer shutdown sending */
|
||||
return 0;
|
||||
}
|
||||
|
||||
Append(res); /* mark read data as 'valid' */
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
UnixSocketRawData::Send(int aFd)
|
||||
{
|
||||
if (!GetSize()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t res = TEMP_FAILURE_RETRY(write(aFd, GetData(), GetSize()));
|
||||
|
||||
if (res < 0) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
return 0; /* socket is blocked; try again later */
|
||||
}
|
||||
return -1;
|
||||
} else if (!res) {
|
||||
/* nothing written */
|
||||
return 0;
|
||||
}
|
||||
|
||||
Consume(res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//
|
||||
// SocketBase
|
||||
//
|
||||
|
||||
SocketConnectionStatus
|
||||
SocketBase::GetConnectionStatus() const
|
||||
{
|
||||
return mConnectionStatus;
|
||||
}
|
||||
|
||||
int
|
||||
SocketBase::GetSuggestedConnectDelayMs() const
|
||||
{
|
||||
return mConnectDelayMs;
|
||||
}
|
||||
|
||||
void
|
||||
SocketBase::NotifySuccess()
|
||||
{
|
||||
mConnectionStatus = SOCKET_CONNECTED;
|
||||
mConnectTimestamp = PR_IntervalNow();
|
||||
OnConnectSuccess();
|
||||
}
|
||||
|
||||
void
|
||||
SocketBase::NotifyError()
|
||||
{
|
||||
mConnectionStatus = SOCKET_DISCONNECTED;
|
||||
mConnectDelayMs = CalculateConnectDelayMs();
|
||||
mConnectTimestamp = 0;
|
||||
OnConnectError();
|
||||
}
|
||||
|
||||
void
|
||||
SocketBase::NotifyDisconnect()
|
||||
{
|
||||
mConnectionStatus = SOCKET_DISCONNECTED;
|
||||
mConnectDelayMs = CalculateConnectDelayMs();
|
||||
mConnectTimestamp = 0;
|
||||
OnDisconnect();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SocketBase::CalculateConnectDelayMs() const
|
||||
{
|
||||
uint32_t connectDelayMs = mConnectDelayMs;
|
||||
|
||||
if (mConnectTimestamp && (PR_IntervalNow()-mConnectTimestamp) > connectDelayMs) {
|
||||
// reset delay if connection has been opened for a while, or...
|
||||
connectDelayMs = 0;
|
||||
} else if (!connectDelayMs) {
|
||||
// ...start with a delay of ~1 sec, or...
|
||||
connectDelayMs = 1<<10;
|
||||
} else if (connectDelayMs < (1<<16)) {
|
||||
// ...otherwise increase delay by a factor of 2
|
||||
connectDelayMs <<= 1;
|
||||
}
|
||||
return connectDelayMs;
|
||||
}
|
||||
|
||||
SocketBase::SocketBase()
|
||||
: mConnectionStatus(SOCKET_DISCONNECTED)
|
||||
, mConnectTimestamp(0)
|
||||
, mConnectDelayMs(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(SocketBase);
|
||||
}
|
||||
|
||||
SocketBase::~SocketBase()
|
||||
{
|
||||
MOZ_ASSERT(mConnectionStatus == SOCKET_DISCONNECTED);
|
||||
|
||||
MOZ_COUNT_DTOR(SocketBase);
|
||||
}
|
||||
|
||||
void
|
||||
SocketBase::SetConnectionStatus(SocketConnectionStatus aConnectionStatus)
|
||||
{
|
||||
mConnectionStatus = aConnectionStatus;
|
||||
}
|
||||
|
||||
//
|
||||
// SocketIOBase
|
||||
//
|
||||
|
||||
SocketIOBase::SocketIOBase(MessageLoop* aConsumerLoop)
|
||||
: mConsumerLoop(aConsumerLoop)
|
||||
{
|
||||
MOZ_ASSERT(mConsumerLoop);
|
||||
|
||||
MOZ_COUNT_CTOR(SocketIOBase);
|
||||
}
|
||||
|
||||
SocketIOBase::~SocketIOBase()
|
||||
{
|
||||
MOZ_COUNT_DTOR(SocketIOBase);
|
||||
}
|
||||
|
||||
MessageLoop*
|
||||
SocketIOBase::GetConsumerThread() const
|
||||
{
|
||||
return mConsumerLoop;
|
||||
}
|
||||
|
||||
bool
|
||||
SocketIOBase::IsConsumerThread() const
|
||||
{
|
||||
return GetConsumerThread() == MessageLoop::current();
|
||||
}
|
||||
|
||||
//
|
||||
// SocketEventTask
|
||||
//
|
||||
|
||||
SocketEventTask::SocketEventTask(SocketIOBase* aIO, SocketEvent aEvent)
|
||||
: SocketTask<SocketIOBase>(aIO)
|
||||
, mEvent(aEvent)
|
||||
{
|
||||
MOZ_COUNT_CTOR(SocketEventTask);
|
||||
}
|
||||
|
||||
SocketEventTask::~SocketEventTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(SocketEventTask);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SocketEventTask::Run()
|
||||
{
|
||||
SocketIOBase* io = SocketTask<SocketIOBase>::GetIO();
|
||||
|
||||
MOZ_ASSERT(io->IsConsumerThread());
|
||||
|
||||
if (NS_WARN_IF(io->IsShutdownOnConsumerThread())) {
|
||||
// Since we've already explicitly closed and the close
|
||||
// happened before this, this isn't really an error.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SocketBase* socketBase = io->GetSocketBase();
|
||||
MOZ_ASSERT(socketBase);
|
||||
|
||||
if (mEvent == CONNECT_SUCCESS) {
|
||||
socketBase->NotifySuccess();
|
||||
} else if (mEvent == CONNECT_ERROR) {
|
||||
socketBase->NotifyError();
|
||||
} else if (mEvent == DISCONNECT) {
|
||||
socketBase->NotifyDisconnect();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// SocketRequestClosingTask
|
||||
//
|
||||
|
||||
SocketRequestClosingTask::SocketRequestClosingTask(SocketIOBase* aIO)
|
||||
: SocketTask<SocketIOBase>(aIO)
|
||||
{
|
||||
MOZ_COUNT_CTOR(SocketRequestClosingTask);
|
||||
}
|
||||
|
||||
SocketRequestClosingTask::~SocketRequestClosingTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(SocketRequestClosingTask);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SocketRequestClosingTask::Run()
|
||||
{
|
||||
SocketIOBase* io = SocketTask<SocketIOBase>::GetIO();
|
||||
|
||||
MOZ_ASSERT(io->IsConsumerThread());
|
||||
|
||||
if (NS_WARN_IF(io->IsShutdownOnConsumerThread())) {
|
||||
// Since we've already explicitly closed and the close
|
||||
// happened before this, this isn't really an error.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SocketBase* socketBase = io->GetSocketBase();
|
||||
MOZ_ASSERT(socketBase);
|
||||
|
||||
socketBase->Close();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// SocketDeleteInstanceTask
|
||||
//
|
||||
|
||||
SocketDeleteInstanceTask::SocketDeleteInstanceTask(SocketIOBase* aIO)
|
||||
: mIO(aIO)
|
||||
{
|
||||
MOZ_COUNT_CTOR(SocketDeleteInstanceTask);
|
||||
}
|
||||
|
||||
SocketDeleteInstanceTask::~SocketDeleteInstanceTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(SocketDeleteInstanceTask);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SocketDeleteInstanceTask::Run()
|
||||
{
|
||||
mIO.reset(); // delete instance
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// SocketIOShutdownTask
|
||||
//
|
||||
|
||||
SocketIOShutdownTask::SocketIOShutdownTask(SocketIOBase* aIO)
|
||||
: SocketIOTask<SocketIOBase>(aIO)
|
||||
{
|
||||
MOZ_COUNT_CTOR(SocketIOShutdownTask);
|
||||
}
|
||||
|
||||
SocketIOShutdownTask::~SocketIOShutdownTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(SocketIOShutdownTask);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SocketIOShutdownTask::Run()
|
||||
{
|
||||
SocketIOBase* io = SocketIOTask<SocketIOBase>::GetIO();
|
||||
|
||||
MOZ_ASSERT(!io->IsConsumerThread());
|
||||
MOZ_ASSERT(!io->IsShutdownOnIOThread());
|
||||
|
||||
// At this point, there should be no new events on the I/O thread
|
||||
// after this one with the possible exception of an accept task,
|
||||
// which ShutdownOnIOThread will cancel for us. We are now fully
|
||||
// shut down, so we can send a message to the consumer thread to
|
||||
// delete |io| safely knowing that it's not reference any longer.
|
||||
io->ShutdownOnIOThread();
|
||||
io->GetConsumerThread()->PostTask(
|
||||
MakeAndAddRef<SocketDeleteInstanceTask>(io));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
585
ipc/unixsocket/SocketBase.h
Normal file
585
ipc/unixsocket/SocketBase.h
Normal file
|
|
@ -0,0 +1,585 @@
|
|||
/* -*- 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_SocketBase_h
|
||||
#define mozilla_ipc_SocketBase_h
|
||||
|
||||
#include "base/message_loop.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
//
|
||||
// UnixSocketBuffer
|
||||
//
|
||||
|
||||
/**
|
||||
* |UnixSocketBuffer| implements a FIFO buffer that stores raw socket
|
||||
* data, either for sending on a socket or received from a socket.
|
||||
*/
|
||||
class UnixSocketBuffer
|
||||
{
|
||||
public:
|
||||
virtual ~UnixSocketBuffer();
|
||||
|
||||
const uint8_t* GetData() const
|
||||
{
|
||||
return mData + mOffset;
|
||||
}
|
||||
|
||||
size_t GetSize() const
|
||||
{
|
||||
return mSize - mOffset;
|
||||
}
|
||||
|
||||
const uint8_t* Consume(size_t aLen);
|
||||
|
||||
nsresult Read(void* aValue, size_t aLen);
|
||||
|
||||
nsresult Read(int8_t& aValue)
|
||||
{
|
||||
return Read(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Read(uint8_t& aValue)
|
||||
{
|
||||
return Read(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Read(int16_t& aValue)
|
||||
{
|
||||
return Read(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Read(uint16_t& aValue)
|
||||
{
|
||||
return Read(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Read(int32_t& aValue)
|
||||
{
|
||||
return Read(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Read(uint32_t& aValue)
|
||||
{
|
||||
return Read(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Read(int64_t& aValue)
|
||||
{
|
||||
return Read(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Read(uint64_t& aValue)
|
||||
{
|
||||
return Read(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Read(float& aValue)
|
||||
{
|
||||
return Read(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Read(double& aValue)
|
||||
{
|
||||
return Read(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
uint8_t* Append(size_t aLen);
|
||||
|
||||
nsresult Write(const void* aValue, size_t aLen);
|
||||
|
||||
nsresult Write(int8_t aValue)
|
||||
{
|
||||
return Write(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Write(uint8_t aValue)
|
||||
{
|
||||
return Write(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Write(int16_t aValue)
|
||||
{
|
||||
return Write(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Write(uint16_t aValue)
|
||||
{
|
||||
return Write(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Write(int32_t aValue)
|
||||
{
|
||||
return Write(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Write(uint32_t aValue)
|
||||
{
|
||||
return Write(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Write(int64_t aValue)
|
||||
{
|
||||
return Write(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Write(uint64_t aValue)
|
||||
{
|
||||
return Write(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Write(float aValue)
|
||||
{
|
||||
return Write(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
nsresult Write(double aValue)
|
||||
{
|
||||
return Write(&aValue, sizeof(aValue));
|
||||
}
|
||||
|
||||
protected:
|
||||
UnixSocketBuffer();
|
||||
|
||||
/**
|
||||
* Sets the raw memory. The caller is responsible for freeing
|
||||
* this memory.
|
||||
*
|
||||
* @param aData A pointer to the buffer's raw memory.
|
||||
* @param aOffset The start of valid bytes in |aData|.
|
||||
* @param aSize The number of valid bytes in |aData|.
|
||||
* @param aAvailableSpace The number of bytes in |aData|.
|
||||
*/
|
||||
void ResetBuffer(uint8_t* aData,
|
||||
size_t aOffset, size_t aSize, size_t aAvailableSpace)
|
||||
{
|
||||
MOZ_ASSERT(aData || !aAvailableSpace);
|
||||
MOZ_ASSERT((aOffset + aSize) <= aAvailableSpace);
|
||||
|
||||
mOffset = aOffset;
|
||||
mSize = aSize;
|
||||
mAvailableSpace = aAvailableSpace;
|
||||
mData = aData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the memory buffer.
|
||||
*
|
||||
* @return A pointer to the buffer's raw memory.
|
||||
*/
|
||||
uint8_t* GetBuffer()
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
size_t GetLeadingSpace() const
|
||||
{
|
||||
return mOffset;
|
||||
}
|
||||
|
||||
size_t GetTrailingSpace() const
|
||||
{
|
||||
return mAvailableSpace - mSize;
|
||||
}
|
||||
|
||||
size_t GetAvailableSpace() const
|
||||
{
|
||||
return mAvailableSpace;
|
||||
}
|
||||
|
||||
void* GetTrailingBytes()
|
||||
{
|
||||
return mData + mSize;
|
||||
}
|
||||
|
||||
uint8_t* GetData(size_t aOffset)
|
||||
{
|
||||
MOZ_ASSERT(aOffset <= mSize);
|
||||
|
||||
return mData + aOffset;
|
||||
}
|
||||
|
||||
void SetRange(size_t aOffset, size_t aSize)
|
||||
{
|
||||
MOZ_ASSERT((aOffset + aSize) <= mAvailableSpace);
|
||||
|
||||
mOffset = aOffset;
|
||||
mSize = mOffset + aSize;
|
||||
}
|
||||
|
||||
void CleanupLeadingSpace();
|
||||
|
||||
private:
|
||||
size_t mSize;
|
||||
size_t mOffset;
|
||||
size_t mAvailableSpace;
|
||||
uint8_t* mData;
|
||||
};
|
||||
|
||||
//
|
||||
// UnixSocketIOBuffer
|
||||
//
|
||||
|
||||
/**
|
||||
* |UnixSocketIOBuffer| is a |UnixSocketBuffer| that supports being
|
||||
* received on a socket or being send on a socket. Network protocols
|
||||
* might differ in their exact usage of Unix socket functions and
|
||||
* |UnixSocketIOBuffer| provides a protocol-neutral interface.
|
||||
*/
|
||||
class UnixSocketIOBuffer : public UnixSocketBuffer
|
||||
{
|
||||
public:
|
||||
UnixSocketIOBuffer();
|
||||
virtual ~UnixSocketIOBuffer();
|
||||
|
||||
/**
|
||||
* Receives data from aFd at the end of the buffer. The returned value
|
||||
* is the number of newly received bytes, or 0 if the peer shut down
|
||||
* its connection, or a negative value on errors.
|
||||
*/
|
||||
virtual ssize_t Receive(int aFd) = 0;
|
||||
|
||||
/**
|
||||
* Sends data to aFd from the beginning of the buffer. The returned value
|
||||
* is the number of bytes written, or a negative value on error.
|
||||
*/
|
||||
virtual ssize_t Send(int aFd) = 0;
|
||||
};
|
||||
|
||||
//
|
||||
// UnixSocketRawData
|
||||
//
|
||||
|
||||
class UnixSocketRawData final : public UnixSocketIOBuffer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This constructor copies aData of aSize bytes length into the
|
||||
* new instance of |UnixSocketRawData|.
|
||||
*
|
||||
* @param aData The buffer to copy.
|
||||
* @param aSize The number of bytes in |aData|.
|
||||
*/
|
||||
UnixSocketRawData(const void* aData, size_t aSize);
|
||||
|
||||
/**
|
||||
* This constructor takes ownership of the data in aData. The
|
||||
* data is assumed to be aSize bytes in length.
|
||||
*
|
||||
* @param aData The buffer to take ownership of.
|
||||
* @param aSize The number of bytes in |aData|.
|
||||
*/
|
||||
UnixSocketRawData(UniquePtr<uint8_t[]> aData, size_t aSize);
|
||||
|
||||
/**
|
||||
* This constructor reserves aSize bytes of space. Currently
|
||||
* it's only possible to fill this buffer by calling |Receive|.
|
||||
*
|
||||
* @param aSize The number of bytes to allocate.
|
||||
*/
|
||||
UnixSocketRawData(size_t aSize);
|
||||
|
||||
/**
|
||||
* The destructor releases the buffer's raw memory.
|
||||
*/
|
||||
~UnixSocketRawData();
|
||||
|
||||
/**
|
||||
* Receives data from aFd at the end of the buffer. The returned value
|
||||
* is the number of newly received bytes, or 0 if the peer shut down
|
||||
* its connection, or a negative value on errors.
|
||||
*/
|
||||
ssize_t Receive(int aFd) override;
|
||||
|
||||
/**
|
||||
* Sends data to aFd from the beginning of the buffer. The returned value
|
||||
* is the number of bytes written, or a negative value on error.
|
||||
*/
|
||||
ssize_t Send(int aFd) override;
|
||||
};
|
||||
|
||||
enum SocketConnectionStatus {
|
||||
SOCKET_DISCONNECTED = 0,
|
||||
SOCKET_LISTENING = 1,
|
||||
SOCKET_CONNECTING = 2,
|
||||
SOCKET_CONNECTED = 3
|
||||
};
|
||||
|
||||
//
|
||||
// SocketBase
|
||||
//
|
||||
|
||||
class SocketBase
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SocketBase)
|
||||
|
||||
SocketConnectionStatus GetConnectionStatus() const;
|
||||
|
||||
int GetSuggestedConnectDelayMs() const;
|
||||
|
||||
/**
|
||||
* Queues the internal representation of socket for deletion. Can be called
|
||||
* from consumer thread.
|
||||
*/
|
||||
virtual void Close() = 0;
|
||||
|
||||
/**
|
||||
* Callback for socket connect/accept success. Called after connect/accept has
|
||||
* finished. Will be run on consumer thread before any reads take place.
|
||||
*/
|
||||
virtual void OnConnectSuccess() = 0;
|
||||
|
||||
/**
|
||||
* Callback for socket connect/accept error. Will be run on consumer thread.
|
||||
*/
|
||||
virtual void OnConnectError() = 0;
|
||||
|
||||
/**
|
||||
* Callback for socket disconnect. Will be run on consumer thread.
|
||||
*/
|
||||
virtual void OnDisconnect() = 0;
|
||||
|
||||
/**
|
||||
* Called by implementation to notify consumer of success.
|
||||
*/
|
||||
void NotifySuccess();
|
||||
|
||||
/**
|
||||
* Called by implementation to notify consumer of error.
|
||||
*/
|
||||
void NotifyError();
|
||||
|
||||
/**
|
||||
* Called by implementation to notify consumer of disconnect.
|
||||
*/
|
||||
void NotifyDisconnect();
|
||||
|
||||
protected:
|
||||
SocketBase();
|
||||
virtual ~SocketBase();
|
||||
|
||||
void SetConnectionStatus(SocketConnectionStatus aConnectionStatus);
|
||||
|
||||
private:
|
||||
uint32_t CalculateConnectDelayMs() const;
|
||||
|
||||
SocketConnectionStatus mConnectionStatus;
|
||||
PRIntervalTime mConnectTimestamp;
|
||||
uint32_t mConnectDelayMs;
|
||||
};
|
||||
|
||||
//
|
||||
// SocketIOBase
|
||||
//
|
||||
|
||||
/**
|
||||
* |SocketIOBase| is a base class for Socket I/O classes that
|
||||
* perform operations on the I/O thread.
|
||||
*/
|
||||
class SocketIOBase
|
||||
{
|
||||
public:
|
||||
virtual ~SocketIOBase();
|
||||
|
||||
/**
|
||||
* Implemented by socket I/O classes to return the current instance of
|
||||
* |SocketBase|.
|
||||
*
|
||||
* @return The current instance of |SocketBase|
|
||||
*/
|
||||
virtual SocketBase* GetSocketBase() = 0;
|
||||
|
||||
/**
|
||||
* Implemented by socket I/O classes to signal that the socket I/O class has
|
||||
* been shut down.
|
||||
*
|
||||
* @return True if the socket I/O class has been shut down, false otherwise.
|
||||
*/
|
||||
virtual bool IsShutdownOnIOThread() const = 0;
|
||||
|
||||
/**
|
||||
* Implemented by socket I/O classes to signal that socket class has
|
||||
* been shut down.
|
||||
*
|
||||
* @return True if the socket class has been shut down, false otherwise.
|
||||
*/
|
||||
virtual bool IsShutdownOnConsumerThread() const = 0;
|
||||
|
||||
/**
|
||||
* Signals to the socket I/O classes that it has been shut down.
|
||||
*/
|
||||
virtual void ShutdownOnIOThread() = 0;
|
||||
|
||||
/**
|
||||
* Signals to the socket I/O classes that the socket class has been
|
||||
* shut down.
|
||||
*/
|
||||
virtual void ShutdownOnConsumerThread() = 0;
|
||||
|
||||
/**
|
||||
* Returns the consumer thread.
|
||||
*
|
||||
* @return A pointer to the consumer thread.
|
||||
*/
|
||||
MessageLoop* GetConsumerThread() const;
|
||||
|
||||
/**
|
||||
* @return True if the current thread is the consumer thread, or false
|
||||
* otherwise.
|
||||
*/
|
||||
bool IsConsumerThread() const;
|
||||
|
||||
protected:
|
||||
SocketIOBase(MessageLoop* aConsumerLoop);
|
||||
|
||||
private:
|
||||
MessageLoop* mConsumerLoop;
|
||||
};
|
||||
|
||||
//
|
||||
// Socket tasks
|
||||
//
|
||||
|
||||
/* |SocketTask| is a task for sending a message from
|
||||
* the I/O thread to the consumer thread.
|
||||
*/
|
||||
template <typename T>
|
||||
class SocketTask : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
virtual ~SocketTask()
|
||||
{ }
|
||||
|
||||
T* GetIO() const
|
||||
{
|
||||
return mIO;
|
||||
}
|
||||
|
||||
protected:
|
||||
SocketTask(T* aIO)
|
||||
: mIO(aIO)
|
||||
{
|
||||
MOZ_ASSERT(aIO);
|
||||
}
|
||||
|
||||
private:
|
||||
T* mIO;
|
||||
};
|
||||
|
||||
/**
|
||||
* |SocketEventTask| reports the connection state on the
|
||||
* I/O thread back to the consumer thread.
|
||||
*/
|
||||
class SocketEventTask final : public SocketTask<SocketIOBase>
|
||||
{
|
||||
public:
|
||||
enum SocketEvent {
|
||||
CONNECT_SUCCESS,
|
||||
CONNECT_ERROR,
|
||||
DISCONNECT
|
||||
};
|
||||
|
||||
SocketEventTask(SocketIOBase* aIO, SocketEvent aEvent);
|
||||
~SocketEventTask();
|
||||
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
private:
|
||||
SocketEvent mEvent;
|
||||
};
|
||||
|
||||
/**
|
||||
* |SocketRequestClosingTask| closes an instance of |SocketBase|
|
||||
* on the consumer thread.
|
||||
*/
|
||||
class SocketRequestClosingTask final : public SocketTask<SocketIOBase>
|
||||
{
|
||||
public:
|
||||
SocketRequestClosingTask(SocketIOBase* aIO);
|
||||
~SocketRequestClosingTask();
|
||||
|
||||
NS_IMETHOD Run() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* |SocketDeleteInstanceTask| deletes an object on the consumer thread.
|
||||
*/
|
||||
class SocketDeleteInstanceTask final : public Runnable
|
||||
{
|
||||
public:
|
||||
SocketDeleteInstanceTask(SocketIOBase* aIO);
|
||||
~SocketDeleteInstanceTask();
|
||||
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
private:
|
||||
UniquePtr<SocketIOBase> mIO;
|
||||
};
|
||||
|
||||
//
|
||||
// Socket I/O tasks
|
||||
//
|
||||
|
||||
/* |SocketIOTask| holds a reference to a Socket I/O object. It's
|
||||
* supposed to run on the I/O thread.
|
||||
*/
|
||||
template<typename Tio>
|
||||
class SocketIOTask : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
virtual ~SocketIOTask()
|
||||
{ }
|
||||
|
||||
Tio* GetIO() const
|
||||
{
|
||||
return mIO;
|
||||
}
|
||||
|
||||
nsresult Cancel() override
|
||||
{
|
||||
mIO = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool IsCanceled() const
|
||||
{
|
||||
return !mIO;
|
||||
}
|
||||
|
||||
protected:
|
||||
SocketIOTask(Tio* aIO)
|
||||
: mIO(aIO)
|
||||
{
|
||||
MOZ_ASSERT(mIO);
|
||||
}
|
||||
|
||||
private:
|
||||
Tio* mIO;
|
||||
};
|
||||
|
||||
/**
|
||||
* |SocketIOShutdownTask| signals shutdown to the socket I/O class on
|
||||
* the I/O thread and sends it to the consumer thread for destruction.
|
||||
*/
|
||||
class SocketIOShutdownTask final : public SocketIOTask<SocketIOBase>
|
||||
{
|
||||
public:
|
||||
SocketIOShutdownTask(SocketIOBase* aIO);
|
||||
~SocketIOShutdownTask();
|
||||
|
||||
NS_IMETHOD Run() override;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
482
ipc/unixsocket/StreamSocket.cpp
Normal file
482
ipc/unixsocket/StreamSocket.cpp
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
/* -*- 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 "StreamSocket.h"
|
||||
#include <fcntl.h>
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "StreamSocketConsumer.h"
|
||||
#include "UnixSocketConnector.h"
|
||||
|
||||
static const size_t MAX_READ_SIZE = 1 << 16;
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
//
|
||||
// StreamSocketIO
|
||||
//
|
||||
|
||||
class StreamSocketIO final : public ConnectionOrientedSocketIO
|
||||
{
|
||||
public:
|
||||
class ConnectTask;
|
||||
class DelayedConnectTask;
|
||||
class ReceiveTask;
|
||||
|
||||
StreamSocketIO(MessageLoop* aConsumerLoop,
|
||||
MessageLoop* aIOLoop,
|
||||
StreamSocket* aStreamSocket,
|
||||
UnixSocketConnector* aConnector);
|
||||
StreamSocketIO(MessageLoop* aConsumerLoop,
|
||||
MessageLoop* aIOLoop,
|
||||
int aFd, ConnectionStatus aConnectionStatus,
|
||||
StreamSocket* aStreamSocket,
|
||||
UnixSocketConnector* aConnector);
|
||||
~StreamSocketIO();
|
||||
|
||||
StreamSocket* GetStreamSocket();
|
||||
DataSocket* GetDataSocket();
|
||||
|
||||
// Delayed-task handling
|
||||
//
|
||||
|
||||
void SetDelayedConnectTask(CancelableRunnable* aTask);
|
||||
void ClearDelayedConnectTask();
|
||||
void CancelDelayedConnectTask();
|
||||
|
||||
// Methods for |DataSocket|
|
||||
//
|
||||
|
||||
nsresult QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer) override;
|
||||
void ConsumeBuffer() override;
|
||||
void DiscardBuffer() override;
|
||||
|
||||
// Methods for |SocketIOBase|
|
||||
//
|
||||
|
||||
SocketBase* GetSocketBase() override;
|
||||
|
||||
bool IsShutdownOnConsumerThread() const override;
|
||||
bool IsShutdownOnIOThread() const override;
|
||||
|
||||
void ShutdownOnConsumerThread() override;
|
||||
void ShutdownOnIOThread() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
StreamSocket* mStreamSocket;
|
||||
|
||||
/**
|
||||
* If true, do not requeue whatever task we're running
|
||||
*/
|
||||
bool mShuttingDownOnIOThread;
|
||||
|
||||
/**
|
||||
* Task member for delayed connect task. Should only be access on consumer
|
||||
* thread.
|
||||
*/
|
||||
CancelableRunnable* mDelayedConnectTask;
|
||||
|
||||
/**
|
||||
* I/O buffer for received data
|
||||
*/
|
||||
UniquePtr<UnixSocketRawData> mBuffer;
|
||||
};
|
||||
|
||||
StreamSocketIO::StreamSocketIO(MessageLoop* aConsumerLoop,
|
||||
MessageLoop* aIOLoop,
|
||||
StreamSocket* aStreamSocket,
|
||||
UnixSocketConnector* aConnector)
|
||||
: ConnectionOrientedSocketIO(aConsumerLoop, aIOLoop, aConnector)
|
||||
, mStreamSocket(aStreamSocket)
|
||||
, mShuttingDownOnIOThread(false)
|
||||
, mDelayedConnectTask(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(mStreamSocket);
|
||||
|
||||
MOZ_COUNT_CTOR_INHERITED(StreamSocketIO, ConnectionOrientedSocketIO);
|
||||
}
|
||||
|
||||
StreamSocketIO::StreamSocketIO(MessageLoop* aConsumerLoop,
|
||||
MessageLoop* aIOLoop,
|
||||
int aFd, ConnectionStatus aConnectionStatus,
|
||||
StreamSocket* aStreamSocket,
|
||||
UnixSocketConnector* aConnector)
|
||||
: ConnectionOrientedSocketIO(aConsumerLoop,
|
||||
aIOLoop,
|
||||
aFd,
|
||||
aConnectionStatus,
|
||||
aConnector)
|
||||
, mStreamSocket(aStreamSocket)
|
||||
, mShuttingDownOnIOThread(false)
|
||||
, mDelayedConnectTask(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(mStreamSocket);
|
||||
|
||||
MOZ_COUNT_CTOR_INHERITED(StreamSocketIO, ConnectionOrientedSocketIO);
|
||||
}
|
||||
|
||||
StreamSocketIO::~StreamSocketIO()
|
||||
{
|
||||
MOZ_ASSERT(IsConsumerThread());
|
||||
MOZ_ASSERT(IsShutdownOnConsumerThread());
|
||||
|
||||
MOZ_COUNT_DTOR_INHERITED(StreamSocketIO, ConnectionOrientedSocketIO);
|
||||
}
|
||||
|
||||
StreamSocket*
|
||||
StreamSocketIO::GetStreamSocket()
|
||||
{
|
||||
return mStreamSocket;
|
||||
}
|
||||
|
||||
DataSocket*
|
||||
StreamSocketIO::GetDataSocket()
|
||||
{
|
||||
return GetStreamSocket();
|
||||
}
|
||||
|
||||
void
|
||||
StreamSocketIO::SetDelayedConnectTask(CancelableRunnable* aTask)
|
||||
{
|
||||
MOZ_ASSERT(IsConsumerThread());
|
||||
|
||||
mDelayedConnectTask = aTask;
|
||||
}
|
||||
|
||||
void
|
||||
StreamSocketIO::ClearDelayedConnectTask()
|
||||
{
|
||||
MOZ_ASSERT(IsConsumerThread());
|
||||
|
||||
mDelayedConnectTask = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
StreamSocketIO::CancelDelayedConnectTask()
|
||||
{
|
||||
MOZ_ASSERT(IsConsumerThread());
|
||||
|
||||
if (!mDelayedConnectTask) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDelayedConnectTask->Cancel();
|
||||
ClearDelayedConnectTask();
|
||||
}
|
||||
|
||||
// |DataSocketIO|
|
||||
|
||||
nsresult
|
||||
StreamSocketIO::QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer)
|
||||
{
|
||||
MOZ_ASSERT(aBuffer);
|
||||
|
||||
if (!mBuffer) {
|
||||
mBuffer = MakeUnique<UnixSocketRawData>(MAX_READ_SIZE);
|
||||
}
|
||||
*aBuffer = mBuffer.get();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* |ReceiveTask| transfers data received on the I/O thread
|
||||
* to an instance of |StreamSocket| on the consumer thread.
|
||||
*/
|
||||
class StreamSocketIO::ReceiveTask final : public SocketTask<StreamSocketIO>
|
||||
{
|
||||
public:
|
||||
ReceiveTask(StreamSocketIO* aIO, UnixSocketBuffer* aBuffer)
|
||||
: SocketTask<StreamSocketIO>(aIO)
|
||||
, mBuffer(aBuffer)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ReceiveTask);
|
||||
}
|
||||
|
||||
~ReceiveTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ReceiveTask);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
StreamSocketIO* io = SocketTask<StreamSocketIO>::GetIO();
|
||||
|
||||
MOZ_ASSERT(io->IsConsumerThread());
|
||||
|
||||
if (NS_WARN_IF(io->IsShutdownOnConsumerThread())) {
|
||||
// Since we've already explicitly closed and the close
|
||||
// happened before this, this isn't really an error.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
StreamSocket* streamSocket = io->GetStreamSocket();
|
||||
MOZ_ASSERT(streamSocket);
|
||||
|
||||
streamSocket->ReceiveSocketData(mBuffer);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
UniquePtr<UnixSocketBuffer> mBuffer;
|
||||
};
|
||||
|
||||
void
|
||||
StreamSocketIO::ConsumeBuffer()
|
||||
{
|
||||
GetConsumerThread()->PostTask(
|
||||
MakeAndAddRef<ReceiveTask>(this, mBuffer.release()));
|
||||
}
|
||||
|
||||
void
|
||||
StreamSocketIO::DiscardBuffer()
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
// |SocketIOBase|
|
||||
|
||||
SocketBase*
|
||||
StreamSocketIO::GetSocketBase()
|
||||
{
|
||||
return GetDataSocket();
|
||||
}
|
||||
|
||||
bool
|
||||
StreamSocketIO::IsShutdownOnConsumerThread() const
|
||||
{
|
||||
MOZ_ASSERT(IsConsumerThread());
|
||||
|
||||
return mStreamSocket == nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
StreamSocketIO::IsShutdownOnIOThread() const
|
||||
{
|
||||
return mShuttingDownOnIOThread;
|
||||
}
|
||||
|
||||
void
|
||||
StreamSocketIO::ShutdownOnConsumerThread()
|
||||
{
|
||||
MOZ_ASSERT(IsConsumerThread());
|
||||
MOZ_ASSERT(!IsShutdownOnConsumerThread());
|
||||
|
||||
mStreamSocket = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
StreamSocketIO::ShutdownOnIOThread()
|
||||
{
|
||||
MOZ_ASSERT(!IsConsumerThread());
|
||||
MOZ_ASSERT(!mShuttingDownOnIOThread);
|
||||
|
||||
Close(); // will also remove fd from I/O loop
|
||||
mShuttingDownOnIOThread = true;
|
||||
}
|
||||
|
||||
//
|
||||
// Socket tasks
|
||||
//
|
||||
|
||||
class StreamSocketIO::ConnectTask final : public SocketIOTask<StreamSocketIO>
|
||||
{
|
||||
public:
|
||||
ConnectTask(StreamSocketIO* aIO)
|
||||
: SocketIOTask<StreamSocketIO>(aIO)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ReceiveTask);
|
||||
}
|
||||
|
||||
~ConnectTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ReceiveTask);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(!GetIO()->IsConsumerThread());
|
||||
MOZ_ASSERT(!IsCanceled());
|
||||
|
||||
GetIO()->Connect();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class StreamSocketIO::DelayedConnectTask final
|
||||
: public SocketIOTask<StreamSocketIO>
|
||||
{
|
||||
public:
|
||||
DelayedConnectTask(StreamSocketIO* aIO)
|
||||
: SocketIOTask<StreamSocketIO>(aIO)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DelayedConnectTask);
|
||||
}
|
||||
|
||||
~DelayedConnectTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DelayedConnectTask);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(GetIO()->IsConsumerThread());
|
||||
|
||||
if (IsCanceled()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
StreamSocketIO* io = GetIO();
|
||||
if (io->IsShutdownOnConsumerThread()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
io->ClearDelayedConnectTask();
|
||||
io->GetIOLoop()->PostTask(MakeAndAddRef<ConnectTask>(io));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// StreamSocket
|
||||
//
|
||||
|
||||
StreamSocket::StreamSocket(StreamSocketConsumer* aConsumer, int aIndex)
|
||||
: mIO(nullptr)
|
||||
, mConsumer(aConsumer)
|
||||
, mIndex(aIndex)
|
||||
{
|
||||
MOZ_ASSERT(mConsumer);
|
||||
|
||||
MOZ_COUNT_CTOR_INHERITED(StreamSocket, ConnectionOrientedSocket);
|
||||
}
|
||||
|
||||
StreamSocket::~StreamSocket()
|
||||
{
|
||||
MOZ_ASSERT(!mIO);
|
||||
|
||||
MOZ_COUNT_DTOR_INHERITED(StreamSocket, ConnectionOrientedSocket);
|
||||
}
|
||||
|
||||
void
|
||||
StreamSocket::ReceiveSocketData(UniquePtr<UnixSocketBuffer>& aBuffer)
|
||||
{
|
||||
mConsumer->ReceiveSocketData(mIndex, aBuffer);
|
||||
}
|
||||
|
||||
nsresult
|
||||
StreamSocket::Connect(UnixSocketConnector* aConnector, int aDelayMs,
|
||||
MessageLoop* aConsumerLoop, MessageLoop* aIOLoop)
|
||||
{
|
||||
MOZ_ASSERT(!mIO);
|
||||
|
||||
mIO = new StreamSocketIO(aConsumerLoop, aIOLoop, this, aConnector);
|
||||
SetConnectionStatus(SOCKET_CONNECTING);
|
||||
|
||||
if (aDelayMs > 0) {
|
||||
RefPtr<StreamSocketIO::DelayedConnectTask> connectTask =
|
||||
MakeAndAddRef<StreamSocketIO::DelayedConnectTask>(mIO);
|
||||
mIO->SetDelayedConnectTask(connectTask);
|
||||
MessageLoop::current()->PostDelayedTask(connectTask.forget(), aDelayMs);
|
||||
} else {
|
||||
aIOLoop->PostTask(MakeAndAddRef<StreamSocketIO::ConnectTask>(mIO));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StreamSocket::Connect(UnixSocketConnector* aConnector, int aDelayMs)
|
||||
{
|
||||
return Connect(aConnector, aDelayMs,
|
||||
MessageLoop::current(), XRE_GetIOMessageLoop());
|
||||
}
|
||||
|
||||
// |ConnectionOrientedSocket|
|
||||
|
||||
nsresult
|
||||
StreamSocket::PrepareAccept(UnixSocketConnector* aConnector,
|
||||
MessageLoop* aConsumerLoop,
|
||||
MessageLoop* aIOLoop,
|
||||
ConnectionOrientedSocketIO*& aIO)
|
||||
{
|
||||
MOZ_ASSERT(!mIO);
|
||||
MOZ_ASSERT(aConnector);
|
||||
|
||||
SetConnectionStatus(SOCKET_CONNECTING);
|
||||
|
||||
mIO = new StreamSocketIO(aConsumerLoop, aIOLoop,
|
||||
-1, UnixSocketWatcher::SOCKET_IS_CONNECTING,
|
||||
this, aConnector);
|
||||
aIO = mIO;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// |DataSocket|
|
||||
|
||||
void
|
||||
StreamSocket::SendSocketData(UnixSocketIOBuffer* aBuffer)
|
||||
{
|
||||
MOZ_ASSERT(mIO);
|
||||
MOZ_ASSERT(mIO->IsConsumerThread());
|
||||
MOZ_ASSERT(!mIO->IsShutdownOnConsumerThread());
|
||||
|
||||
mIO->GetIOLoop()->PostTask(
|
||||
MakeAndAddRef<SocketIOSendTask<StreamSocketIO, UnixSocketIOBuffer>>(
|
||||
mIO, aBuffer));
|
||||
}
|
||||
|
||||
// |SocketBase|
|
||||
|
||||
void
|
||||
StreamSocket::Close()
|
||||
{
|
||||
MOZ_ASSERT(mIO);
|
||||
MOZ_ASSERT(mIO->IsConsumerThread());
|
||||
|
||||
mIO->CancelDelayedConnectTask();
|
||||
|
||||
// From this point on, we consider |mIO| as being deleted. We sever
|
||||
// the relationship here so any future calls to |Connect| will create
|
||||
// a new I/O object.
|
||||
mIO->ShutdownOnConsumerThread();
|
||||
mIO->GetIOLoop()->PostTask(MakeAndAddRef<SocketIOShutdownTask>(mIO));
|
||||
mIO = nullptr;
|
||||
|
||||
NotifyDisconnect();
|
||||
}
|
||||
|
||||
void
|
||||
StreamSocket::OnConnectSuccess()
|
||||
{
|
||||
mConsumer->OnConnectSuccess(mIndex);
|
||||
}
|
||||
|
||||
void
|
||||
StreamSocket::OnConnectError()
|
||||
{
|
||||
mConsumer->OnConnectError(mIndex);
|
||||
}
|
||||
|
||||
void
|
||||
StreamSocket::OnDisconnect()
|
||||
{
|
||||
mConsumer->OnDisconnect(mIndex);
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
95
ipc/unixsocket/StreamSocket.h
Normal file
95
ipc/unixsocket/StreamSocket.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/* -*- 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_streamsocket_h
|
||||
#define mozilla_ipc_streamsocket_h
|
||||
|
||||
#include "ConnectionOrientedSocket.h"
|
||||
|
||||
class MessageLoop;
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
class StreamSocketConsumer;
|
||||
class StreamSocketIO;
|
||||
class UnixSocketConnector;
|
||||
|
||||
class StreamSocket final : public ConnectionOrientedSocket
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs an instance of |StreamSocket|.
|
||||
*
|
||||
* @param aConsumer The consumer for the socket.
|
||||
* @param aIndex An arbitrary index.
|
||||
*/
|
||||
StreamSocket(StreamSocketConsumer* aConsumer, int aIndex);
|
||||
|
||||
/**
|
||||
* Method to be called whenever data is received. Consumer-thread only.
|
||||
*
|
||||
* @param aBuffer Data received from the socket.
|
||||
*/
|
||||
void ReceiveSocketData(UniquePtr<UnixSocketBuffer>& aBuffer);
|
||||
|
||||
/**
|
||||
* Starts a task on the socket that will try to connect to a socket in a
|
||||
* non-blocking manner.
|
||||
*
|
||||
* @param aConnector Connector object for socket type specific functions
|
||||
* @param aDelayMs Time delay in milliseconds.
|
||||
* @param aConsumerLoop The socket's consumer thread.
|
||||
* @param aIOLoop The socket's I/O thread.
|
||||
* @return NS_OK on success, or an XPCOM error code otherwise.
|
||||
*/
|
||||
nsresult Connect(UnixSocketConnector* aConnector, int aDelayMs,
|
||||
MessageLoop* aConsumerLoop, MessageLoop* aIOLoop);
|
||||
|
||||
/**
|
||||
* Starts a task on the socket that will try to connect to a socket in a
|
||||
* non-blocking manner.
|
||||
*
|
||||
* @param aConnector Connector object for socket type specific functions
|
||||
* @param aDelayMs Time delay in milliseconds.
|
||||
* @return NS_OK on success, or an XPCOM error code otherwise.
|
||||
*/
|
||||
nsresult Connect(UnixSocketConnector* aConnector, int aDelayMs = 0);
|
||||
|
||||
// Methods for |ConnectionOrientedSocket|
|
||||
//
|
||||
|
||||
nsresult PrepareAccept(UnixSocketConnector* aConnector,
|
||||
MessageLoop* aConsumerLoop,
|
||||
MessageLoop* aIOLoop,
|
||||
ConnectionOrientedSocketIO*& aIO) override;
|
||||
|
||||
// Methods for |DataSocket|
|
||||
//
|
||||
|
||||
void SendSocketData(UnixSocketIOBuffer* aBuffer) override;
|
||||
|
||||
// Methods for |SocketBase|
|
||||
//
|
||||
|
||||
void Close() override;
|
||||
void OnConnectSuccess() override;
|
||||
void OnConnectError() override;
|
||||
void OnDisconnect() override;
|
||||
|
||||
protected:
|
||||
virtual ~StreamSocket();
|
||||
|
||||
private:
|
||||
StreamSocketIO* mIO;
|
||||
StreamSocketConsumer* mConsumer;
|
||||
int mIndex;
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
} // namepsace mozilla
|
||||
|
||||
#endif // mozilla_ipc_streamsocket_h
|
||||
20
ipc/unixsocket/StreamSocketConsumer.cpp
Normal file
20
ipc/unixsocket/StreamSocketConsumer.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* -*- 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 "StreamSocketConsumer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
//
|
||||
// StreamSocketConsumer
|
||||
//
|
||||
|
||||
StreamSocketConsumer::~StreamSocketConsumer()
|
||||
{ }
|
||||
|
||||
}
|
||||
}
|
||||
60
ipc/unixsocket/StreamSocketConsumer.h
Normal file
60
ipc/unixsocket/StreamSocketConsumer.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- 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_streamsocketconsumer_h
|
||||
#define mozilla_ipc_streamsocketconsumer_h
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
class UnixSocketBuffer;
|
||||
|
||||
/**
|
||||
* |StreamSocketConsumer| handles socket events and received data.
|
||||
*/
|
||||
class StreamSocketConsumer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Method to be called whenever data is received. Consumer-thread only.
|
||||
*
|
||||
* @param aIndex The index that has been given to the stream socket.
|
||||
* @param aBuffer Data received from the socket.
|
||||
*/
|
||||
virtual void ReceiveSocketData(int aIndex,
|
||||
UniquePtr<UnixSocketBuffer>& aBuffer) = 0;
|
||||
|
||||
/**
|
||||
* Callback for socket success. Consumer-thread only.
|
||||
*
|
||||
* @param aIndex The index that has been given to the stream socket.
|
||||
*/
|
||||
virtual void OnConnectSuccess(int aIndex) = 0;
|
||||
|
||||
/**
|
||||
* Callback for socket errors. Consumer-thread only.
|
||||
*
|
||||
* @param aIndex The index that has been given to the stream socket.
|
||||
*/
|
||||
virtual void OnConnectError(int aIndex) = 0;
|
||||
|
||||
/**
|
||||
* Callback for socket disconnect. Consumer-thread only.
|
||||
*
|
||||
* @param aIndex The index that has been given to the stream socket.
|
||||
*/
|
||||
virtual void OnDisconnect(int aIndex) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~StreamSocketConsumer();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
38
ipc/unixsocket/UnixSocketConnector.cpp
Normal file
38
ipc/unixsocket/UnixSocketConnector.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* -*- 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 "UnixSocketConnector.h"
|
||||
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
UnixSocketConnector::UnixSocketConnector()
|
||||
{
|
||||
MOZ_COUNT_CTOR(UnixSocketConnector);
|
||||
}
|
||||
|
||||
UnixSocketConnector::~UnixSocketConnector()
|
||||
{
|
||||
MOZ_COUNT_DTOR(UnixSocketConnector);
|
||||
}
|
||||
|
||||
nsresult
|
||||
UnixSocketConnector::Duplicate(UniquePtr<UnixSocketConnector>& aConnector)
|
||||
{
|
||||
UnixSocketConnector* connectorPtr;
|
||||
auto rv = Duplicate(connectorPtr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
aConnector = Move(UniquePtr<UnixSocketConnector>(connectorPtr));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
102
ipc/unixsocket/UnixSocketConnector.h
Normal file
102
ipc/unixsocket/UnixSocketConnector.h
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/* -*- 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_unixsocketconnector_h
|
||||
#define mozilla_ipc_unixsocketconnector_h
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include "mozilla/ipc/UnixSocketWatcher.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
/**
|
||||
* |UnixSocketConnector| defines the socket creation and connection/listening
|
||||
* functions for |UnixSocketConsumer|, et al. Due to the fact that socket setup
|
||||
* can vary between protocols (Unix sockets, TCP sockets, Bluetooth sockets, etc),
|
||||
* this allows the user to create whatever connection mechanism they need while
|
||||
* still depending on libevent for non-blocking communication handling.
|
||||
*/
|
||||
class UnixSocketConnector
|
||||
{
|
||||
public:
|
||||
virtual ~UnixSocketConnector();
|
||||
|
||||
/**
|
||||
* Converts an address to a human-readable string.
|
||||
*
|
||||
* @param aAddress A socket address
|
||||
* @param aAddressLength The number of valid bytes in |aAddress|
|
||||
* @param[out] aAddressString The resulting string
|
||||
* @return NS_OK on success, or an XPCOM error code otherwise.
|
||||
*/
|
||||
virtual nsresult ConvertAddressToString(const struct sockaddr& aAddress,
|
||||
socklen_t aAddressLength,
|
||||
nsACString& aAddressString) = 0;
|
||||
|
||||
/**
|
||||
* Creates a listening socket. I/O thread only.
|
||||
*
|
||||
* @param[out] aAddress The listening socket's address
|
||||
* @param[out] aAddressLength The number of valid bytes in |aAddress|
|
||||
* @param[out] aListenFd The socket's file descriptor
|
||||
* @return NS_OK on success, or an XPCOM error code otherwise.
|
||||
*/
|
||||
virtual nsresult CreateListenSocket(struct sockaddr* aAddress,
|
||||
socklen_t* aAddressLength,
|
||||
int& aListenFd) = 0;
|
||||
|
||||
/**
|
||||
* Accepts a stream socket from a listening socket. I/O thread only.
|
||||
*
|
||||
* @param aListenFd The listening socket
|
||||
* @param[out] aAddress Returns the stream socket's address
|
||||
* @param[out] aAddressLength Returns the number of valid bytes in |aAddress|
|
||||
* @param[out] aStreamFd The stream socket's file descriptor
|
||||
* @return NS_OK on success, or an XPCOM error code otherwise.
|
||||
*/
|
||||
virtual nsresult AcceptStreamSocket(int aListenFd,
|
||||
struct sockaddr* aAddress,
|
||||
socklen_t* aAddressLen,
|
||||
int& aStreamFd) = 0;
|
||||
|
||||
/**
|
||||
* Creates a stream socket. I/O thread only.
|
||||
*
|
||||
* @param[in|out] aAddress The stream socket's address
|
||||
* @param[in|out] aAddressLength The number of valid bytes in |aAddress|
|
||||
* @param[out] aStreamFd The socket's file descriptor
|
||||
* @return NS_OK on success, or an XPCOM error code otherwise.
|
||||
*/
|
||||
virtual nsresult CreateStreamSocket(struct sockaddr* aAddress,
|
||||
socklen_t* aAddressLength,
|
||||
int& aStreamFd) = 0;
|
||||
|
||||
/**
|
||||
* Copies the instance of |UnixSocketConnector|. I/O thread only.
|
||||
*
|
||||
* @param[in] aConnector Returns a new instance of the connector class
|
||||
* @return NS_OK on success, or an XPCOM error code otherwise
|
||||
*/
|
||||
virtual nsresult Duplicate(UnixSocketConnector*& aConnector) = 0;
|
||||
|
||||
/**
|
||||
* Copies the instance of |UnixSocketConnector|. I/O thread only.
|
||||
*
|
||||
* @param[in] aConnector Returns a new instance of the connector class
|
||||
* @return NS_OK on success, or an XPCOM error code otherwise
|
||||
*/
|
||||
nsresult Duplicate(UniquePtr<UnixSocketConnector>& aConnector);
|
||||
|
||||
protected:
|
||||
UnixSocketConnector();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
31
ipc/unixsocket/moz.build
Normal file
31
ipc/unixsocket/moz.build
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# -*- 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 += [
|
||||
'ConnectionOrientedSocket.h',
|
||||
'DataSocket.h',
|
||||
'ListenSocket.h',
|
||||
'ListenSocketConsumer.h',
|
||||
'SocketBase.h',
|
||||
'StreamSocket.h',
|
||||
'StreamSocketConsumer.h',
|
||||
'UnixSocketConnector.h'
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'ConnectionOrientedSocket.cpp',
|
||||
'DataSocket.cpp',
|
||||
'ListenSocket.cpp',
|
||||
'ListenSocketConsumer.cpp',
|
||||
'SocketBase.cpp',
|
||||
'StreamSocket.cpp',
|
||||
'StreamSocketConsumer.cpp',
|
||||
'UnixSocketConnector.cpp'
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
Loading…
Add table
Add a link
Reference in a new issue