Bug 1869043 assert that graph set access is main thread only r=padenot
[gecko.git] / image / IDecodingTask.cpp
blob1816c9056be68f6ffc7082f326bdbd3c86f92131
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/. */
6 #include "IDecodingTask.h"
8 #include "nsThreadUtils.h"
9 #include "mozilla/AppShutdown.h"
11 #include "Decoder.h"
12 #include "DecodePool.h"
13 #include "RasterImage.h"
14 #include "SurfaceCache.h"
16 namespace mozilla {
18 using gfx::IntRect;
20 namespace image {
22 ///////////////////////////////////////////////////////////////////////////////
23 // Helpers for sending notifications to the image associated with a decoder.
24 ///////////////////////////////////////////////////////////////////////////////
26 void IDecodingTask::EnsureHasEventTarget(NotNull<RasterImage*> aImage) {
27 if (!mEventTarget) {
28 // We determine the event target as late as possible, at the first dispatch
29 // time, because the observers bound to an imgRequest will affect it.
30 // We cache it rather than query for the event target each time because the
31 // event target can change. We don't want to risk events being executed in
32 // a different order than they are dispatched, which can happen if we
33 // selected scheduler groups which have no ordering guarantees relative to
34 // each other (e.g. it moves from scheduler group A for doc group DA to
35 // scheduler group B for doc group DB due to changing observers -- if we
36 // dispatched the first event on A, and the second on B, we don't know which
37 // will execute first.)
38 RefPtr<ProgressTracker> tracker = aImage->GetProgressTracker();
39 if (tracker) {
40 mEventTarget = tracker->GetEventTarget();
41 } else {
42 mEventTarget = GetMainThreadSerialEventTarget();
47 bool IDecodingTask::IsOnEventTarget() const {
48 // This is essentially equivalent to NS_IsOnMainThread() because all of the
49 // event targets are for the main thread (although perhaps with a different
50 // label / scheduler group). The observers in ProgressTracker may have
51 // different event targets from this, so this is just a best effort guess.
52 bool current = false;
53 mEventTarget->IsOnCurrentThread(&current);
54 return current;
57 void IDecodingTask::NotifyProgress(NotNull<RasterImage*> aImage,
58 NotNull<Decoder*> aDecoder) {
59 MOZ_ASSERT(aDecoder->HasProgress() && !aDecoder->IsMetadataDecode());
60 EnsureHasEventTarget(aImage);
62 // Capture the decoder's state. If we need to notify asynchronously, it's
63 // important that we don't wait until the lambda actually runs to capture the
64 // state that we're going to notify. That would both introduce data races on
65 // the decoder's state and cause inconsistencies between the NotifyProgress()
66 // calls we make off-main-thread and the notifications that RasterImage
67 // actually receives, which would cause bugs.
68 Progress progress = aDecoder->TakeProgress();
69 OrientedIntRect invalidRect = aDecoder->TakeInvalidRect();
70 Maybe<uint32_t> frameCount = aDecoder->TakeCompleteFrameCount();
71 DecoderFlags decoderFlags = aDecoder->GetDecoderFlags();
72 SurfaceFlags surfaceFlags = aDecoder->GetSurfaceFlags();
74 // Synchronously notify if we can.
75 if (IsOnEventTarget() && !(decoderFlags & DecoderFlags::ASYNC_NOTIFY)) {
76 aImage->NotifyProgress(progress, invalidRect, frameCount, decoderFlags,
77 surfaceFlags);
78 return;
81 // Don't try to dispatch after shutdown, we'll just leak the runnable.
82 if (NS_WARN_IF(
83 AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads))) {
84 return;
87 // We're forced to notify asynchronously.
88 NotNull<RefPtr<RasterImage>> image = aImage;
89 mEventTarget->Dispatch(CreateRenderBlockingRunnable(NS_NewRunnableFunction(
90 "IDecodingTask::NotifyProgress",
91 [=]() -> void {
92 image->NotifyProgress(progress, invalidRect,
93 frameCount, decoderFlags,
94 surfaceFlags);
95 })),
96 NS_DISPATCH_NORMAL);
99 void IDecodingTask::NotifyDecodeComplete(NotNull<RasterImage*> aImage,
100 NotNull<Decoder*> aDecoder) {
101 MOZ_ASSERT(aDecoder->HasError() || !aDecoder->InFrame(),
102 "Decode complete in the middle of a frame?");
103 EnsureHasEventTarget(aImage);
105 // Capture the decoder's state.
106 DecoderFinalStatus finalStatus = aDecoder->FinalStatus();
107 ImageMetadata metadata = aDecoder->GetImageMetadata();
108 DecoderTelemetry telemetry = aDecoder->Telemetry();
109 Progress progress = aDecoder->TakeProgress();
110 OrientedIntRect invalidRect = aDecoder->TakeInvalidRect();
111 Maybe<uint32_t> frameCount = aDecoder->TakeCompleteFrameCount();
112 DecoderFlags decoderFlags = aDecoder->GetDecoderFlags();
113 SurfaceFlags surfaceFlags = aDecoder->GetSurfaceFlags();
115 // Synchronously notify if we can.
116 if (IsOnEventTarget() && !(decoderFlags & DecoderFlags::ASYNC_NOTIFY)) {
117 aImage->NotifyDecodeComplete(finalStatus, metadata, telemetry, progress,
118 invalidRect, frameCount, decoderFlags,
119 surfaceFlags);
120 return;
123 // Don't try to dispatch after shutdown, we'll just leak the runnable.
124 if (NS_WARN_IF(
125 AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads))) {
126 return;
129 // We're forced to notify asynchronously.
130 NotNull<RefPtr<RasterImage>> image = aImage;
131 mEventTarget->Dispatch(CreateRenderBlockingRunnable(NS_NewRunnableFunction(
132 "IDecodingTask::NotifyDecodeComplete",
133 [=]() -> void {
134 image->NotifyDecodeComplete(
135 finalStatus, metadata, telemetry, progress,
136 invalidRect, frameCount, decoderFlags,
137 surfaceFlags);
138 })),
139 NS_DISPATCH_NORMAL);
142 ///////////////////////////////////////////////////////////////////////////////
143 // IDecodingTask implementation.
144 ///////////////////////////////////////////////////////////////////////////////
146 void IDecodingTask::Resume() { DecodePool::Singleton()->AsyncRun(this); }
148 ///////////////////////////////////////////////////////////////////////////////
149 // MetadataDecodingTask implementation.
150 ///////////////////////////////////////////////////////////////////////////////
152 MetadataDecodingTask::MetadataDecodingTask(NotNull<Decoder*> aDecoder)
153 : mMutex("mozilla::image::MetadataDecodingTask"), mDecoder(aDecoder) {
154 MOZ_ASSERT(mDecoder->IsMetadataDecode(),
155 "Use DecodingTask for non-metadata decodes");
158 void MetadataDecodingTask::Run() {
159 MutexAutoLock lock(mMutex);
161 LexerResult result = mDecoder->Decode(WrapNotNull(this));
163 if (result.is<TerminalState>()) {
164 NotifyDecodeComplete(mDecoder->GetImage(), mDecoder);
165 return; // We're done.
168 if (result == LexerResult(Yield::NEED_MORE_DATA)) {
169 // We can't make any more progress right now. We also don't want to report
170 // any progress, because it's important that metadata decode results are
171 // delivered atomically. The decoder itself will ensure that we get
172 // reenqueued when more data is available; just return for now.
173 return;
176 MOZ_ASSERT_UNREACHABLE("Metadata decode yielded for an unexpected reason");
179 ///////////////////////////////////////////////////////////////////////////////
180 // AnonymousDecodingTask implementation.
181 ///////////////////////////////////////////////////////////////////////////////
183 AnonymousDecodingTask::AnonymousDecodingTask(NotNull<Decoder*> aDecoder,
184 bool aResumable)
185 : mDecoder(aDecoder), mResumable(aResumable) {}
187 void AnonymousDecodingTask::Run() {
188 while (true) {
189 LexerResult result = mDecoder->Decode(WrapNotNull(this));
191 if (result.is<TerminalState>()) {
192 return; // We're done.
195 if (result == LexerResult(Yield::NEED_MORE_DATA)) {
196 // We can't make any more progress right now. Let the caller decide how to
197 // handle it.
198 return;
201 // Right now we don't do anything special for other kinds of yields, so just
202 // keep working.
203 MOZ_ASSERT(result.is<Yield>());
207 void AnonymousDecodingTask::Resume() {
208 // Anonymous decoders normally get all their data at once. We have tests
209 // where they don't; typically in these situations, the test re-runs them
210 // manually. However some tests want to verify Resume works, so they will
211 // explicitly request this behaviour.
212 if (mResumable) {
213 RefPtr<AnonymousDecodingTask> self(this);
214 NS_DispatchToMainThread(
215 NS_NewRunnableFunction("image::AnonymousDecodingTask::Resume",
216 [self]() -> void { self->Run(); }));
220 } // namespace image
221 } // namespace mozilla