MediaShutdownManager.cpp (6075B)
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/Services.h" 12 #include "mozilla/StaticPtr.h" 13 #include "mozilla/media/MediaUtils.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 25 NS_IMPL_ISUPPORTS(MediaShutdownManager, nsIAsyncShutdownBlocker) 26 27 MediaShutdownManager::MediaShutdownManager() { 28 MOZ_ASSERT(NS_IsMainThread()); 29 MOZ_DIAGNOSTIC_ASSERT(sInitPhase == NotInited); 30 } 31 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 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 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 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 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 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 127 MediaShutdownManager::GetName(nsAString& aName) { 128 aName = u"MediaShutdownManager: shutdown"_ns; 129 return NS_OK; 130 } 131 132 NS_IMETHODIMP 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 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