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