mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
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:
parent
4190f9f233
commit
bf5f2935f7
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user