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