diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp index d8aae4b7593c..52a326a04b1f 100644 --- a/dom/clients/manager/ClientManagerService.cpp +++ b/dom/clients/manager/ClientManagerService.cpp @@ -6,9 +6,13 @@ #include "ClientManagerService.h" +#include "ClientManagerParent.h" #include "ClientSourceParent.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/PBackgroundSharedTypes.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/SystemGroup.h" +#include "nsIAsyncShutdown.h" namespace mozilla { namespace dom { @@ -56,11 +60,107 @@ MatchPrincipalInfo(const PrincipalInfo& aLeft, const PrincipalInfo& aRight) MOZ_CRASH("unexpected principal type!"); } +class ClientShutdownBlocker final : public nsIAsyncShutdownBlocker +{ + RefPtr mPromise; + + ~ClientShutdownBlocker() = default; + +public: + explicit ClientShutdownBlocker(GenericPromise::Private* aPromise) + : mPromise(aPromise) + { + MOZ_DIAGNOSTIC_ASSERT(mPromise); + } + + NS_IMETHOD + GetName(nsAString& aNameOut) override + { + aNameOut = + NS_LITERAL_STRING("ClientManagerService: start destroying IPC actors early"); + return NS_OK; + } + + NS_IMETHOD + BlockShutdown(nsIAsyncShutdownClient* aClient) override + { + mPromise->Resolve(true, __func__); + aClient->RemoveBlocker(this); + return NS_OK; + } + + NS_IMETHOD + GetState(nsIPropertyBag**) override + { + return NS_OK; + } + + NS_DECL_ISUPPORTS +}; + +NS_IMPL_ISUPPORTS(ClientShutdownBlocker, nsIAsyncShutdownBlocker) + +// Helper function the resolves a MozPromise when we detect that the browser +// has begun to shutdown. +RefPtr +OnShutdown() +{ + RefPtr ref = new GenericPromise::Private(__func__); + + nsCOMPtr r = NS_NewRunnableFunction("ClientManagerServer::OnShutdown", + [ref] () { + nsCOMPtr svc = services::GetAsyncShutdown(); + if (!svc) { + ref->Resolve(true, __func__); + return; + } + + nsCOMPtr phase; + MOZ_ALWAYS_SUCCEEDS(svc->GetXpcomWillShutdown(getter_AddRefs(phase))); + if (!phase) { + ref->Resolve(true, __func__); + return; + } + + nsCOMPtr blocker = new ClientShutdownBlocker(ref); + nsresult rv = + phase->AddBlocker(blocker, NS_LITERAL_STRING(__FILE__), __LINE__, + NS_LITERAL_STRING("ClientManagerService shutdown")); + + if (NS_FAILED(rv)) { + ref->Resolve(true, __func__); + return; + } + }); + + MOZ_ALWAYS_SUCCEEDS( + SystemGroup::Dispatch(TaskCategory::Other, r.forget())); + + return Move(ref); +} + } // anonymous namespace ClientManagerService::ClientManagerService() + : mShutdown(false) { AssertIsOnBackgroundThread(); + + RefPtr self = this; + + // While the ClientManagerService will be gracefully terminated as windows + // and workers are naturally killed, this can cause us to do extra work + // relatively late in the shutdown process. To avoid this we eagerly begin + // shutdown at the first sign it has begun. Since we handle normal shutdown + // gracefully we don't really need to block anything here. We just begin + // destroying our IPC actors immediately. + // + // Note. capture the self reference here will keep the service alive until + // shutdown. This is what almost always happens anyway, though. + OnShutdown()->Then(GetCurrentThreadSerialEventTarget(), __func__, + [self] () { + self->Shutdown(); + }); } ClientManagerService::~ClientManagerService() @@ -73,6 +173,24 @@ ClientManagerService::~ClientManagerService() sClientManagerServiceInstance = nullptr; } +void +ClientManagerService::Shutdown() +{ + AssertIsOnBackgroundThread(); + + // Our shutdown promise should really only resolve once. So double-shutdown + // is not expected + MOZ_DIAGNOSTIC_ASSERT(!mShutdown); + mShutdown = true; + + // Begin destroying our various manager actors which will in turn destroy + // all source, handle, and operation actors. + AutoTArray list(mManagerList); + for (auto actor : list) { + Unused << PClientManagerParent::Send__delete__(actor); + } +} + // static already_AddRefed ClientManagerService::GetOrCreateInstance() @@ -142,6 +260,11 @@ ClientManagerService::AddManager(ClientManagerParent* aManager) MOZ_DIAGNOSTIC_ASSERT(aManager); MOZ_ASSERT(!mManagerList.Contains(aManager)); mManagerList.AppendElement(aManager); + + // If shutdown has already begun then immediately destroy the actor. + if (mShutdown) { + Unused << PClientManagerParent::Send__delete__(aManager); + } } void diff --git a/dom/clients/manager/ClientManagerService.h b/dom/clients/manager/ClientManagerService.h index f73fda7275ff..042733e76c29 100644 --- a/dom/clients/manager/ClientManagerService.h +++ b/dom/clients/manager/ClientManagerService.h @@ -7,6 +7,7 @@ #define _mozilla_dom_ClientManagerService_h #include "mozilla/ipc/PBackgroundSharedTypes.h" +#include "mozilla/MozPromise.h" #include "nsDataHashtable.h" namespace mozilla { @@ -27,9 +28,14 @@ class ClientManagerService final nsTArray mManagerList; + bool mShutdown; + ClientManagerService(); ~ClientManagerService(); + void + Shutdown(); + public: static already_AddRefed GetOrCreateInstance();