michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "MediaShutdownManager.h" michael@0: #include "nsContentUtils.h" michael@0: #include "mozilla/StaticPtr.h" michael@0: #include "MediaDecoder.h" michael@0: #include "SharedThreadPool.h" michael@0: #include "prlog.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: #ifdef PR_LOGGING michael@0: extern PRLogModuleInfo* gMediaDecoderLog; michael@0: #define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg) michael@0: #else michael@0: #define DECODER_LOG(type, msg) michael@0: #endif michael@0: michael@0: NS_IMPL_ISUPPORTS(MediaShutdownManager, nsIObserver) michael@0: michael@0: MediaShutdownManager::MediaShutdownManager() michael@0: : mIsObservingShutdown(false), michael@0: mIsDoingXPCOMShutDown(false) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_COUNT_CTOR(MediaShutdownManager); michael@0: } michael@0: michael@0: MediaShutdownManager::~MediaShutdownManager() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_COUNT_DTOR(MediaShutdownManager); michael@0: } michael@0: michael@0: // Note that we don't use ClearOnShutdown() on this StaticRefPtr, as that michael@0: // may interfere with our shutdown listener. michael@0: StaticRefPtr MediaShutdownManager::sInstance; michael@0: michael@0: MediaShutdownManager& michael@0: MediaShutdownManager::Instance() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: if (!sInstance) { michael@0: sInstance = new MediaShutdownManager(); michael@0: } michael@0: return *sInstance; michael@0: } michael@0: michael@0: void michael@0: MediaShutdownManager::EnsureCorrectShutdownObserverState() michael@0: { michael@0: MOZ_ASSERT(!mIsDoingXPCOMShutDown); michael@0: bool needShutdownObserver = mDecoders.Count() > 0; michael@0: if (needShutdownObserver != mIsObservingShutdown) { michael@0: mIsObservingShutdown = needShutdownObserver; michael@0: if (mIsObservingShutdown) { michael@0: nsContentUtils::RegisterShutdownObserver(this); michael@0: } else { michael@0: nsContentUtils::UnregisterShutdownObserver(this); michael@0: // Clear our singleton reference. This will probably delete michael@0: // this instance, so don't deref |this| clearing sInstance. michael@0: sInstance = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: MediaShutdownManager::Register(MediaDecoder* aDecoder) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: // Don't call Register() after you've Unregistered() all the decoders, michael@0: // that's not going to work. michael@0: MOZ_ASSERT(!mDecoders.Contains(aDecoder)); michael@0: mDecoders.PutEntry(aDecoder); michael@0: MOZ_ASSERT(mDecoders.Contains(aDecoder)); michael@0: MOZ_ASSERT(mDecoders.Count() > 0); michael@0: EnsureCorrectShutdownObserverState(); michael@0: } michael@0: michael@0: void michael@0: MediaShutdownManager::Unregister(MediaDecoder* aDecoder) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(mDecoders.Contains(aDecoder)); michael@0: if (!mIsDoingXPCOMShutDown) { michael@0: mDecoders.RemoveEntry(aDecoder); michael@0: EnsureCorrectShutdownObserverState(); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: MediaShutdownManager::Observe(nsISupports *aSubjet, michael@0: const char *aTopic, michael@0: const char16_t *someData) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { michael@0: Shutdown(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: ShutdownMediaDecoder(nsRefPtrHashKey* aEntry, void*) michael@0: { michael@0: aEntry->GetKey()->Shutdown(); michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: void michael@0: MediaShutdownManager::Shutdown() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(sInstance); michael@0: michael@0: DECODER_LOG(PR_LOG_DEBUG, ("MediaShutdownManager::Shutdown() start...")); michael@0: michael@0: // Mark that we're shutting down, so that Unregister(*) calls don't remove michael@0: // hashtable entries. If Unregsiter(*) was to remove from the hash table, michael@0: // the iterations over the hashtables below would be disrupted. michael@0: mIsDoingXPCOMShutDown = true; michael@0: michael@0: // Iterate over the decoders and shut them down, and remove them from the michael@0: // hashtable. michael@0: mDecoders.EnumerateEntries(ShutdownMediaDecoder, nullptr); michael@0: michael@0: // Ensure all media shared thread pools are shutdown. This joins with all michael@0: // threads in the state machine thread pool, the decoder thread pool, and michael@0: // any others. michael@0: SharedThreadPool::SpinUntilShutdown(); michael@0: michael@0: // Remove the MediaShutdownManager instance from the shutdown observer michael@0: // list. michael@0: nsContentUtils::UnregisterShutdownObserver(this); michael@0: michael@0: // Clear the singleton instance. The only remaining reference should be the michael@0: // reference that the observer service used to call us with. The michael@0: // MediaShutdownManager will be deleted once the observer service cleans michael@0: // up after it finishes its notifications. michael@0: sInstance = nullptr; michael@0: michael@0: DECODER_LOG(PR_LOG_DEBUG, ("MediaShutdownManager::Shutdown() end.")); michael@0: } michael@0: michael@0: } // namespace mozilla