blob: d376cca58387ef148fda28b211faa4b371e4f4fb [file] [log] [blame]
// Copyright 2016 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 "mojo/edk/system/remote_message_pipe_bootstrap.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/threading/thread_task_runner_handle.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/system/node_controller.h"
#include "mojo/edk/system/ports/name.h"
namespace mojo {
namespace edk {
namespace {
struct BootstrapData {
// The node name of the sender.
ports::NodeName node_name;
// The port name of the sender's local bootstrap port.
ports::PortName port_name;
};
} // namespace
// static
void RemoteMessagePipeBootstrap::Create(
NodeController* node_controller,
ScopedPlatformHandle platform_handle,
const ports::PortRef& port) {
CHECK(node_controller);
CHECK(node_controller->io_task_runner());
if (node_controller->io_task_runner()->RunsTasksOnCurrentThread()) {
// Owns itself.
new RemoteMessagePipeBootstrap(
node_controller, std::move(platform_handle), port);
} else {
node_controller->io_task_runner()->PostTask(
FROM_HERE,
base::Bind(&RemoteMessagePipeBootstrap::Create,
base::Unretained(node_controller),
base::Passed(&platform_handle), port));
}
}
RemoteMessagePipeBootstrap::~RemoteMessagePipeBootstrap() {
DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
base::MessageLoop::current()->RemoveDestructionObserver(this);
if (channel_)
channel_->ShutDown();
}
RemoteMessagePipeBootstrap::RemoteMessagePipeBootstrap(
NodeController* node_controller,
ScopedPlatformHandle platform_handle,
const ports::PortRef& port)
: node_controller_(node_controller),
local_port_(port),
io_task_runner_(base::ThreadTaskRunnerHandle::Get()),
channel_(Channel::Create(this, std::move(platform_handle),
io_task_runner_)) {
base::MessageLoop::current()->AddDestructionObserver(this);
channel_->Start();
Channel::MessagePtr message(new Channel::Message(sizeof(BootstrapData), 0));
BootstrapData* data = static_cast<BootstrapData*>(message->mutable_payload());
data->node_name = node_controller_->name();
data->port_name = local_port_.name();
channel_->Write(std::move(message));
}
void RemoteMessagePipeBootstrap::ShutDown() {
DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
if (shutting_down_)
return;
shutting_down_ = true;
// Shut down asynchronously so ShutDown() can be called from within
// OnChannelMessage and OnChannelError.
io_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RemoteMessagePipeBootstrap::ShutDownNow,
base::Unretained(this)));
}
void RemoteMessagePipeBootstrap::WillDestroyCurrentMessageLoop() {
DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
ShutDownNow();
}
void RemoteMessagePipeBootstrap::OnChannelMessage(
const void* payload,
size_t payload_size,
ScopedPlatformHandleVectorPtr handles) {
DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
DCHECK(!handles || !handles->size());
const BootstrapData* data = static_cast<const BootstrapData*>(payload);
if (peer_info_received_) {
// This should only be a confirmation from the other end, which tells us
// it's now safe to shut down.
if (payload_size != 0)
DLOG(ERROR) << "Unexpected message received in message pipe bootstrap.";
ShutDown();
return;
}
if (payload_size != sizeof(BootstrapData)) {
DLOG(ERROR) << "Invalid bootstrap payload.";
ShutDown();
return;
}
peer_info_received_ = true;
// We need to choose one side to initiate the port merge. It doesn't matter
// who does it as long as they don't both try. Simple solution: pick the one
// with the "smaller" port name.
if (local_port_.name() < data->port_name) {
node_controller_->node()->MergePorts(local_port_, data->node_name,
data->port_name);
}
// Send another ping to the other end to trigger shutdown. This may race with
// the other end sending its own ping, but it doesn't matter. Whoever wins
// will cause the other end to tear down, and the ensuing channel error will
// in turn clean up the remaining end.
Channel::MessagePtr message(new Channel::Message(0, 0));
channel_->Write(std::move(message));
}
void RemoteMessagePipeBootstrap::OnChannelError() {
DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
ShutDown();
}
void RemoteMessagePipeBootstrap::ShutDownNow() {
delete this;
}
} // namespace edk
} // namespace mojo