summaryrefslogtreecommitdiffstats
path: root/dom/media/systemservices/MediaUtils.cpp
blob: fad2fd4e2cd29b43b92e6853b7e7908a1f045f17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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