mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 14:55:47 +00:00
8d97cd8228
This patch adds support for ManagedEndpoint instances to be dropped & gracefully destroyed. Before this change, a ManagedEndpoint which was dropped without being bound would not clean up its' peer actor, meaning that messages to and from that actor would be discarded. This is done by adding a new actor destroy reason for dropping a ManagedEndpoint. Differential Revision: https://phabricator.services.mozilla.com/D128776
149 lines
5.4 KiB
C++
149 lines
5.4 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=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 "mozilla/ipc/Endpoint.h"
|
|
#include "chrome/common/ipc_message.h"
|
|
#include "mozilla/ipc/IPDLParamTraits.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "mozilla/ipc/ProtocolMessageUtils.h"
|
|
|
|
namespace mozilla::ipc {
|
|
|
|
UntypedManagedEndpoint::UntypedManagedEndpoint(IProtocol* aActor)
|
|
: mInner(Some(Inner{
|
|
/* mOtherSide */ aActor->GetWeakLifecycleProxy(),
|
|
/* mToplevel */ nullptr,
|
|
aActor->Id(),
|
|
aActor->GetProtocolId(),
|
|
aActor->Manager()->Id(),
|
|
aActor->Manager()->GetProtocolId(),
|
|
})) {}
|
|
|
|
UntypedManagedEndpoint::~UntypedManagedEndpoint() {
|
|
if (!IsValid()) {
|
|
return;
|
|
}
|
|
|
|
if (mInner->mOtherSide) {
|
|
// If this ManagedEndpoint was never sent over IPC, deliver a fake
|
|
// MANAGED_ENDPOINT_DROPPED_MESSAGE_TYPE message directly to the other side
|
|
// actor.
|
|
mInner->mOtherSide->ActorEventTarget()->Dispatch(NS_NewRunnableFunction(
|
|
"~ManagedEndpoint (Local)",
|
|
[otherSide = mInner->mOtherSide, id = mInner->mId] {
|
|
if (IProtocol* actor = otherSide->Get(); actor && actor->CanRecv()) {
|
|
MOZ_DIAGNOSTIC_ASSERT(actor->Id() == id, "Wrong Actor?");
|
|
RefPtr<ActorLifecycleProxy> strongProxy(actor->GetLifecycleProxy());
|
|
strongProxy->Get()->OnMessageReceived(
|
|
IPC::Message(id, MANAGED_ENDPOINT_DROPPED_MESSAGE_TYPE));
|
|
}
|
|
}));
|
|
} else if (mInner->mToplevel) {
|
|
// If it was sent over IPC, we'll need to send the message to the sending
|
|
// side. Let's send the message async.
|
|
mInner->mToplevel->ActorEventTarget()->Dispatch(NS_NewRunnableFunction(
|
|
"~ManagedEndpoint (Remote)",
|
|
[toplevel = mInner->mToplevel, id = mInner->mId] {
|
|
if (IProtocol* actor = toplevel->Get();
|
|
actor && actor->CanSend() && actor->GetIPCChannel()) {
|
|
actor->GetIPCChannel()->Send(MakeUnique<IPC::Message>(
|
|
id, MANAGED_ENDPOINT_DROPPED_MESSAGE_TYPE));
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
bool UntypedManagedEndpoint::BindCommon(IProtocol* aActor,
|
|
IProtocol* aManager) {
|
|
MOZ_ASSERT(aManager);
|
|
if (!mInner) {
|
|
NS_WARNING("Cannot bind to invalid endpoint");
|
|
return false;
|
|
}
|
|
|
|
// Perform thread assertions.
|
|
if (mInner->mToplevel) {
|
|
MOZ_DIAGNOSTIC_ASSERT(
|
|
mInner->mToplevel->ActorEventTarget()->IsOnCurrentThread());
|
|
MOZ_DIAGNOSTIC_ASSERT(aManager->ToplevelProtocol() ==
|
|
mInner->mToplevel->Get());
|
|
}
|
|
|
|
if (NS_WARN_IF(aManager->Id() != mInner->mManagerId) ||
|
|
NS_WARN_IF(aManager->GetProtocolId() != mInner->mManagerType) ||
|
|
NS_WARN_IF(aActor->GetProtocolId() != mInner->mType)) {
|
|
MOZ_ASSERT_UNREACHABLE("Actor and manager do not match Endpoint");
|
|
return false;
|
|
}
|
|
|
|
if (!aManager->CanSend() || !aManager->GetIPCChannel()) {
|
|
NS_WARNING("Manager cannot send");
|
|
return false;
|
|
}
|
|
|
|
int32_t id = mInner->mId;
|
|
mInner.reset();
|
|
|
|
// Our typed caller will insert the actor into the managed container.
|
|
aActor->SetManagerAndRegister(aManager, id);
|
|
|
|
aManager->GetIPCChannel()->Send(
|
|
MakeUnique<IPC::Message>(id, MANAGED_ENDPOINT_BOUND_MESSAGE_TYPE));
|
|
return true;
|
|
}
|
|
|
|
/* static */
|
|
void IPDLParamTraits<UntypedManagedEndpoint>::Write(IPC::Message* aMsg,
|
|
IProtocol* aActor,
|
|
paramType&& aParam) {
|
|
bool isValid = aParam.mInner.isSome();
|
|
WriteIPDLParam(aMsg, aActor, isValid);
|
|
if (!isValid) {
|
|
return;
|
|
}
|
|
|
|
auto inner = std::move(*aParam.mInner);
|
|
aParam.mInner.reset();
|
|
|
|
MOZ_RELEASE_ASSERT(inner.mOtherSide, "Has not been sent over IPC yet");
|
|
MOZ_RELEASE_ASSERT(inner.mOtherSide->ActorEventTarget()->IsOnCurrentThread(),
|
|
"Must be being sent from the correct thread");
|
|
MOZ_RELEASE_ASSERT(
|
|
inner.mOtherSide->Get() && inner.mOtherSide->Get()->ToplevelProtocol() ==
|
|
aActor->ToplevelProtocol(),
|
|
"Must be being sent over the same toplevel protocol");
|
|
|
|
WriteIPDLParam(aMsg, aActor, inner.mId);
|
|
WriteIPDLParam(aMsg, aActor, inner.mType);
|
|
WriteIPDLParam(aMsg, aActor, inner.mManagerId);
|
|
WriteIPDLParam(aMsg, aActor, inner.mManagerType);
|
|
}
|
|
|
|
/* static */
|
|
bool IPDLParamTraits<UntypedManagedEndpoint>::Read(const IPC::Message* aMsg,
|
|
PickleIterator* aIter,
|
|
IProtocol* aActor,
|
|
paramType* aResult) {
|
|
*aResult = UntypedManagedEndpoint{};
|
|
bool isValid = false;
|
|
if (!aActor || !ReadIPDLParam(aMsg, aIter, aActor, &isValid)) {
|
|
return false;
|
|
}
|
|
if (!isValid) {
|
|
return true;
|
|
}
|
|
|
|
aResult->mInner.emplace();
|
|
auto& inner = *aResult->mInner;
|
|
inner.mToplevel = aActor->ToplevelProtocol()->GetWeakLifecycleProxy();
|
|
return ReadIPDLParam(aMsg, aIter, aActor, &inner.mId) &&
|
|
ReadIPDLParam(aMsg, aIter, aActor, &inner.mType) &&
|
|
ReadIPDLParam(aMsg, aIter, aActor, &inner.mManagerId) &&
|
|
ReadIPDLParam(aMsg, aIter, aActor, &inner.mManagerType);
|
|
}
|
|
|
|
} // namespace mozilla::ipc
|