blob: 445dce41709edbbc36486f4b18f1e11d449b644b [file] [log] [blame]
// Copyright 2019 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/blink/public/common/messaging/message_port_descriptor.h"
#include <utility>
namespace blink {
namespace {
MessagePortDescriptor::InstrumentationDelegate* g_instrumentation_delegate =
nullptr;
} // namespace
// static
const size_t MessagePortDescriptor::kInvalidSequenceNumber;
// static
const size_t MessagePortDescriptor::kFirstValidSequenceNumber;
// static
void MessagePortDescriptor::SetInstrumentationDelegate(
InstrumentationDelegate* delegate) {
// There should only ever be one delegate, and this only should toggle from
// being set to not being set and vice-versa. The toggling only ever occurs
// during tests; in production a single instrumentation delegate is installed
// early during Blink startup and left in place forever afterwards.
DCHECK(!delegate ^ !g_instrumentation_delegate);
g_instrumentation_delegate = delegate;
}
MessagePortDescriptor::MessagePortDescriptor() = default;
MessagePortDescriptor::MessagePortDescriptor(
MessagePortDescriptor&& message_port)
: handle_(std::move(message_port.handle_)),
#if DCHECK_IS_ON()
raw_handle_(
std::exchange(message_port.raw_handle_, mojo::MessagePipeHandle())),
#endif
id_(std::exchange(message_port.id_, base::UnguessableToken::Null())),
sequence_number_(std::exchange(message_port.sequence_number_,
kInvalidSequenceNumber)) {
#if DCHECK_IS_ON()
DCHECK_EQ(raw_handle_.value(), handle_.get().value());
#endif
}
MessagePortDescriptor::~MessagePortDescriptor() {
Reset();
}
MessagePortDescriptor& MessagePortDescriptor::operator=(
MessagePortDescriptor&& message_port) {
Reset();
handle_ = std::move(message_port.handle_);
#if DCHECK_IS_ON()
raw_handle_ =
std::exchange(message_port.raw_handle_, mojo::MessagePipeHandle());
DCHECK_EQ(raw_handle_.value(), handle_.get().value());
#endif
id_ = std::exchange(message_port.id_, base::UnguessableToken::Null());
sequence_number_ =
std::exchange(message_port.sequence_number_, kInvalidSequenceNumber);
return *this;
}
bool MessagePortDescriptor::IsValid() const {
// |handle_| can be valid or invalid, depending on if we're entangled or
// not. But everything else should be consistent.
#if DCHECK_IS_ON()
DCHECK_EQ(id_.is_empty(), !raw_handle_.is_valid());
DCHECK_EQ(sequence_number_ == kInvalidSequenceNumber,
!raw_handle_.is_valid());
return raw_handle_.is_valid();
#else
DCHECK_EQ(id_.is_empty(), sequence_number_ == kInvalidSequenceNumber);
return !id_.is_empty() && sequence_number_ != kInvalidSequenceNumber;
#endif
}
bool MessagePortDescriptor::IsEntangled() const {
// This descriptor is entangled if it's valid, but its handle has been loaned
// out.
return IsValid() && !handle_.is_valid();
}
bool MessagePortDescriptor::IsDefault() const {
if (IsValid())
return false;
// This is almost the converse of IsValid, except that we additionally expect
// the |handle_| to be empty as well (which IsValid doesn't verify).
DCHECK(!handle_.is_valid());
return true;
}
void MessagePortDescriptor::Reset() {
if (IsValid()) {
// Call NotifyDestroyed before clearing members, as the notification needs
// to access them.
NotifyDestroyed();
// Ensure that MessagePipeDescriptor-wrapped handles are fully accounted for
// over their entire lifetime.
DCHECK(handle_.is_valid());
#if DCHECK_IS_ON()
DCHECK(raw_handle_.is_valid());
DCHECK_EQ(raw_handle_.value(), handle_.get().value());
raw_handle_ = mojo::MessagePipeHandle();
#endif
handle_.reset();
id_ = base::UnguessableToken::Null();
sequence_number_ = kInvalidSequenceNumber;
}
}
void MessagePortDescriptor::Init(mojo::ScopedMessagePipeHandle handle,
base::UnguessableToken id,
uint64_t sequence_number) {
// Init is only called by deserialization code and thus should only be called
// on a default initialized descriptor.
DCHECK(IsDefault());
handle_ = std::move(handle);
#if DCHECK_IS_ON()
raw_handle_ = handle_.get();
#endif
id_ = id;
sequence_number_ = sequence_number;
// Init should only create a valid not-entangled descriptor, or a default
// descriptor.
DCHECK((IsValid() && !IsEntangled()) || IsDefault());
}
mojo::ScopedMessagePipeHandle MessagePortDescriptor::TakeHandle() {
DCHECK(handle_.is_valid());
#if DCHECK_IS_ON()
DCHECK(raw_handle_.is_valid());
DCHECK_EQ(raw_handle_.value(), handle_.get().value());
raw_handle_ = mojo::MessagePipeHandle();
#endif
return std::move(handle_);
}
base::UnguessableToken MessagePortDescriptor::TakeId() {
DCHECK(!id_.is_empty());
return std::exchange(id_, base::UnguessableToken::Null());
}
uint64_t MessagePortDescriptor::TakeSequenceNumber() {
DCHECK_NE(kInvalidSequenceNumber, sequence_number_);
return std::exchange(sequence_number_, kInvalidSequenceNumber);
}
mojo::ScopedMessagePipeHandle MessagePortDescriptor::TakeHandleToEntangle(
const base::UnguessableToken& execution_context_id) {
DCHECK(handle_.is_valid());
NotifyAttached(execution_context_id);
// Do not use TakeHandle, because it also resets |raw_handle_|. In DCHECK
// builds we use |raw_handle_| to ensure that the same handle is given back to
// us via "GiveDisentangledHandle".
return std::move(handle_);
}
void MessagePortDescriptor::GiveDisentangledHandle(
mojo::ScopedMessagePipeHandle handle) {
// We should only ever be given back the same handle that was taken from us.
DCHECK(!handle_.is_valid());
#if DCHECK_IS_ON()
DCHECK_EQ(raw_handle_.value(), handle.get().value());
#endif
handle_ = std::move(handle);
NotifyDetached();
}
MessagePortDescriptor::MessagePortDescriptor(
mojo::ScopedMessagePipeHandle handle)
: handle_(std::move(handle)),
#if DCHECK_IS_ON()
raw_handle_(handle_.get()),
#endif
id_(base::UnguessableToken::Create()),
sequence_number_(kFirstValidSequenceNumber) {
}
void MessagePortDescriptor::NotifyAttached(
const base::UnguessableToken& execution_context_id) {
DCHECK(!id_.is_empty());
if (g_instrumentation_delegate) {
g_instrumentation_delegate->NotifyMessagePortAttached(
id_, sequence_number_++, execution_context_id);
}
}
void MessagePortDescriptor::NotifyDetached() {
DCHECK(!id_.is_empty());
if (g_instrumentation_delegate) {
g_instrumentation_delegate->NotifyMessagePortDetached(id_,
sequence_number_++);
}
}
void MessagePortDescriptor::NotifyDestroyed() {
DCHECK(!id_.is_empty());
if (g_instrumentation_delegate) {
g_instrumentation_delegate->NotifyMessagePortDestroyed(id_,
sequence_number_++);
}
}
MessagePortDescriptorPair::MessagePortDescriptorPair() {
mojo::MessagePipe pipe;
port0_ = MessagePortDescriptor(std::move(pipe.handle0));
port1_ = MessagePortDescriptor(std::move(pipe.handle1));
// Notify the instrumentation that these ports are newly created and peers of
// each other.
if (g_instrumentation_delegate)
g_instrumentation_delegate->NotifyMessagePortPairCreated(*this);
}
MessagePortDescriptorPair::~MessagePortDescriptorPair() = default;
} // namespace blink