diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /ipc/unixsocket/SocketBase.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'ipc/unixsocket/SocketBase.cpp')
-rw-r--r-- | ipc/unixsocket/SocketBase.cpp | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/ipc/unixsocket/SocketBase.cpp b/ipc/unixsocket/SocketBase.cpp new file mode 100644 index 000000000..b11729652 --- /dev/null +++ b/ipc/unixsocket/SocketBase.cpp @@ -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; +} + +} +} |