| Index: third_party/mojo/src/mojo/edk/system/master_connection_manager.cc
|
| diff --git a/third_party/mojo/src/mojo/edk/system/master_connection_manager.cc b/third_party/mojo/src/mojo/edk/system/master_connection_manager.cc
|
| deleted file mode 100644
|
| index ced54ec069a1642a24abc562500d93dab69117cc..0000000000000000000000000000000000000000
|
| --- a/third_party/mojo/src/mojo/edk/system/master_connection_manager.cc
|
| +++ /dev/null
|
| @@ -1,741 +0,0 @@
|
| -// Copyright 2015 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "third_party/mojo/src/mojo/edk/system/master_connection_manager.h"
|
| -
|
| -#include <utility>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/bind_helpers.h"
|
| -#include "base/location.h"
|
| -#include "base/logging.h"
|
| -#include "base/message_loop/message_loop.h"
|
| -#include "base/synchronization/waitable_event.h"
|
| -#include "mojo/public/cpp/system/macros.h"
|
| -#include "third_party/mojo/src/mojo/edk/embedder/master_process_delegate.h"
|
| -#include "third_party/mojo/src/mojo/edk/embedder/platform_channel_pair.h"
|
| -#include "third_party/mojo/src/mojo/edk/embedder/platform_handle.h"
|
| -#include "third_party/mojo/src/mojo/edk/embedder/platform_handle_vector.h"
|
| -#include "third_party/mojo/src/mojo/edk/system/connection_manager_messages.h"
|
| -#include "third_party/mojo/src/mojo/edk/system/message_in_transit.h"
|
| -#include "third_party/mojo/src/mojo/edk/system/raw_channel.h"
|
| -#include "third_party/mojo/src/mojo/edk/system/transport_data.h"
|
| -
|
| -namespace mojo {
|
| -namespace system {
|
| -
|
| -namespace {
|
| -
|
| -const ProcessIdentifier kFirstSlaveProcessIdentifier = 2;
|
| -
|
| -static_assert(kMasterProcessIdentifier != kInvalidProcessIdentifier,
|
| - "Bad master process identifier");
|
| -static_assert(kFirstSlaveProcessIdentifier != kInvalidProcessIdentifier,
|
| - "Bad first slave process identifier");
|
| -static_assert(kMasterProcessIdentifier != kFirstSlaveProcessIdentifier,
|
| - "Master and first slave process identifiers are the same");
|
| -
|
| -MessageInTransit::Subtype ConnectionManagerResultToMessageInTransitSubtype(
|
| - ConnectionManager::Result result) {
|
| - switch (result) {
|
| - case ConnectionManager::Result::FAILURE:
|
| - return MessageInTransit::Subtype::CONNECTION_MANAGER_ACK_FAILURE;
|
| - case ConnectionManager::Result::SUCCESS:
|
| - return MessageInTransit::Subtype::CONNECTION_MANAGER_ACK_SUCCESS;
|
| - case ConnectionManager::Result::SUCCESS_CONNECT_SAME_PROCESS:
|
| - return MessageInTransit::Subtype::
|
| - CONNECTION_MANAGER_ACK_SUCCESS_CONNECT_SAME_PROCESS;
|
| - case ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION:
|
| - return MessageInTransit::Subtype::
|
| - CONNECTION_MANAGER_ACK_SUCCESS_CONNECT_NEW_CONNECTION;
|
| - case ConnectionManager::Result::SUCCESS_CONNECT_REUSE_CONNECTION:
|
| - return MessageInTransit::Subtype::
|
| - CONNECTION_MANAGER_ACK_SUCCESS_CONNECT_REUSE_CONNECTION;
|
| - }
|
| - NOTREACHED();
|
| - return MessageInTransit::Subtype::CONNECTION_MANAGER_ACK_FAILURE;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -// MasterConnectionManager::Helper ---------------------------------------------
|
| -
|
| -// |MasterConnectionManager::Helper| is not thread-safe, and must only be used
|
| -// on its |owner_|'s private thread.
|
| -class MOJO_SYSTEM_IMPL_EXPORT MasterConnectionManager::Helper final
|
| - : public RawChannel::Delegate {
|
| - public:
|
| - Helper(MasterConnectionManager* owner,
|
| - ProcessIdentifier process_identifier,
|
| - embedder::SlaveInfo slave_info,
|
| - embedder::ScopedPlatformHandle platform_handle);
|
| - ~Helper() override;
|
| -
|
| - void Init();
|
| - embedder::SlaveInfo Shutdown();
|
| -
|
| - private:
|
| - // |RawChannel::Delegate| methods:
|
| - void OnReadMessage(
|
| - const MessageInTransit::View& message_view,
|
| - embedder::ScopedPlatformHandleVectorPtr platform_handles) override;
|
| - void OnError(Error error) override;
|
| -
|
| - // Handles an error that's fatal to this object. Note that this probably
|
| - // results in |Shutdown()| being called (in the nested context) and then this
|
| - // object being destroyed.
|
| - void FatalError();
|
| -
|
| - MasterConnectionManager* const owner_;
|
| - const ProcessIdentifier process_identifier_;
|
| - embedder::SlaveInfo const slave_info_;
|
| - scoped_ptr<RawChannel> raw_channel_;
|
| -
|
| - MOJO_DISALLOW_COPY_AND_ASSIGN(Helper);
|
| -};
|
| -
|
| -MasterConnectionManager::Helper::Helper(
|
| - MasterConnectionManager* owner,
|
| - ProcessIdentifier process_identifier,
|
| - embedder::SlaveInfo slave_info,
|
| - embedder::ScopedPlatformHandle platform_handle)
|
| - : owner_(owner),
|
| - process_identifier_(process_identifier),
|
| - slave_info_(slave_info),
|
| - raw_channel_(RawChannel::Create(std::move(platform_handle))) {}
|
| -
|
| -MasterConnectionManager::Helper::~Helper() {
|
| - DCHECK(!raw_channel_);
|
| -}
|
| -
|
| -void MasterConnectionManager::Helper::Init() {
|
| - raw_channel_->Init(this);
|
| -}
|
| -
|
| -embedder::SlaveInfo MasterConnectionManager::Helper::Shutdown() {
|
| - raw_channel_->Shutdown();
|
| - raw_channel_.reset();
|
| - return slave_info_;
|
| -}
|
| -
|
| -void MasterConnectionManager::Helper::OnReadMessage(
|
| - const MessageInTransit::View& message_view,
|
| - embedder::ScopedPlatformHandleVectorPtr platform_handles) {
|
| - if (message_view.type() != MessageInTransit::Type::CONNECTION_MANAGER) {
|
| - LOG(ERROR) << "Invalid message type " << message_view.type();
|
| - FatalError(); // WARNING: This destroys us.
|
| - return;
|
| - }
|
| -
|
| - // Currently, all the messages simply have a |ConnectionIdentifier| as data.
|
| - if (message_view.num_bytes() != sizeof(ConnectionIdentifier)) {
|
| - LOG(ERROR) << "Invalid message size " << message_view.num_bytes();
|
| - FatalError(); // WARNING: This destroys us.
|
| - return;
|
| - }
|
| -
|
| - // And none of them should have any platform handles attached.
|
| - if (message_view.transport_data_buffer()) {
|
| - LOG(ERROR) << "Invalid message with transport data";
|
| - FatalError(); // WARNING: This destroys us.
|
| - return;
|
| - }
|
| -
|
| - const ConnectionIdentifier* connection_id =
|
| - reinterpret_cast<const ConnectionIdentifier*>(message_view.bytes());
|
| - Result result = Result::FAILURE;
|
| - // Note: It's important to fully zero-initialize |data|, including padding,
|
| - // since it'll be sent to another process.
|
| - ConnectionManagerAckSuccessConnectData data = {};
|
| - embedder::ScopedPlatformHandle platform_handle;
|
| - uint32_t num_bytes = 0;
|
| - const void* bytes = nullptr;
|
| - switch (message_view.subtype()) {
|
| - case MessageInTransit::Subtype::CONNECTION_MANAGER_ALLOW_CONNECT:
|
| - result = owner_->AllowConnectImpl(process_identifier_, *connection_id)
|
| - ? Result::SUCCESS
|
| - : Result::FAILURE;
|
| - break;
|
| - case MessageInTransit::Subtype::CONNECTION_MANAGER_CANCEL_CONNECT:
|
| - result = owner_->CancelConnectImpl(process_identifier_, *connection_id)
|
| - ? Result::SUCCESS
|
| - : Result::FAILURE;
|
| - break;
|
| - case MessageInTransit::Subtype::CONNECTION_MANAGER_CONNECT: {
|
| - result = owner_->ConnectImpl(process_identifier_, *connection_id,
|
| - &data.peer_process_identifier,
|
| - &data.is_first, &platform_handle);
|
| - DCHECK_NE(result, Result::SUCCESS);
|
| - // Success acks for "connect" have the peer process identifier as data
|
| - // (and also a platform handle in the case of "new connection" -- handled
|
| - // further below).
|
| - if (result != Result::FAILURE) {
|
| - num_bytes = static_cast<uint32_t>(sizeof(data));
|
| - bytes = &data;
|
| - }
|
| - break;
|
| - }
|
| - default:
|
| - LOG(ERROR) << "Invalid message subtype " << message_view.subtype();
|
| - FatalError(); // WARNING: This destroys us.
|
| - return;
|
| - }
|
| -
|
| - scoped_ptr<MessageInTransit> response(new MessageInTransit(
|
| - MessageInTransit::Type::CONNECTION_MANAGER_ACK,
|
| - ConnectionManagerResultToMessageInTransitSubtype(result), num_bytes,
|
| - bytes));
|
| -
|
| - if (result == Result::SUCCESS_CONNECT_NEW_CONNECTION) {
|
| - DCHECK_EQ(message_view.subtype(),
|
| - MessageInTransit::Subtype::CONNECTION_MANAGER_CONNECT);
|
| - DCHECK(platform_handle.is_valid());
|
| - embedder::ScopedPlatformHandleVectorPtr platform_handles(
|
| - new embedder::PlatformHandleVector());
|
| - platform_handles->push_back(platform_handle.release());
|
| - response->SetTransportData(make_scoped_ptr(
|
| - new TransportData(std::move(platform_handles),
|
| - raw_channel_->GetSerializedPlatformHandleSize())));
|
| - } else {
|
| - DCHECK(!platform_handle.is_valid());
|
| - }
|
| -
|
| - if (!raw_channel_->WriteMessage(std::move(response))) {
|
| - LOG(ERROR) << "WriteMessage failed";
|
| - FatalError(); // WARNING: This destroys us.
|
| - return;
|
| - }
|
| -}
|
| -
|
| -void MasterConnectionManager::Helper::OnError(Error /*error*/) {
|
| - // Every error (read or write) is fatal (for that particular connection). Read
|
| - // errors are fatal since no more commands will be received from that
|
| - // connection. Write errors are fatal since it is no longer possible to send
|
| - // responses.
|
| - FatalError(); // WARNING: This destroys us.
|
| -}
|
| -
|
| -void MasterConnectionManager::Helper::FatalError() {
|
| - owner_->OnError(process_identifier_); // WARNING: This destroys us.
|
| -}
|
| -
|
| -// MasterConnectionManager::PendingConnectInfo ---------------------------------
|
| -
|
| -struct MOJO_SYSTEM_IMPL_EXPORT MasterConnectionManager::PendingConnectInfo {
|
| - // States:
|
| - // - This is created upon a first "allow connect" (with |first| set
|
| - // immediately). We then wait for a second "allow connect".
|
| - // - After the second "allow connect" (and |second| is set), we wait for
|
| - // "connects" from both |first| and |second|.
|
| - // - We may then receive "connect" from either |first| or |second|, at which
|
| - // which point it remains to wait for "connect" from the other.
|
| - // I.e., the valid state transitions are:
|
| - // AWAITING_SECOND_ALLOW_CONNECT -> AWAITING_CONNECTS_FROM_BOTH
|
| - // -> {AWAITING_CONNECT_FROM_FIRST,AWAITING_CONNECT_FROM_SECOND}
|
| - enum class State {
|
| - AWAITING_SECOND_ALLOW_CONNECT,
|
| - AWAITING_CONNECTS_FROM_BOTH,
|
| - AWAITING_CONNECT_FROM_FIRST,
|
| - AWAITING_CONNECT_FROM_SECOND
|
| - };
|
| -
|
| - explicit PendingConnectInfo(ProcessIdentifier first)
|
| - : state(State::AWAITING_SECOND_ALLOW_CONNECT),
|
| - first(first),
|
| - second(kInvalidProcessIdentifier) {
|
| - DCHECK_NE(first, kInvalidProcessIdentifier);
|
| - }
|
| - ~PendingConnectInfo() {}
|
| -
|
| - State state;
|
| -
|
| - ProcessIdentifier first;
|
| - ProcessIdentifier second;
|
| -};
|
| -
|
| -// MasterConnectionManager::ProcessConnections ---------------------------------
|
| -
|
| -class MasterConnectionManager::ProcessConnections {
|
| - public:
|
| - enum class ConnectionStatus { NONE, PENDING, RUNNING };
|
| -
|
| - ProcessConnections() {}
|
| - ~ProcessConnections() {
|
| - // TODO(vtl): Log a warning if there are connections pending? (This might be
|
| - // very spammy, since the |MasterConnectionManager| may have many
|
| - // |ProcessConnections|.
|
| - for (auto& p : process_connections_)
|
| - p.second.CloseIfNecessary();
|
| - }
|
| -
|
| - // If |pending_platform_handle| is non-null and the status is |PENDING| this
|
| - // will "return"/pass the stored pending platform handle. Warning: In that
|
| - // case, this has the side effect of changing the state to |RUNNING|.
|
| - ConnectionStatus GetConnectionStatus(
|
| - ProcessIdentifier to_process_identifier,
|
| - embedder::ScopedPlatformHandle* pending_platform_handle) {
|
| - DCHECK(!pending_platform_handle || !pending_platform_handle->is_valid());
|
| -
|
| - auto it = process_connections_.find(to_process_identifier);
|
| - if (it == process_connections_.end())
|
| - return ConnectionStatus::NONE;
|
| - if (!it->second.is_valid())
|
| - return ConnectionStatus::RUNNING;
|
| - // Pending:
|
| - if (pending_platform_handle) {
|
| - pending_platform_handle->reset(it->second);
|
| - it->second = embedder::PlatformHandle();
|
| - }
|
| - return ConnectionStatus::PENDING;
|
| - }
|
| -
|
| - void AddConnection(ProcessIdentifier to_process_identifier,
|
| - ConnectionStatus status,
|
| - embedder::ScopedPlatformHandle pending_platform_handle) {
|
| - DCHECK(process_connections_.find(to_process_identifier) ==
|
| - process_connections_.end());
|
| -
|
| - if (status == ConnectionStatus::RUNNING) {
|
| - DCHECK(!pending_platform_handle.is_valid());
|
| - process_connections_[to_process_identifier] = embedder::PlatformHandle();
|
| - } else if (status == ConnectionStatus::PENDING) {
|
| - DCHECK(pending_platform_handle.is_valid());
|
| - process_connections_[to_process_identifier] =
|
| - pending_platform_handle.release();
|
| - } else {
|
| - NOTREACHED();
|
| - }
|
| - }
|
| -
|
| - private:
|
| - base::hash_map<ProcessIdentifier, embedder::PlatformHandle>
|
| - process_connections_; // "Owns" any valid platform handles.
|
| -
|
| - MOJO_DISALLOW_COPY_AND_ASSIGN(ProcessConnections);
|
| -};
|
| -
|
| -// MasterConnectionManager -----------------------------------------------------
|
| -
|
| -MasterConnectionManager::MasterConnectionManager(
|
| - embedder::PlatformSupport* platform_support)
|
| - : ConnectionManager(platform_support),
|
| - master_process_delegate_(),
|
| - private_thread_("MasterConnectionManagerPrivateThread"),
|
| - next_process_identifier_(kFirstSlaveProcessIdentifier) {
|
| - connections_[kMasterProcessIdentifier] = new ProcessConnections();
|
| -}
|
| -
|
| -MasterConnectionManager::~MasterConnectionManager() {
|
| - DCHECK(!delegate_thread_task_runner_);
|
| - DCHECK(!master_process_delegate_);
|
| - DCHECK(!private_thread_.message_loop());
|
| - DCHECK(helpers_.empty());
|
| - DCHECK(pending_connects_.empty());
|
| -}
|
| -
|
| -void MasterConnectionManager::Init(
|
| - embedder::MasterProcessDelegate* master_process_delegate) {
|
| - DCHECK(master_process_delegate);
|
| - DCHECK(!delegate_thread_task_runner_);
|
| - DCHECK(!master_process_delegate_);
|
| - DCHECK(!private_thread_.message_loop());
|
| -
|
| - delegate_thread_task_runner_ = base::MessageLoop::current()->task_runner();
|
| - master_process_delegate_ = master_process_delegate;
|
| - CHECK(private_thread_.StartWithOptions(
|
| - base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
|
| -}
|
| -
|
| -ProcessIdentifier MasterConnectionManager::AddSlave(
|
| - embedder::SlaveInfo slave_info,
|
| - embedder::ScopedPlatformHandle platform_handle) {
|
| - // We don't really care if |slave_info| is non-null or not.
|
| - DCHECK(platform_handle.is_valid());
|
| - AssertNotOnPrivateThread();
|
| -
|
| - ProcessIdentifier slave_process_identifier;
|
| - {
|
| - MutexLocker locker(&mutex_);
|
| - CHECK_NE(next_process_identifier_, kMasterProcessIdentifier);
|
| - slave_process_identifier = next_process_identifier_;
|
| - next_process_identifier_++;
|
| - DCHECK(connections_.find(slave_process_identifier) == connections_.end());
|
| - connections_[slave_process_identifier] = new ProcessConnections();
|
| - }
|
| -
|
| - // We have to wait for the task to be executed, in case someone calls
|
| - // |AddSlave()| followed immediately by |Shutdown()|.
|
| - base::WaitableEvent event(false, false);
|
| - private_thread_.message_loop()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&MasterConnectionManager::AddSlaveOnPrivateThread,
|
| - base::Unretained(this), base::Unretained(slave_info),
|
| - base::Passed(&platform_handle), slave_process_identifier,
|
| - base::Unretained(&event)));
|
| - event.Wait();
|
| -
|
| - return slave_process_identifier;
|
| -}
|
| -
|
| -ProcessIdentifier MasterConnectionManager::AddSlaveAndBootstrap(
|
| - embedder::SlaveInfo slave_info,
|
| - embedder::ScopedPlatformHandle platform_handle,
|
| - const ConnectionIdentifier& connection_id) {
|
| - ProcessIdentifier slave_process_identifier =
|
| - AddSlave(slave_info, std::move(platform_handle));
|
| -
|
| - MutexLocker locker(&mutex_);
|
| - DCHECK(pending_connects_.find(connection_id) == pending_connects_.end());
|
| - PendingConnectInfo* info = new PendingConnectInfo(kMasterProcessIdentifier);
|
| - info->state = PendingConnectInfo::State::AWAITING_CONNECTS_FROM_BOTH;
|
| - info->second = slave_process_identifier;
|
| - pending_connects_[connection_id] = info;
|
| -
|
| - return slave_process_identifier;
|
| -}
|
| -
|
| -void MasterConnectionManager::Shutdown() {
|
| - AssertNotOnPrivateThread();
|
| - DCHECK(master_process_delegate_);
|
| - DCHECK(private_thread_.message_loop());
|
| -
|
| - // The |Stop()| will actually finish all posted tasks.
|
| - private_thread_.message_loop()->PostTask(
|
| - FROM_HERE, base::Bind(&MasterConnectionManager::ShutdownOnPrivateThread,
|
| - base::Unretained(this)));
|
| - private_thread_.Stop();
|
| - DCHECK(helpers_.empty());
|
| - DCHECK(pending_connects_.empty());
|
| - master_process_delegate_ = nullptr;
|
| - delegate_thread_task_runner_ = nullptr;
|
| -}
|
| -
|
| -bool MasterConnectionManager::AllowConnect(
|
| - const ConnectionIdentifier& connection_id) {
|
| - AssertNotOnPrivateThread();
|
| - return AllowConnectImpl(kMasterProcessIdentifier, connection_id);
|
| -}
|
| -
|
| -bool MasterConnectionManager::CancelConnect(
|
| - const ConnectionIdentifier& connection_id) {
|
| - AssertNotOnPrivateThread();
|
| - return CancelConnectImpl(kMasterProcessIdentifier, connection_id);
|
| -}
|
| -
|
| -ConnectionManager::Result MasterConnectionManager::Connect(
|
| - const ConnectionIdentifier& connection_id,
|
| - ProcessIdentifier* peer_process_identifier,
|
| - bool* is_first,
|
| - embedder::ScopedPlatformHandle* platform_handle) {
|
| - return ConnectImpl(kMasterProcessIdentifier, connection_id,
|
| - peer_process_identifier, is_first, platform_handle);
|
| -}
|
| -
|
| -bool MasterConnectionManager::AllowConnectImpl(
|
| - ProcessIdentifier process_identifier,
|
| - const ConnectionIdentifier& connection_id) {
|
| - DCHECK_NE(process_identifier, kInvalidProcessIdentifier);
|
| -
|
| - MutexLocker locker(&mutex_);
|
| -
|
| - auto it = pending_connects_.find(connection_id);
|
| - if (it == pending_connects_.end()) {
|
| - pending_connects_[connection_id] =
|
| - new PendingConnectInfo(process_identifier);
|
| - // TODO(vtl): Track process identifier -> pending connections also (so these
|
| - // can be removed efficiently if that process disconnects).
|
| - DVLOG(1) << "New pending connection ID " << connection_id.ToString()
|
| - << ": AllowConnect() from first process identifier "
|
| - << process_identifier;
|
| - return true;
|
| - }
|
| -
|
| - PendingConnectInfo* info = it->second;
|
| - if (info->state == PendingConnectInfo::State::AWAITING_SECOND_ALLOW_CONNECT) {
|
| - info->state = PendingConnectInfo::State::AWAITING_CONNECTS_FROM_BOTH;
|
| - info->second = process_identifier;
|
| - DVLOG(1) << "Pending connection ID " << connection_id.ToString()
|
| - << ": AllowConnect() from second process identifier "
|
| - << process_identifier;
|
| - return true;
|
| - }
|
| -
|
| - // Someone's behaving badly, but we don't know who (it might not be the
|
| - // caller).
|
| - LOG(ERROR) << "AllowConnect() from process " << process_identifier
|
| - << " for connection ID " << connection_id.ToString()
|
| - << " already in state " << static_cast<int>(info->state);
|
| - pending_connects_.erase(it);
|
| - delete info;
|
| - return false;
|
| -}
|
| -
|
| -bool MasterConnectionManager::CancelConnectImpl(
|
| - ProcessIdentifier process_identifier,
|
| - const ConnectionIdentifier& connection_id) {
|
| - DCHECK_NE(process_identifier, kInvalidProcessIdentifier);
|
| -
|
| - MutexLocker locker(&mutex_);
|
| -
|
| - auto it = pending_connects_.find(connection_id);
|
| - if (it == pending_connects_.end()) {
|
| - // Not necessarily the caller's fault, and not necessarily an error.
|
| - DVLOG(1) << "CancelConnect() from process " << process_identifier
|
| - << " for connection ID " << connection_id.ToString()
|
| - << " which is not (or no longer) pending";
|
| - return true;
|
| - }
|
| -
|
| - PendingConnectInfo* info = it->second;
|
| - if (process_identifier != info->first && process_identifier != info->second) {
|
| - LOG(ERROR) << "CancelConnect() from process " << process_identifier
|
| - << " for connection ID " << connection_id.ToString()
|
| - << " which is neither connectee";
|
| - return false;
|
| - }
|
| -
|
| - // Just erase it. The other side may also try to cancel, in which case it'll
|
| - // "fail" in the first if statement above (we assume that connection IDs never
|
| - // collide, so there's no need to carefully track both sides).
|
| - pending_connects_.erase(it);
|
| - delete info;
|
| - return true;
|
| -}
|
| -
|
| -ConnectionManager::Result MasterConnectionManager::ConnectImpl(
|
| - ProcessIdentifier process_identifier,
|
| - const ConnectionIdentifier& connection_id,
|
| - ProcessIdentifier* peer_process_identifier,
|
| - bool* is_first,
|
| - embedder::ScopedPlatformHandle* platform_handle) {
|
| - DCHECK_NE(process_identifier, kInvalidProcessIdentifier);
|
| - DCHECK(peer_process_identifier);
|
| - DCHECK(is_first);
|
| - DCHECK(platform_handle);
|
| - DCHECK(!platform_handle->is_valid()); // Not technically wrong, but unlikely.
|
| -
|
| - MutexLocker locker(&mutex_);
|
| -
|
| - auto it = pending_connects_.find(connection_id);
|
| - if (it == pending_connects_.end()) {
|
| - // Not necessarily the caller's fault.
|
| - LOG(ERROR) << "Connect() from process " << process_identifier
|
| - << " for connection ID " << connection_id.ToString()
|
| - << " which is not pending";
|
| - return Result::FAILURE;
|
| - }
|
| -
|
| - PendingConnectInfo* info = it->second;
|
| - ProcessIdentifier peer;
|
| - if (info->state == PendingConnectInfo::State::AWAITING_CONNECTS_FROM_BOTH) {
|
| - if (process_identifier == info->first) {
|
| - info->state = PendingConnectInfo::State::AWAITING_CONNECT_FROM_SECOND;
|
| - peer = info->second;
|
| - } else if (process_identifier == info->second) {
|
| - info->state = PendingConnectInfo::State::AWAITING_CONNECT_FROM_FIRST;
|
| - peer = info->first;
|
| - } else {
|
| - LOG(ERROR) << "Connect() from process " << process_identifier
|
| - << " for connection ID " << connection_id.ToString()
|
| - << " which is neither connectee";
|
| - return Result::FAILURE;
|
| - }
|
| -
|
| - DVLOG(1) << "Connection ID " << connection_id.ToString()
|
| - << ": first Connect() from process identifier "
|
| - << process_identifier;
|
| - *peer_process_identifier = peer;
|
| - *is_first = true;
|
| - return ConnectImplHelperNoLock(process_identifier, peer, platform_handle);
|
| - }
|
| -
|
| - // The remaining cases all result in |it| being removed from
|
| - // |pending_connects_| and deleting |info|.
|
| - pending_connects_.erase(it);
|
| - scoped_ptr<PendingConnectInfo> info_deleter(info);
|
| -
|
| - // |remaining_connectee| should be the same as |process_identifier|.
|
| - ProcessIdentifier remaining_connectee;
|
| - if (info->state == PendingConnectInfo::State::AWAITING_CONNECT_FROM_FIRST) {
|
| - remaining_connectee = info->first;
|
| - peer = info->second;
|
| - } else if (info->state ==
|
| - PendingConnectInfo::State::AWAITING_CONNECT_FROM_SECOND) {
|
| - remaining_connectee = info->second;
|
| - peer = info->first;
|
| - } else {
|
| - // Someone's behaving badly, but we don't know who (it might not be the
|
| - // caller).
|
| - LOG(ERROR) << "Connect() from process " << process_identifier
|
| - << " for connection ID " << connection_id.ToString()
|
| - << " in state " << static_cast<int>(info->state);
|
| - return Result::FAILURE;
|
| - }
|
| -
|
| - if (process_identifier != remaining_connectee) {
|
| - LOG(ERROR) << "Connect() from process " << process_identifier
|
| - << " for connection ID " << connection_id.ToString()
|
| - << " which is not the remaining connectee";
|
| - return Result::FAILURE;
|
| - }
|
| -
|
| - DVLOG(1) << "Connection ID " << connection_id.ToString()
|
| - << ": second Connect() from process identifier "
|
| - << process_identifier;
|
| - *peer_process_identifier = peer;
|
| - *is_first = false;
|
| - return ConnectImplHelperNoLock(process_identifier, peer, platform_handle);
|
| -}
|
| -
|
| -ConnectionManager::Result MasterConnectionManager::ConnectImplHelperNoLock(
|
| - ProcessIdentifier process_identifier,
|
| - ProcessIdentifier peer_process_identifier,
|
| - embedder::ScopedPlatformHandle* platform_handle) {
|
| - if (process_identifier == peer_process_identifier) {
|
| - platform_handle->reset();
|
| - DVLOG(1) << "Connect: same process";
|
| - return Result::SUCCESS_CONNECT_SAME_PROCESS;
|
| - }
|
| -
|
| - // We should know about the process identified by |process_identifier|.
|
| - DCHECK(connections_.find(process_identifier) != connections_.end());
|
| - ProcessConnections* process_connections = connections_[process_identifier];
|
| - // We should also know about the peer.
|
| - DCHECK(connections_.find(peer_process_identifier) != connections_.end());
|
| - switch (process_connections->GetConnectionStatus(peer_process_identifier,
|
| - platform_handle)) {
|
| - case ProcessConnections::ConnectionStatus::NONE: {
|
| - // TODO(vtl): In the "second connect" case, this should never be reached
|
| - // (but it's not easy to DCHECK this invariant here).
|
| - process_connections->AddConnection(
|
| - peer_process_identifier,
|
| - ProcessConnections::ConnectionStatus::RUNNING,
|
| - embedder::ScopedPlatformHandle());
|
| - embedder::PlatformChannelPair platform_channel_pair;
|
| - *platform_handle = platform_channel_pair.PassServerHandle();
|
| -
|
| - connections_[peer_process_identifier]->AddConnection(
|
| - process_identifier, ProcessConnections::ConnectionStatus::PENDING,
|
| - platform_channel_pair.PassClientHandle());
|
| - break;
|
| - }
|
| - case ProcessConnections::ConnectionStatus::PENDING:
|
| - DCHECK(connections_[peer_process_identifier]->GetConnectionStatus(
|
| - process_identifier, nullptr) ==
|
| - ProcessConnections::ConnectionStatus::RUNNING);
|
| - break;
|
| - case ProcessConnections::ConnectionStatus::RUNNING:
|
| - // |process_identifier| already has a connection to
|
| - // |peer_process_identifier|, so it should reuse that.
|
| - platform_handle->reset();
|
| - DVLOG(1) << "Connect: reuse connection";
|
| - return Result::SUCCESS_CONNECT_REUSE_CONNECTION;
|
| - }
|
| - DCHECK(platform_handle->is_valid());
|
| - DVLOG(1) << "Connect: new connection";
|
| - return Result::SUCCESS_CONNECT_NEW_CONNECTION;
|
| -}
|
| -
|
| -void MasterConnectionManager::ShutdownOnPrivateThread() {
|
| - AssertOnPrivateThread();
|
| -
|
| - if (!pending_connects_.empty()) {
|
| - DVLOG(1) << "Shutting down with connections pending";
|
| - for (auto& p : pending_connects_)
|
| - delete p.second;
|
| - pending_connects_.clear();
|
| - }
|
| -
|
| - for (auto& p : connections_)
|
| - delete p.second;
|
| - connections_.clear();
|
| -
|
| - if (!helpers_.empty()) {
|
| - DVLOG(1) << "Shutting down with slaves still connected";
|
| - for (auto& p : helpers_) {
|
| - embedder::SlaveInfo slave_info = p.second->Shutdown();
|
| - delete p.second;
|
| - CallOnSlaveDisconnect(slave_info);
|
| - }
|
| - helpers_.clear();
|
| - }
|
| -}
|
| -
|
| -void MasterConnectionManager::AddSlaveOnPrivateThread(
|
| - embedder::SlaveInfo slave_info,
|
| - embedder::ScopedPlatformHandle platform_handle,
|
| - ProcessIdentifier slave_process_identifier,
|
| - base::WaitableEvent* event) {
|
| - DCHECK(platform_handle.is_valid());
|
| - DCHECK(event);
|
| - AssertOnPrivateThread();
|
| -
|
| - scoped_ptr<Helper> helper(new Helper(this, slave_process_identifier,
|
| - slave_info, std::move(platform_handle)));
|
| - helper->Init();
|
| -
|
| - DCHECK(helpers_.find(slave_process_identifier) == helpers_.end());
|
| - helpers_[slave_process_identifier] = helper.release();
|
| -
|
| - DVLOG(1) << "Added slave process identifier " << slave_process_identifier;
|
| - event->Signal();
|
| -}
|
| -
|
| -void MasterConnectionManager::OnError(ProcessIdentifier process_identifier) {
|
| - DCHECK_NE(process_identifier, kInvalidProcessIdentifier);
|
| - AssertOnPrivateThread();
|
| -
|
| - auto it = helpers_.find(process_identifier);
|
| - DCHECK(it != helpers_.end());
|
| - Helper* helper = it->second;
|
| - embedder::SlaveInfo slave_info = helper->Shutdown();
|
| - helpers_.erase(it);
|
| - delete helper;
|
| -
|
| - {
|
| - MutexLocker locker(&mutex_);
|
| -
|
| - // TODO(vtl): This isn't very efficient.
|
| - for (auto it = pending_connects_.begin(); it != pending_connects_.end();) {
|
| - if (it->second->first == process_identifier ||
|
| - it->second->second == process_identifier) {
|
| - auto it_to_erase = it;
|
| - ++it;
|
| - delete it_to_erase->second;
|
| - pending_connects_.erase(it_to_erase);
|
| - } else {
|
| - ++it;
|
| - }
|
| - }
|
| - }
|
| -
|
| - CallOnSlaveDisconnect(slave_info);
|
| -}
|
| -
|
| -void MasterConnectionManager::CallOnSlaveDisconnect(
|
| - embedder::SlaveInfo slave_info) {
|
| - AssertOnPrivateThread();
|
| - DCHECK(master_process_delegate_);
|
| - delegate_thread_task_runner_->PostTask(
|
| - FROM_HERE, base::Bind(&embedder::MasterProcessDelegate::OnSlaveDisconnect,
|
| - base::Unretained(master_process_delegate_),
|
| - base::Unretained(slave_info)));
|
| -}
|
| -
|
| -void MasterConnectionManager::AssertNotOnPrivateThread() const {
|
| - // This should only be called after |Init()| and before |Shutdown()|. (If not,
|
| - // the subsequent |DCHECK_NE()| is invalid, since the current thread may not
|
| - // have a message loop.)
|
| - DCHECK(private_thread_.message_loop());
|
| - DCHECK_NE(base::MessageLoop::current(), private_thread_.message_loop());
|
| -}
|
| -
|
| -void MasterConnectionManager::AssertOnPrivateThread() const {
|
| - // This should only be called after |Init()| and before |Shutdown()|.
|
| - DCHECK(private_thread_.message_loop());
|
| - DCHECK_EQ(base::MessageLoop::current(), private_thread_.message_loop());
|
| -}
|
| -
|
| -} // namespace system
|
| -} // namespace mojo
|
|
|