summaryrefslogtreecommitdiffstats
path: root/dom/media/systemservices/MediaUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/systemservices/MediaUtils.cpp')
-rw-r--r--dom/media/systemservices/MediaUtils.cpp126
1 files changed, 126 insertions, 0 deletions
diff --git a/dom/media/systemservices/MediaUtils.cpp b/dom/media/systemservices/MediaUtils.cpp
new file mode 100644
index 0000000000..fad2fd4e2c
--- /dev/null
+++ b/dom/media/systemservices/MediaUtils.cpp
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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 "MediaUtils.h"
+
+#include "mozilla/AppShutdown.h"
+#include "mozilla/Services.h"
+
+namespace mozilla::media {
+
+nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier() {
+ nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
+ if (!svc) {
+ // We can fail to get the shutdown service if we're already shutting down.
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIAsyncShutdownClient> barrier;
+ nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
+ if (!barrier) {
+ // We are probably in a content process. We need to do cleanup at
+ // XPCOM shutdown in leakchecking builds.
+ rv = svc->GetXpcomWillShutdown(getter_AddRefs(barrier));
+ }
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ MOZ_RELEASE_ASSERT(barrier);
+ return barrier;
+}
+
+nsCOMPtr<nsIAsyncShutdownClient> MustGetShutdownBarrier() {
+ nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
+ MOZ_RELEASE_ASSERT(barrier);
+ return barrier;
+}
+
+NS_IMPL_ISUPPORTS(ShutdownBlocker, nsIAsyncShutdownBlocker)
+
+namespace {
+class TicketBlocker : public ShutdownBlocker {
+ using ShutdownMozPromise = ShutdownBlockingTicket::ShutdownMozPromise;
+
+ public:
+ explicit TicketBlocker(const nsAString& aName)
+ : ShutdownBlocker(aName), mPromise(mHolder.Ensure(__func__)) {}
+
+ NS_IMETHOD
+ BlockShutdown(nsIAsyncShutdownClient* aProfileBeforeChange) override {
+ mHolder.Resolve(true, __func__);
+ return NS_OK;
+ }
+
+ void RejectIfExists() { mHolder.RejectIfExists(false, __func__); }
+
+ ShutdownMozPromise* ShutdownPromise() { return mPromise; }
+
+ private:
+ ~TicketBlocker() = default;
+
+ MozPromiseHolder<ShutdownMozPromise> mHolder;
+ const RefPtr<ShutdownMozPromise> mPromise;
+};
+
+class ShutdownBlockingTicketImpl : public ShutdownBlockingTicket {
+ private:
+ RefPtr<TicketBlocker> mBlocker;
+
+ public:
+ explicit ShutdownBlockingTicketImpl(RefPtr<TicketBlocker> aBlocker)
+ : mBlocker(std::move(aBlocker)) {}
+
+ static UniquePtr<ShutdownBlockingTicket> Create(const nsAString& aName,
+ const nsAString& aFileName,
+ int32_t aLineNr) {
+ auto blocker = MakeRefPtr<TicketBlocker>(aName);
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "ShutdownBlockingTicketImpl::AddBlocker",
+ [blocker, file = nsString(aFileName), aLineNr] {
+ MustGetShutdownBarrier()->AddBlocker(blocker, file, aLineNr, u""_ns);
+ }));
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown)) {
+ // Adding a blocker is not guaranteed to succeed. Remove the blocker in
+ // case it succeeded anyway, and bail.
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "ShutdownBlockingTicketImpl::RemoveBlocker", [blocker] {
+ MustGetShutdownBarrier()->RemoveBlocker(blocker);
+ blocker->RejectIfExists();
+ }));
+ return nullptr;
+ }
+
+ // Adding a blocker is now guaranteed to succeed:
+ // - If AppShutdown::IsInOrBeyond(AppShutdown) returned false,
+ // - then the AddBlocker main thread task was queued before AppShutdown's
+ // sCurrentShutdownPhase is set to ShutdownPhase::AppShutdown,
+ // - which is before AppShutdown will drain the (main thread) event queue to
+ // run the AddBlocker task, if not already run,
+ // - which is before profile-before-change (the earliest barrier we'd add a
+ // blocker to, see GetShutdownBarrier()) is notified,
+ // - which is when AsyncShutdown prevents further conditions (blockers)
+ // being added to the profile-before-change barrier.
+ return MakeUnique<ShutdownBlockingTicketImpl>(std::move(blocker));
+ }
+
+ ~ShutdownBlockingTicketImpl() {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
+ NS_NewRunnableFunction(__func__, [blocker = std::move(mBlocker)] {
+ GetShutdownBarrier()->RemoveBlocker(blocker);
+ blocker->RejectIfExists();
+ })));
+ }
+
+ ShutdownMozPromise* ShutdownPromise() override {
+ return mBlocker->ShutdownPromise();
+ }
+};
+} // namespace
+
+UniquePtr<ShutdownBlockingTicket> ShutdownBlockingTicket::Create(
+ const nsAString& aName, const nsAString& aFileName, int32_t aLineNr) {
+ return ShutdownBlockingTicketImpl::Create(aName, aFileName, aLineNr);
+}
+
+} // namespace mozilla::media