blob: 1160d738abc9063439e5837548e76501ace0b6e2 [file] [log] [blame]
// Copyright 2013 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/system/channel.h"
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/stringprintf.h"
namespace mojo {
namespace system {
COMPILE_ASSERT(Channel::kBootstrapEndpointId !=
MessageInTransit::kInvalidEndpointId,
kBootstrapEndpointId_is_invalid);
STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::EndpointId
Channel::kBootstrapEndpointId;
Channel::EndpointInfo::EndpointInfo() {
}
Channel::EndpointInfo::EndpointInfo(scoped_refptr<MessagePipe> message_pipe,
unsigned port)
: message_pipe(message_pipe),
port(port) {
}
Channel::EndpointInfo::~EndpointInfo() {
}
Channel::Channel()
: next_local_id_(kBootstrapEndpointId) {
}
bool Channel::Init(const PlatformChannelHandle& handle) {
DCHECK(creation_thread_checker_.CalledOnValidThread());
// No need to take |lock_|, since this must be called before this object
// becomes thread-safe.
DCHECK(!raw_channel_.get());
raw_channel_.reset(
RawChannel::Create(handle, this, base::MessageLoop::current()));
if (!raw_channel_->Init()) {
raw_channel_.reset();
return false;
}
return true;
}
void Channel::Shutdown() {
DCHECK(creation_thread_checker_.CalledOnValidThread());
base::AutoLock locker(lock_);
DCHECK(raw_channel_.get());
raw_channel_->Shutdown();
raw_channel_.reset();
// TODO(vtl): Should I clear |local_id_to_endpoint_info_map_|? Or assert that
// it's empty?
}
MessageInTransit::EndpointId Channel::AttachMessagePipeEndpoint(
scoped_refptr<MessagePipe> message_pipe, unsigned port) {
MessageInTransit::EndpointId local_id;
{
base::AutoLock locker(lock_);
while (next_local_id_ == MessageInTransit::kInvalidEndpointId ||
local_id_to_endpoint_info_map_.find(next_local_id_) !=
local_id_to_endpoint_info_map_.end())
next_local_id_++;
local_id = next_local_id_;
next_local_id_++;
// TODO(vtl): Use emplace when we move to C++11 unordered_maps. (It'll avoid
// some expensive reference count increment/decrements.) Once this is done,
// we should be able to delete |EndpointInfo|'s default constructor.
local_id_to_endpoint_info_map_[local_id] = EndpointInfo(message_pipe, port);
}
message_pipe->Attach(port, scoped_refptr<Channel>(this), local_id);
return local_id;
}
void Channel::RunMessagePipeEndpoint(MessageInTransit::EndpointId local_id,
MessageInTransit::EndpointId remote_id) {
EndpointInfo endpoint_info;
{
base::AutoLock locker(lock_);
IdToEndpointInfoMap::const_iterator it =
local_id_to_endpoint_info_map_.find(local_id);
CHECK(it != local_id_to_endpoint_info_map_.end());
endpoint_info = it->second;
}
endpoint_info.message_pipe->Run(endpoint_info.port, remote_id);
}
bool Channel::WriteMessage(MessageInTransit* message) {
base::AutoLock locker(lock_);
if (!raw_channel_.get()) {
// TODO(vtl): I think this is probably not an error condition, but I should
// think about it (and the shutdown sequence) more carefully.
LOG(INFO) << "WriteMessage() after shutdown";
return false;
}
return raw_channel_->WriteMessage(message);
}
void Channel::DetachMessagePipeEndpoint(MessageInTransit::EndpointId local_id) {
DCHECK_NE(local_id, MessageInTransit::kInvalidEndpointId);
base::AutoLock locker_(lock_);
local_id_to_endpoint_info_map_.erase(local_id);
}
Channel::~Channel() {
// The channel should have been shut down first.
DCHECK(!raw_channel_.get());
}
void Channel::OnReadMessage(const MessageInTransit& message) {
switch (message.type()) {
case MessageInTransit::kTypeMessagePipeEndpoint:
case MessageInTransit::kTypeMessagePipe:
OnReadMessageForDownstream(message);
break;
case MessageInTransit::TYPE_CHANNEL:
OnReadMessageForChannel(message);
break;
default:
HandleRemoteError(base::StringPrintf(
"Received message of invalid type %u",
static_cast<unsigned>(message.type())));
break;
}
}
void Channel::OnFatalError(FatalError fatal_error) {
// TODO(vtl): IMPORTANT. Notify all our endpoints that they're dead.
NOTIMPLEMENTED();
}
void Channel::OnReadMessageForDownstream(const MessageInTransit& message) {
DCHECK(message.type() == MessageInTransit::kTypeMessagePipeEndpoint ||
message.type() == MessageInTransit::kTypeMessagePipe);
MessageInTransit::EndpointId local_id = message.destination_id();
if (local_id == MessageInTransit::kInvalidEndpointId) {
HandleRemoteError("Received message with no destination ID");
return;
}
EndpointInfo endpoint_info;
{
base::AutoLock locker(lock_);
// Since we own |raw_channel_|, and this method and |Shutdown()| should only
// be called from the creation thread, |raw_channel_| should never be null
// here.
DCHECK(raw_channel_.get());
IdToEndpointInfoMap::const_iterator it =
local_id_to_endpoint_info_map_.find(local_id);
if (it == local_id_to_endpoint_info_map_.end()) {
HandleRemoteError(base::StringPrintf(
"Received a message for nonexistent local destination ID %u",
static_cast<unsigned>(local_id)));
return;
}
endpoint_info = it->second;
}
// We need to duplicate the message, because |EnqueueMessage()| will take
// ownership of it.
MessageInTransit* own_message = MessageInTransit::Create(
message.type(), message.subtype(), message.data(), message.data_size());
if (endpoint_info.message_pipe->EnqueueMessage(
MessagePipe::GetPeerPort(endpoint_info.port), own_message, NULL) !=
MOJO_RESULT_OK) {
HandleLocalError(base::StringPrintf(
"Failed to enqueue message to local destination ID %u",
static_cast<unsigned>(local_id)));
return;
}
}
void Channel::OnReadMessageForChannel(const MessageInTransit& message) {
// TODO(vtl): Currently no channel-only messages yet.
HandleRemoteError("Received invalid channel message");
NOTREACHED();
}
void Channel::HandleRemoteError(const base::StringPiece& error_message) {
// TODO(vtl): Is this how we really want to handle this?
LOG(INFO) << error_message;
}
void Channel::HandleLocalError(const base::StringPiece& error_message) {
// TODO(vtl): Is this how we really want to handle this?
LOG(FATAL) << error_message;
}
} // namespace system
} // namespace mojo