Bug 1568597 - RemoteWorkerManager::SelectTargetActorForSharedWorker should select an actor that is kept alive. r=asuth

Differential Revision: https://phabricator.services.mozilla.com/D88412
This commit is contained in:
Luca Greco 2020-08-28 18:11:22 +00:00
parent 4190f9f233
commit bf5f2935f7
5 changed files with 117 additions and 89 deletions

View File

@ -272,7 +272,7 @@ void RemoteWorkerManager::RegisterActor(RemoteWorkerServiceParent* aActor) {
mChildActors.AppendElement(aActor); mChildActors.AppendElement(aActor);
if (!mPendings.IsEmpty()) { if (!mPendings.IsEmpty()) {
const auto& remoteType = GetRemoteTypeForActor(aActor); const auto& processRemoteType = aActor->GetRemoteType();
nsTArray<Pending> unlaunched; nsTArray<Pending> unlaunched;
// Flush pending launching. // Flush pending launching.
@ -283,7 +283,7 @@ void RemoteWorkerManager::RegisterActor(RemoteWorkerServiceParent* aActor) {
const auto& workerRemoteType = p.mData.remoteType(); const auto& workerRemoteType = p.mData.remoteType();
if (MatchRemoteType(remoteType, workerRemoteType)) { if (MatchRemoteType(processRemoteType, workerRemoteType)) {
LOG(("RegisterActor - Launch Pending, workerRemoteType=%s", LOG(("RegisterActor - Launch Pending, workerRemoteType=%s",
workerRemoteType.get())); workerRemoteType.get()));
LaunchInternal(p.mController, aActor, p.mData); LaunchInternal(p.mController, aActor, p.mData);
@ -346,11 +346,13 @@ void RemoteWorkerManager::Launch(RemoteWorkerController* aController,
} }
/** /**
* If a target actor for a Service Worker has been selected, the actor has * If a target actor for the remote worker has been selected, the actor has
* already been registered with the corresponding ContentParent (see * already been registered with the corresponding `ContentParent` and we
* `SelectTargetActorForServiceWorker()`). * should not increment the `mRemoteWorkerActorData`'s `mCount` again (see
* `SelectTargetActorForServiceWorker()` /
* `SelectTargetActorForSharedWorker()`).
*/ */
LaunchInternal(aController, targetActor, aData, IsServiceWorker(aData)); LaunchInternal(aController, targetActor, aData, true);
} }
void RemoteWorkerManager::LaunchInternal( void RemoteWorkerManager::LaunchInternal(
@ -388,34 +390,26 @@ void RemoteWorkerManager::AsyncCreationFailed(
NS_DispatchToCurrentThread(r.forget()); NS_DispatchToCurrentThread(r.forget());
} }
/* static */
nsCString RemoteWorkerManager::GetRemoteTypeForActor(
const RemoteWorkerServiceParent* aActor) {
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
MOZ_ASSERT(aActor);
RefPtr<ContentParent> contentParent =
BackgroundParent::GetContentParent(aActor->Manager());
auto scopeExit =
MakeScopeExit([&] { NS_ReleaseOnMainThread(contentParent.forget()); });
if (NS_WARN_IF(!contentParent)) {
return EmptyCString();
}
nsCString aRemoteType(contentParent->GetRemoteType());
return aRemoteType;
}
template <typename Callback> template <typename Callback>
void RemoteWorkerManager::ForEachActor(Callback&& aCallback) const { void RemoteWorkerManager::ForEachActor(
Callback&& aCallback, const nsACString& aRemoteType,
Maybe<base::ProcessId> aProcessId) const {
AssertIsOnBackgroundThread(); AssertIsOnBackgroundThread();
const auto length = mChildActors.Length(); const auto length = mChildActors.Length();
const auto end = static_cast<uint32_t>(rand()) % length;
auto end = static_cast<uint32_t>(rand()) % length;
if (aProcessId) {
// Start from the actor with the given processId instead of starting from
// a random index.
for (auto j = length - 1; j > 0; j--) {
if (mChildActors[j]->OtherPid() == *aProcessId) {
end = j;
break;
}
}
}
uint32_t i = end; uint32_t i = end;
nsTArray<RefPtr<ContentParent>> proxyReleaseArray; nsTArray<RefPtr<ContentParent>> proxyReleaseArray;
@ -424,14 +418,16 @@ void RemoteWorkerManager::ForEachActor(Callback&& aCallback) const {
MOZ_ASSERT(i < mChildActors.Length()); MOZ_ASSERT(i < mChildActors.Length());
RemoteWorkerServiceParent* actor = mChildActors[i]; RemoteWorkerServiceParent* actor = mChildActors[i];
RefPtr<ContentParent> contentParent = if (MatchRemoteType(actor->GetRemoteType(), aRemoteType)) {
BackgroundParent::GetContentParent(actor->Manager()); RefPtr<ContentParent> contentParent =
BackgroundParent::GetContentParent(actor->Manager());
auto scopeExit = MakeScopeExit( auto scopeExit = MakeScopeExit(
[&]() { proxyReleaseArray.AppendElement(std::move(contentParent)); }); [&]() { proxyReleaseArray.AppendElement(std::move(contentParent)); });
if (!aCallback(actor, std::move(contentParent))) { if (!aCallback(actor, std::move(contentParent))) {
break; break;
}
} }
i = (i + 1) % length; i = (i + 1) % length;
@ -475,41 +471,55 @@ RemoteWorkerManager::SelectTargetActorForServiceWorker(
const auto& workerRemoteType = aData.remoteType(); const auto& workerRemoteType = aData.remoteType();
ForEachActor([&](RemoteWorkerServiceParent* aActor, ForEachActor(
RefPtr<ContentParent>&& aContentParent) { [&](RemoteWorkerServiceParent* aActor,
const auto& remoteType = aContentParent->GetRemoteType(); RefPtr<ContentParent>&& aContentParent) {
auto lock = aContentParent->mRemoteWorkerActorData.Lock();
if (MatchRemoteType(remoteType, workerRemoteType)) { // Select the first actor that matches the remoteType and it is not
auto lock = aContentParent->mRemoteWorkerActorData.Lock(); // already shutting down.
if (lock->mCount || !lock->mShutdownStarted) {
++lock->mCount;
if (lock->mCount || !lock->mShutdownStarted) { // This won't cause any race conditions because the content process
++lock->mCount; // should wait for the permissions to be received before executing the
// Service Worker.
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [contentParent = std::move(aContentParent),
principalInfo = aData.principalInfo()] {
TransmitPermissionsAndBlobURLsForPrincipalInfo(contentParent,
principalInfo);
});
// This won't cause any race conditions because the content process MOZ_ALWAYS_SUCCEEDS(
// should wait for the permissions to be received before executing the SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
// Service Worker.
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [contentParent = std::move(aContentParent),
principalInfo = aData.principalInfo()] {
TransmitPermissionsAndBlobURLsForPrincipalInfo(contentParent,
principalInfo);
});
MOZ_ALWAYS_SUCCEEDS( actor = aActor;
SchedulerGroup::Dispatch(TaskCategory::Other, r.forget())); return false;
}
actor = aActor; MOZ_ASSERT(!actor);
return false; return true;
} },
} workerRemoteType);
MOZ_ASSERT(!actor);
return true;
});
return actor; return actor;
} }
/**
* When Fission is enabled, Shared Workers may have to be spawned into different
* child process from the one where it has been registered from, and that child
* process may be going to be marked as dead and shutdown.
*
* To make sure to keep the selected child process alive we can used the same
* strategy that is being used by
* RemoteWorkerManager::SelectTargetActorForServiceWorker for very similar
* reasons and described in more detail in the inline comment right above that
* method (in short here on the background thread, while
* `ContentParent::mRemoteWorkerActorData` is locked, if `mCount` > 0, we can
* register the remote worker actor "early" and guarantee that the corresponding
* content process will not shutdown).
*/
RemoteWorkerServiceParent* RemoteWorkerServiceParent*
RemoteWorkerManager::SelectTargetActorForSharedWorker( RemoteWorkerManager::SelectTargetActorForSharedWorker(
base::ProcessId aProcessId, const RemoteWorkerData& aData) const { base::ProcessId aProcessId, const RemoteWorkerData& aData) const {
@ -518,26 +528,31 @@ RemoteWorkerManager::SelectTargetActorForSharedWorker(
RemoteWorkerServiceParent* actor = nullptr; RemoteWorkerServiceParent* actor = nullptr;
ForEachActor([&](RemoteWorkerServiceParent* aActor, const auto& workerRemoteType = aData.remoteType();
RefPtr<ContentParent>&& aContentParent) {
bool matchRemoteType =
MatchRemoteType(aContentParent->GetRemoteType(), aData.remoteType());
if (!matchRemoteType) { ForEachActor(
return true; [&](RemoteWorkerServiceParent* aActor,
} RefPtr<ContentParent>&& aContentParent) {
// Make sure to choose an actor related to a child process that is not
// going to shutdown while we are still in the process of launching the
// remote worker.
//
// ForEachActor will start from the child actor coming from the child
// process with a pid equal to aProcessId if any, otherwise it would
// start from a random actor in the mChildActors array, this guarantees
// that we will choose that actor if it does also match the remote type.
auto lock = aContentParent->mRemoteWorkerActorData.Lock();
if ((lock->mCount || !lock->mShutdownStarted) &&
(aActor->OtherPid() == aProcessId || !actor)) {
++lock->mCount;
actor = aActor;
return false;
}
if (aActor->OtherPid() == aProcessId) { MOZ_ASSERT(!actor);
actor = aActor; return true;
return false; },
} workerRemoteType, Some(aProcessId));
if (!actor) {
actor = aActor;
}
return true;
});
return actor; return actor;
} }

View File

@ -75,11 +75,10 @@ class RemoteWorkerManager final {
void AsyncCreationFailed(RemoteWorkerController* aController); void AsyncCreationFailed(RemoteWorkerController* aController);
static nsCString GetRemoteTypeForActor( // Iterate through all RemoteWorkerServiceParent actors with the given
const RemoteWorkerServiceParent* aActor); // remoteType, starting from the actor related to a child process with pid
// aProcessId if needed and available or from a random index otherwise (as if
// Iterate through all RemoteWorkerServiceParent actors, starting from a // iterating through a circular array).
// random index (as if iterating through a circular array).
// //
// aCallback should be a invokable object with a function signature of // aCallback should be a invokable object with a function signature of
// bool (RemoteWorkerServiceParent*, RefPtr<ContentParent>&&) // bool (RemoteWorkerServiceParent*, RefPtr<ContentParent>&&)
@ -91,7 +90,8 @@ class RemoteWorkerManager final {
// doesn't need to worry about proxy-releasing the ContentParent if it isn't // doesn't need to worry about proxy-releasing the ContentParent if it isn't
// moved out of the parameter. // moved out of the parameter.
template <typename Callback> template <typename Callback>
void ForEachActor(Callback&& aCallback) const; void ForEachActor(Callback&& aCallback, const nsACString& aRemoteType,
Maybe<base::ProcessId> aProcessId = Nothing()) const;
// The list of existing RemoteWorkerServiceParent actors for child processes. // The list of existing RemoteWorkerServiceParent actors for child processes.
// Raw pointers because RemoteWorkerServiceParent actors unregister themselves // Raw pointers because RemoteWorkerServiceParent actors unregister themselves

View File

@ -18,8 +18,9 @@ RemoteWorkerServiceParent::RemoteWorkerServiceParent()
RemoteWorkerServiceParent::~RemoteWorkerServiceParent() = default; RemoteWorkerServiceParent::~RemoteWorkerServiceParent() = default;
void RemoteWorkerServiceParent::Initialize() { void RemoteWorkerServiceParent::Initialize(const nsACString& aRemoteType) {
AssertIsOnBackgroundThread(); AssertIsOnBackgroundThread();
mRemoteType = aRemoteType;
mManager->RegisterActor(this); mManager->RegisterActor(this);
} }

View File

@ -8,6 +8,7 @@
#define mozilla_dom_RemoteWorkerServiceParent_h #define mozilla_dom_RemoteWorkerServiceParent_h
#include "mozilla/dom/PRemoteWorkerServiceParent.h" #include "mozilla/dom/PRemoteWorkerServiceParent.h"
#include "mozilla/dom/RemoteType.h"
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
@ -21,10 +22,13 @@ class RemoteWorkerServiceParent final : public PRemoteWorkerServiceParent {
void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override; void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override;
void Initialize(); void Initialize(const nsACString& aRemoteType);
nsCString GetRemoteType() const { return mRemoteType; }
private: private:
RefPtr<RemoteWorkerManager> mManager; RefPtr<RemoteWorkerManager> mManager;
nsCString mRemoteType = NOT_REMOTE_TYPE;
}; };
} // namespace dom } // namespace dom

View File

@ -523,7 +523,15 @@ IPCResult BackgroundParentImpl::RecvPRemoteWorkerServiceConstructor(
PRemoteWorkerServiceParent* aActor) { PRemoteWorkerServiceParent* aActor) {
mozilla::dom::RemoteWorkerServiceParent* actor = mozilla::dom::RemoteWorkerServiceParent* actor =
static_cast<mozilla::dom::RemoteWorkerServiceParent*>(aActor); static_cast<mozilla::dom::RemoteWorkerServiceParent*>(aActor);
actor->Initialize();
RefPtr<ContentParent> parent = BackgroundParent::GetContentParent(this);
// If the ContentParent is null we are dealing with a same-process actor.
if (!parent) {
actor->Initialize(NOT_REMOTE_TYPE);
} else {
actor->Initialize(parent->GetRemoteType());
NS_ReleaseOnMainThread("ContentParent release", parent.forget());
}
return IPC_OK(); return IPC_OK();
} }