forked from mirrors/gecko-dev
		
	 42c137c01d
			
		
	
	
		42c137c01d
		
	
	
	
	
		
			
			This patch converts the remaining B2G code from |Task| to |Runnable| and fixes related API calls. This is a follow-up to bug 1266595.
		
			
				
	
	
		
			449 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			449 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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;
 | |
| }
 | |
| 
 | |
| }
 | |
| }
 |