tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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