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"
12 #include "DecodePool.h"
13 #include "RasterImage.h"
14 #include "SurfaceCache.h"
22 ///////////////////////////////////////////////////////////////////////////////
23 // Helpers for sending notifications to the image associated with a decoder.
24 ///////////////////////////////////////////////////////////////////////////////
26 void IDecodingTask::EnsureHasEventTarget(NotNull
<RasterImage
*> aImage
) {
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();
40 mEventTarget
= tracker
->GetEventTarget();
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.
53 mEventTarget
->IsOnCurrentThread(¤t
);
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
,
81 // Don't try to dispatch after shutdown, we'll just leak the runnable.
83 AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads
))) {
87 // We're forced to notify asynchronously.
88 NotNull
<RefPtr
<RasterImage
>> image
= aImage
;
89 mEventTarget
->Dispatch(CreateRenderBlockingRunnable(NS_NewRunnableFunction(
90 "IDecodingTask::NotifyProgress",
92 image
->NotifyProgress(progress
, invalidRect
,
93 frameCount
, decoderFlags
,
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
,
123 // Don't try to dispatch after shutdown, we'll just leak the runnable.
125 AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads
))) {
129 // We're forced to notify asynchronously.
130 NotNull
<RefPtr
<RasterImage
>> image
= aImage
;
131 mEventTarget
->Dispatch(CreateRenderBlockingRunnable(NS_NewRunnableFunction(
132 "IDecodingTask::NotifyDecodeComplete",
134 image
->NotifyDecodeComplete(
135 finalStatus
, metadata
, telemetry
, progress
,
136 invalidRect
, frameCount
, decoderFlags
,
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.
176 MOZ_ASSERT_UNREACHABLE("Metadata decode yielded for an unexpected reason");
179 ///////////////////////////////////////////////////////////////////////////////
180 // AnonymousDecodingTask implementation.
181 ///////////////////////////////////////////////////////////////////////////////
183 AnonymousDecodingTask::AnonymousDecodingTask(NotNull
<Decoder
*> aDecoder
,
185 : mDecoder(aDecoder
), mResumable(aResumable
) {}
187 void AnonymousDecodingTask::Run() {
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
201 // Right now we don't do anything special for other kinds of yields, so just
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.
213 RefPtr
<AnonymousDecodingTask
> self(this);
214 NS_DispatchToMainThread(
215 NS_NewRunnableFunction("image::AnonymousDecodingTask::Resume",
216 [self
]() -> void { self
->Run(); }));
221 } // namespace mozilla