tor-browser

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

IDecodingTask.cpp (7673B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "IDecodingTask.h"
      7 
      8 #include "nsThreadUtils.h"
      9 #include "mozilla/AppShutdown.h"
     10 
     11 #include "Decoder.h"
     12 #include "DecodePool.h"
     13 #include "RasterImage.h"
     14 #include "SurfaceCache.h"
     15 
     16 namespace mozilla {
     17 
     18 using gfx::IntRect;
     19 
     20 namespace image {
     21 
     22 ///////////////////////////////////////////////////////////////////////////////
     23 // Helpers for sending notifications to the image associated with a decoder.
     24 ///////////////////////////////////////////////////////////////////////////////
     25 
     26 void IDecodingTask::NotifyProgress(NotNull<RasterImage*> aImage,
     27                                   NotNull<Decoder*> aDecoder) {
     28  MOZ_ASSERT(aDecoder->HasProgress() && !aDecoder->IsMetadataDecode());
     29 
     30  // Capture the decoder's state. If we need to notify asynchronously, it's
     31  // important that we don't wait until the lambda actually runs to capture the
     32  // state that we're going to notify. That would both introduce data races on
     33  // the decoder's state and cause inconsistencies between the NotifyProgress()
     34  // calls we make off-main-thread and the notifications that RasterImage
     35  // actually receives, which would cause bugs.
     36  Progress progress = aDecoder->TakeProgress();
     37  OrientedIntRect invalidRect = aDecoder->TakeInvalidRect();
     38  Maybe<uint32_t> frameCount = aDecoder->TakeCompleteFrameCount();
     39  DecoderFlags decoderFlags = aDecoder->GetDecoderFlags();
     40  SurfaceFlags surfaceFlags = aDecoder->GetSurfaceFlags();
     41 
     42  // Synchronously notify if we can.
     43  if (NS_IsMainThread() && !(decoderFlags & DecoderFlags::ASYNC_NOTIFY)) {
     44    aImage->NotifyProgress(progress, invalidRect, frameCount, decoderFlags,
     45                           surfaceFlags);
     46    return;
     47  }
     48 
     49  // Don't try to dispatch after shutdown, we'll just leak the runnable.
     50  if (NS_WARN_IF(
     51          AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads))) {
     52    return;
     53  }
     54 
     55  // We're forced to notify asynchronously.
     56  NotNull<RefPtr<RasterImage>> image = aImage;
     57  nsCOMPtr<nsIEventTarget> eventTarget = GetMainThreadSerialEventTarget();
     58  eventTarget->Dispatch(CreateRenderBlockingRunnable(NS_NewRunnableFunction(
     59                            "IDecodingTask::NotifyProgress",
     60                            [=]() -> void {
     61                              image->NotifyProgress(progress, invalidRect,
     62                                                    frameCount, decoderFlags,
     63                                                    surfaceFlags);
     64                            })),
     65                        NS_DISPATCH_NORMAL);
     66 }
     67 
     68 void IDecodingTask::NotifyDecodeComplete(NotNull<RasterImage*> aImage,
     69                                         NotNull<Decoder*> aDecoder) {
     70  MOZ_ASSERT(aDecoder->HasError() || !aDecoder->InFrame(),
     71             "Decode complete in the middle of a frame?");
     72 
     73  // Capture the decoder's state.
     74  DecoderFinalStatus finalStatus = aDecoder->FinalStatus();
     75  ImageMetadata metadata = aDecoder->GetImageMetadata();
     76  DecoderTelemetry telemetry = aDecoder->Telemetry();
     77  Progress progress = aDecoder->TakeProgress();
     78  OrientedIntRect invalidRect = aDecoder->TakeInvalidRect();
     79  Maybe<uint32_t> frameCount = aDecoder->TakeCompleteFrameCount();
     80  DecoderFlags decoderFlags = aDecoder->GetDecoderFlags();
     81  SurfaceFlags surfaceFlags = aDecoder->GetSurfaceFlags();
     82 
     83  // Synchronously notify if we can.
     84  if (NS_IsMainThread() && !(decoderFlags & DecoderFlags::ASYNC_NOTIFY)) {
     85    aImage->NotifyDecodeComplete(finalStatus, metadata, telemetry, progress,
     86                                 invalidRect, frameCount, decoderFlags,
     87                                 surfaceFlags);
     88    return;
     89  }
     90 
     91  // Don't try to dispatch after shutdown, we'll just leak the runnable.
     92  if (NS_WARN_IF(
     93          AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads))) {
     94    return;
     95  }
     96 
     97  // We're forced to notify asynchronously.
     98  NotNull<RefPtr<RasterImage>> image = aImage;
     99  nsCOMPtr<nsIEventTarget> eventTarget = GetMainThreadSerialEventTarget();
    100  eventTarget->Dispatch(CreateRenderBlockingRunnable(NS_NewRunnableFunction(
    101                            "IDecodingTask::NotifyDecodeComplete",
    102                            [=]() -> void {
    103                              image->NotifyDecodeComplete(
    104                                  finalStatus, metadata, telemetry, progress,
    105                                  invalidRect, frameCount, decoderFlags,
    106                                  surfaceFlags);
    107                            })),
    108                        NS_DISPATCH_NORMAL);
    109 }
    110 
    111 ///////////////////////////////////////////////////////////////////////////////
    112 // IDecodingTask implementation.
    113 ///////////////////////////////////////////////////////////////////////////////
    114 
    115 void IDecodingTask::Resume() { DecodePool::Singleton()->AsyncRun(this); }
    116 
    117 ///////////////////////////////////////////////////////////////////////////////
    118 // MetadataDecodingTask implementation.
    119 ///////////////////////////////////////////////////////////////////////////////
    120 
    121 MetadataDecodingTask::MetadataDecodingTask(NotNull<Decoder*> aDecoder)
    122    : mMutex("mozilla::image::MetadataDecodingTask"), mDecoder(aDecoder) {
    123  MOZ_ASSERT(mDecoder->IsMetadataDecode(),
    124             "Use DecodingTask for non-metadata decodes");
    125 }
    126 
    127 void MetadataDecodingTask::Run() {
    128  MutexAutoLock lock(mMutex);
    129 
    130  LexerResult result = mDecoder->Decode(WrapNotNull(this));
    131 
    132  if (result.is<TerminalState>()) {
    133    NotifyDecodeComplete(mDecoder->GetImage(), mDecoder);
    134    return;  // We're done.
    135  }
    136 
    137  if (result == LexerResult(Yield::NEED_MORE_DATA)) {
    138    // We can't make any more progress right now. We also don't want to report
    139    // any progress, because it's important that metadata decode results are
    140    // delivered atomically. The decoder itself will ensure that we get
    141    // reenqueued when more data is available; just return for now.
    142    return;
    143  }
    144 
    145  MOZ_ASSERT_UNREACHABLE("Metadata decode yielded for an unexpected reason");
    146 }
    147 
    148 ///////////////////////////////////////////////////////////////////////////////
    149 // AnonymousDecodingTask implementation.
    150 ///////////////////////////////////////////////////////////////////////////////
    151 
    152 AnonymousDecodingTask::AnonymousDecodingTask(NotNull<Decoder*> aDecoder,
    153                                             bool aResumable)
    154    : mDecoder(aDecoder), mResumable(aResumable) {}
    155 
    156 void AnonymousDecodingTask::Run() {
    157  while (true) {
    158    LexerResult result = mDecoder->Decode(WrapNotNull(this));
    159 
    160    if (result.is<TerminalState>()) {
    161      return;  // We're done.
    162    }
    163 
    164    if (result == LexerResult(Yield::NEED_MORE_DATA)) {
    165      // We can't make any more progress right now. Let the caller decide how to
    166      // handle it.
    167      return;
    168    }
    169 
    170    // Right now we don't do anything special for other kinds of yields, so just
    171    // keep working.
    172    MOZ_ASSERT(result.is<Yield>());
    173  }
    174 }
    175 
    176 void AnonymousDecodingTask::Resume() {
    177  // Anonymous decoders normally get all their data at once. We have tests
    178  // where they don't; typically in these situations, the test re-runs them
    179  // manually. However some tests want to verify Resume works, so they will
    180  // explicitly request this behaviour.
    181  if (mResumable) {
    182    RefPtr<AnonymousDecodingTask> self(this);
    183    NS_DispatchToMainThread(
    184        NS_NewRunnableFunction("image::AnonymousDecodingTask::Resume",
    185                               [self]() -> void { self->Run(); }));
    186  }
    187 }
    188 
    189 }  // namespace image
    190 }  // namespace mozilla