1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "MediaShutdownManager.h"
8
9 #include "MediaDecoder.h"
10 #include "mozilla/Logging.h"
11 #include "mozilla/media/MediaUtils.h"
12 #include "mozilla/Services.h"
13 #include "mozilla/StaticPtr.h"
14 #include "nsComponentManagerUtils.h"
15 #include "nsIWritablePropertyBag2.h"
16
17 namespace mozilla {
18
19 #undef LOGW
20
21 extern LazyLogModule gMediaDecoderLog;
22 #define DECODER_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
23 #define LOGW(...) NS_WARNING(nsPrintfCString(__VA_ARGS__).get())
24
NS_IMPL_ISUPPORTS(MediaShutdownManager,nsIAsyncShutdownBlocker)25 NS_IMPL_ISUPPORTS(MediaShutdownManager, nsIAsyncShutdownBlocker)
26
27 MediaShutdownManager::MediaShutdownManager() {
28 MOZ_ASSERT(NS_IsMainThread());
29 MOZ_DIAGNOSTIC_ASSERT(sInitPhase == NotInited);
30 }
31
~MediaShutdownManager()32 MediaShutdownManager::~MediaShutdownManager() { MOZ_ASSERT(NS_IsMainThread()); }
33
34 // Note that we don't use ClearOnShutdown() on this StaticRefPtr, as that
35 // may interfere with our shutdown listener.
36 StaticRefPtr<MediaShutdownManager> MediaShutdownManager::sInstance;
37
38 MediaShutdownManager::InitPhase MediaShutdownManager::sInitPhase =
39 MediaShutdownManager::NotInited;
40
Instance()41 MediaShutdownManager& MediaShutdownManager::Instance() {
42 MOZ_ASSERT(NS_IsMainThread());
43 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
44 if (!sInstance) {
45 MOZ_CRASH_UNSAFE_PRINTF("sInstance is null. sInitPhase=%d",
46 int(sInitPhase));
47 }
48 #endif
49 return *sInstance;
50 }
51
InitStatics()52 void MediaShutdownManager::InitStatics() {
53 MOZ_ASSERT(NS_IsMainThread());
54 if (sInitPhase != NotInited) {
55 return;
56 }
57
58 sInstance = new MediaShutdownManager();
59 MOZ_DIAGNOSTIC_ASSERT(sInstance);
60
61 nsCOMPtr<nsIAsyncShutdownClient> barrier = media::GetShutdownBarrier();
62
63 if (!barrier) {
64 LOGW("Failed to get barrier, cannot add shutdown blocker!");
65 sInitPhase = InitFailed;
66 return;
67 }
68
69 nsresult rv =
70 barrier->AddBlocker(sInstance, NS_LITERAL_STRING_FROM_CSTRING(__FILE__),
71 __LINE__, u"MediaShutdownManager shutdown"_ns);
72 if (NS_FAILED(rv)) {
73 LOGW("Failed to add shutdown blocker! rv=%x", uint32_t(rv));
74 sInitPhase = InitFailed;
75 return;
76 }
77 sInitPhase = InitSucceeded;
78 }
79
RemoveBlocker()80 void MediaShutdownManager::RemoveBlocker() {
81 MOZ_ASSERT(NS_IsMainThread());
82 MOZ_DIAGNOSTIC_ASSERT(sInitPhase == XPCOMShutdownStarted);
83 MOZ_ASSERT(mDecoders.Count() == 0);
84 nsCOMPtr<nsIAsyncShutdownClient> barrier = media::GetShutdownBarrier();
85 // xpcom should still be available because we blocked shutdown by having a
86 // blocker. Until it completely shuts down we should still be able to get
87 // the barrier.
88 MOZ_RELEASE_ASSERT(
89 barrier,
90 "Failed to get shutdown barrier, cannot remove shutdown blocker!");
91 barrier->RemoveBlocker(this);
92 // Clear our singleton reference. This will probably delete
93 // this instance, so don't deref |this| clearing sInstance.
94 sInitPhase = XPCOMShutdownEnded;
95 sInstance = nullptr;
96 DECODER_LOG(LogLevel::Debug, ("MediaShutdownManager::BlockShutdown() end."));
97 }
98
Register(MediaDecoder * aDecoder)99 nsresult MediaShutdownManager::Register(MediaDecoder* aDecoder) {
100 MOZ_ASSERT(NS_IsMainThread());
101 if (sInitPhase == InitFailed) {
102 return NS_ERROR_NOT_INITIALIZED;
103 }
104 if (sInitPhase == XPCOMShutdownStarted) {
105 return NS_ERROR_ABORT;
106 }
107 // Don't call Register() after you've Unregistered() all the decoders,
108 // that's not going to work.
109 MOZ_ASSERT(!mDecoders.Contains(aDecoder));
110 mDecoders.Insert(aDecoder);
111 MOZ_ASSERT(mDecoders.Contains(aDecoder));
112 MOZ_ASSERT(mDecoders.Count() > 0);
113 return NS_OK;
114 }
115
Unregister(MediaDecoder * aDecoder)116 void MediaShutdownManager::Unregister(MediaDecoder* aDecoder) {
117 MOZ_ASSERT(NS_IsMainThread());
118 if (!mDecoders.EnsureRemoved(aDecoder)) {
119 return;
120 }
121 if (sInitPhase == XPCOMShutdownStarted && mDecoders.Count() == 0) {
122 RemoveBlocker();
123 }
124 }
125
126 NS_IMETHODIMP
GetName(nsAString & aName)127 MediaShutdownManager::GetName(nsAString& aName) {
128 aName = u"MediaShutdownManager: shutdown"_ns;
129 return NS_OK;
130 }
131
132 NS_IMETHODIMP
GetState(nsIPropertyBag ** aBagOut)133 MediaShutdownManager::GetState(nsIPropertyBag** aBagOut) {
134 MOZ_ASSERT(NS_IsMainThread());
135 MOZ_ASSERT(aBagOut);
136
137 nsCOMPtr<nsIWritablePropertyBag2> propertyBag =
138 do_CreateInstance("@mozilla.org/hash-property-bag;1");
139
140 if (NS_WARN_IF(!propertyBag)) {
141 return NS_ERROR_OUT_OF_MEMORY;
142 }
143
144 nsresult rv = propertyBag->SetPropertyAsInt32(
145 u"sInitPhase"_ns, static_cast<int32_t>(sInitPhase));
146
147 if (NS_WARN_IF(NS_FAILED(rv))) {
148 return rv;
149 }
150
151 nsAutoCString decoderInfo;
152 for (const auto& key : mDecoders) {
153 // Grab the full extended type for the decoder. This can be used to help
154 // indicate problems with specific decoders by associating type -> decoder.
155 decoderInfo.Append(key->ContainerType().ExtendedType().OriginalString());
156 decoderInfo.Append(", ");
157 }
158
159 rv = propertyBag->SetPropertyAsACString(u"decoderInfo"_ns, decoderInfo);
160
161 if (NS_WARN_IF(NS_FAILED(rv))) {
162 return rv;
163 }
164
165 propertyBag.forget(aBagOut);
166
167 return NS_OK;
168 }
169
170 NS_IMETHODIMP
BlockShutdown(nsIAsyncShutdownClient *)171 MediaShutdownManager::BlockShutdown(nsIAsyncShutdownClient*) {
172 MOZ_ASSERT(NS_IsMainThread());
173 MOZ_DIAGNOSTIC_ASSERT(sInitPhase == InitSucceeded);
174 MOZ_DIAGNOSTIC_ASSERT(sInstance);
175
176 DECODER_LOG(LogLevel::Debug,
177 ("MediaShutdownManager::BlockShutdown() start..."));
178
179 // Set this flag to ensure no Register() is allowed when Shutdown() begins.
180 sInitPhase = XPCOMShutdownStarted;
181
182 auto oldCount = mDecoders.Count();
183 if (oldCount == 0) {
184 RemoveBlocker();
185 return NS_OK;
186 }
187
188 // Iterate over the decoders and shut them down.
189 for (const auto& key : mDecoders) {
190 key->NotifyXPCOMShutdown();
191 // Check MediaDecoder::Shutdown doesn't call Unregister() synchronously in
192 // order not to corrupt our hashtable traversal.
193 MOZ_ASSERT(mDecoders.Count() == oldCount);
194 }
195
196 return NS_OK;
197 }
198
199 } // namespace mozilla
200
201 // avoid redefined macro in unified build
202 #undef LOGW
203