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/. */
7 #include "MediaShutdownManager.h"
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"
21 extern LazyLogModule gMediaDecoderLog
;
22 #define DECODER_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
23 #define LOGW(...) NS_WARNING(nsPrintfCString(__VA_ARGS__).get())
25 NS_IMPL_ISUPPORTS(MediaShutdownManager
, nsIAsyncShutdownBlocker
)
27 MediaShutdownManager::MediaShutdownManager() {
28 MOZ_ASSERT(NS_IsMainThread());
29 MOZ_DIAGNOSTIC_ASSERT(sInitPhase
== NotInited
);
32 MediaShutdownManager::~MediaShutdownManager() { MOZ_ASSERT(NS_IsMainThread()); }
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
;
38 MediaShutdownManager::InitPhase
MediaShutdownManager::sInitPhase
=
39 MediaShutdownManager::NotInited
;
41 MediaShutdownManager
& MediaShutdownManager::Instance() {
42 MOZ_ASSERT(NS_IsMainThread());
43 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
45 MOZ_CRASH_UNSAFE_PRINTF("sInstance is null. sInitPhase=%d",
52 void MediaShutdownManager::InitStatics() {
53 MOZ_ASSERT(NS_IsMainThread());
54 if (sInitPhase
!= NotInited
) {
58 sInstance
= new MediaShutdownManager();
59 MOZ_DIAGNOSTIC_ASSERT(sInstance
);
61 nsCOMPtr
<nsIAsyncShutdownClient
> barrier
= media::GetShutdownBarrier();
64 LOGW("Failed to get barrier, cannot add shutdown blocker!");
65 sInitPhase
= InitFailed
;
70 barrier
->AddBlocker(sInstance
, NS_LITERAL_STRING_FROM_CSTRING(__FILE__
),
71 __LINE__
, u
"MediaShutdownManager shutdown"_ns
);
73 LOGW("Failed to add shutdown blocker! rv=%x", uint32_t(rv
));
74 sInitPhase
= InitFailed
;
77 sInitPhase
= InitSucceeded
;
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
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
;
96 DECODER_LOG(LogLevel::Debug
, ("MediaShutdownManager::BlockShutdown() end."));
99 nsresult
MediaShutdownManager::Register(MediaDecoder
* aDecoder
) {
100 MOZ_ASSERT(NS_IsMainThread());
101 if (sInitPhase
== InitFailed
) {
102 return NS_ERROR_NOT_INITIALIZED
;
104 if (sInitPhase
== XPCOMShutdownStarted
) {
105 return NS_ERROR_ABORT
;
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);
116 void MediaShutdownManager::Unregister(MediaDecoder
* aDecoder
) {
117 MOZ_ASSERT(NS_IsMainThread());
118 if (!mDecoders
.EnsureRemoved(aDecoder
)) {
121 if (sInitPhase
== XPCOMShutdownStarted
&& mDecoders
.Count() == 0) {
127 MediaShutdownManager::GetName(nsAString
& aName
) {
128 aName
= u
"MediaShutdownManager: shutdown"_ns
;
133 MediaShutdownManager::GetState(nsIPropertyBag
** aBagOut
) {
134 MOZ_ASSERT(NS_IsMainThread());
137 nsCOMPtr
<nsIWritablePropertyBag2
> propertyBag
=
138 do_CreateInstance("@mozilla.org/hash-property-bag;1");
140 if (NS_WARN_IF(!propertyBag
)) {
141 return NS_ERROR_OUT_OF_MEMORY
;
144 nsresult rv
= propertyBag
->SetPropertyAsInt32(
145 u
"sInitPhase"_ns
, static_cast<int32_t>(sInitPhase
));
147 if (NS_WARN_IF(NS_FAILED(rv
))) {
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(", ");
159 rv
= propertyBag
->SetPropertyAsACString(u
"decoderInfo"_ns
, decoderInfo
);
161 if (NS_WARN_IF(NS_FAILED(rv
))) {
165 propertyBag
.forget(aBagOut
);
171 MediaShutdownManager::BlockShutdown(nsIAsyncShutdownClient
*) {
172 MOZ_ASSERT(NS_IsMainThread());
173 MOZ_DIAGNOSTIC_ASSERT(sInitPhase
== InitSucceeded
);
174 MOZ_DIAGNOSTIC_ASSERT(sInstance
);
176 DECODER_LOG(LogLevel::Debug
,
177 ("MediaShutdownManager::BlockShutdown() start..."));
179 // Set this flag to ensure no Register() is allowed when Shutdown() begins.
180 sInitPhase
= XPCOMShutdownStarted
;
182 auto oldCount
= mDecoders
.Count();
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
);
199 } // namespace mozilla
201 // avoid redefined macro in unified build