/src/mozilla-central/image/IDecodingTask.cpp
Line | Count | Source (jump to first uncovered line) |
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 "gfxPrefs.h" |
9 | | #include "nsThreadUtils.h" |
10 | | |
11 | | #include "Decoder.h" |
12 | | #include "DecodePool.h" |
13 | | #include "RasterImage.h" |
14 | | #include "SurfaceCache.h" |
15 | | |
16 | | #include "mozilla/SystemGroup.h" |
17 | | |
18 | | namespace mozilla { |
19 | | |
20 | | using gfx::IntRect; |
21 | | |
22 | | namespace image { |
23 | | |
24 | | /////////////////////////////////////////////////////////////////////////////// |
25 | | // Helpers for sending notifications to the image associated with a decoder. |
26 | | /////////////////////////////////////////////////////////////////////////////// |
27 | | |
28 | | void |
29 | | IDecodingTask::EnsureHasEventTarget(NotNull<RasterImage*> aImage) |
30 | 0 | { |
31 | 0 | if (!mEventTarget) { |
32 | 0 | // We determine the event target as late as possible, at the first dispatch |
33 | 0 | // time, because the observers bound to an imgRequest will affect it. |
34 | 0 | // We cache it rather than query for the event target each time because the |
35 | 0 | // event target can change. We don't want to risk events being executed in |
36 | 0 | // a different order than they are dispatched, which can happen if we |
37 | 0 | // selected scheduler groups which have no ordering guarantees relative to |
38 | 0 | // each other (e.g. it moves from scheduler group A for doc group DA to |
39 | 0 | // scheduler group B for doc group DB due to changing observers -- if we |
40 | 0 | // dispatched the first event on A, and the second on B, we don't know which |
41 | 0 | // will execute first.) |
42 | 0 | RefPtr<ProgressTracker> tracker = aImage->GetProgressTracker(); |
43 | 0 | if (tracker) { |
44 | 0 | mEventTarget = tracker->GetEventTarget(); |
45 | 0 | } else { |
46 | 0 | mEventTarget = SystemGroup::EventTargetFor(TaskCategory::Other); |
47 | 0 | } |
48 | 0 | } |
49 | 0 | } |
50 | | |
51 | | bool |
52 | | IDecodingTask::IsOnEventTarget() const |
53 | 0 | { |
54 | 0 | // This is essentially equivalent to NS_IsOnMainThread() because all of the |
55 | 0 | // event targets are for the main thread (although perhaps with a different |
56 | 0 | // label / scheduler group). The observers in ProgressTracker may have |
57 | 0 | // different event targets from this, so this is just a best effort guess. |
58 | 0 | bool current = false; |
59 | 0 | mEventTarget->IsOnCurrentThread(¤t); |
60 | 0 | return current; |
61 | 0 | } |
62 | | |
63 | | void |
64 | | IDecodingTask::NotifyProgress(NotNull<RasterImage*> aImage, |
65 | | NotNull<Decoder*> aDecoder) |
66 | 0 | { |
67 | 0 | MOZ_ASSERT(aDecoder->HasProgress() && !aDecoder->IsMetadataDecode()); |
68 | 0 | EnsureHasEventTarget(aImage); |
69 | 0 |
|
70 | 0 | // Capture the decoder's state. If we need to notify asynchronously, it's |
71 | 0 | // important that we don't wait until the lambda actually runs to capture the |
72 | 0 | // state that we're going to notify. That would both introduce data races on |
73 | 0 | // the decoder's state and cause inconsistencies between the NotifyProgress() |
74 | 0 | // calls we make off-main-thread and the notifications that RasterImage |
75 | 0 | // actually receives, which would cause bugs. |
76 | 0 | Progress progress = aDecoder->TakeProgress(); |
77 | 0 | IntRect invalidRect = aDecoder->TakeInvalidRect(); |
78 | 0 | Maybe<uint32_t> frameCount = aDecoder->TakeCompleteFrameCount(); |
79 | 0 | DecoderFlags decoderFlags = aDecoder->GetDecoderFlags(); |
80 | 0 | SurfaceFlags surfaceFlags = aDecoder->GetSurfaceFlags(); |
81 | 0 |
|
82 | 0 | // Synchronously notify if we can. |
83 | 0 | if (IsOnEventTarget() && !(decoderFlags & DecoderFlags::ASYNC_NOTIFY)) { |
84 | 0 | aImage->NotifyProgress(progress, invalidRect, frameCount, |
85 | 0 | decoderFlags, surfaceFlags); |
86 | 0 | return; |
87 | 0 | } |
88 | 0 | |
89 | 0 | // We're forced to notify asynchronously. |
90 | 0 | NotNull<RefPtr<RasterImage>> image = aImage; |
91 | 0 | mEventTarget->Dispatch(NS_NewRunnableFunction( |
92 | 0 | "IDecodingTask::NotifyProgress", |
93 | 0 | [=]() -> void { |
94 | 0 | image->NotifyProgress(progress, invalidRect, frameCount, |
95 | 0 | decoderFlags, surfaceFlags); |
96 | 0 | }), NS_DISPATCH_NORMAL); |
97 | 0 | } |
98 | | |
99 | | void |
100 | | IDecodingTask::NotifyDecodeComplete(NotNull<RasterImage*> aImage, |
101 | | NotNull<Decoder*> aDecoder) |
102 | 0 | { |
103 | 0 | MOZ_ASSERT(aDecoder->HasError() || !aDecoder->InFrame(), |
104 | 0 | "Decode complete in the middle of a frame?"); |
105 | 0 | EnsureHasEventTarget(aImage); |
106 | 0 |
|
107 | 0 | // Capture the decoder's state. |
108 | 0 | DecoderFinalStatus finalStatus = aDecoder->FinalStatus(); |
109 | 0 | ImageMetadata metadata = aDecoder->GetImageMetadata(); |
110 | 0 | DecoderTelemetry telemetry = aDecoder->Telemetry(); |
111 | 0 | Progress progress = aDecoder->TakeProgress(); |
112 | 0 | IntRect invalidRect = aDecoder->TakeInvalidRect(); |
113 | 0 | Maybe<uint32_t> frameCount = aDecoder->TakeCompleteFrameCount(); |
114 | 0 | DecoderFlags decoderFlags = aDecoder->GetDecoderFlags(); |
115 | 0 | SurfaceFlags surfaceFlags = aDecoder->GetSurfaceFlags(); |
116 | 0 |
|
117 | 0 | // Synchronously notify if we can. |
118 | 0 | if (IsOnEventTarget() && !(decoderFlags & DecoderFlags::ASYNC_NOTIFY)) { |
119 | 0 | aImage->NotifyDecodeComplete(finalStatus, metadata, telemetry, progress, |
120 | 0 | invalidRect, frameCount, decoderFlags, |
121 | 0 | surfaceFlags); |
122 | 0 | return; |
123 | 0 | } |
124 | 0 | |
125 | 0 | // We're forced to notify asynchronously. |
126 | 0 | NotNull<RefPtr<RasterImage>> image = aImage; |
127 | 0 | mEventTarget->Dispatch(NS_NewRunnableFunction( |
128 | 0 | "IDecodingTask::NotifyDecodeComplete", |
129 | 0 | [=]() -> void { |
130 | 0 | image->NotifyDecodeComplete(finalStatus, metadata, telemetry, progress, |
131 | 0 | invalidRect, frameCount, decoderFlags, |
132 | 0 | surfaceFlags); |
133 | 0 | }), NS_DISPATCH_NORMAL); |
134 | 0 | } |
135 | | |
136 | | |
137 | | /////////////////////////////////////////////////////////////////////////////// |
138 | | // IDecodingTask implementation. |
139 | | /////////////////////////////////////////////////////////////////////////////// |
140 | | |
141 | | void |
142 | | IDecodingTask::Resume() |
143 | 0 | { |
144 | 0 | DecodePool::Singleton()->AsyncRun(this); |
145 | 0 | } |
146 | | |
147 | | |
148 | | /////////////////////////////////////////////////////////////////////////////// |
149 | | // MetadataDecodingTask implementation. |
150 | | /////////////////////////////////////////////////////////////////////////////// |
151 | | |
152 | | MetadataDecodingTask::MetadataDecodingTask(NotNull<Decoder*> aDecoder) |
153 | | : mMutex("mozilla::image::MetadataDecodingTask") |
154 | | , mDecoder(aDecoder) |
155 | 0 | { |
156 | 0 | MOZ_ASSERT(mDecoder->IsMetadataDecode(), |
157 | 0 | "Use DecodingTask for non-metadata decodes"); |
158 | 0 | } |
159 | | |
160 | | void |
161 | | MetadataDecodingTask::Run() |
162 | 0 | { |
163 | 0 | MutexAutoLock lock(mMutex); |
164 | 0 |
|
165 | 0 | LexerResult result = mDecoder->Decode(WrapNotNull(this)); |
166 | 0 |
|
167 | 0 | if (result.is<TerminalState>()) { |
168 | 0 | NotifyDecodeComplete(mDecoder->GetImage(), mDecoder); |
169 | 0 | return; // We're done. |
170 | 0 | } |
171 | 0 | |
172 | 0 | if (result == LexerResult(Yield::NEED_MORE_DATA)) { |
173 | 0 | // We can't make any more progress right now. We also don't want to report |
174 | 0 | // any progress, because it's important that metadata decode results are |
175 | 0 | // delivered atomically. The decoder itself will ensure that we get |
176 | 0 | // reenqueued when more data is available; just return for now. |
177 | 0 | return; |
178 | 0 | } |
179 | 0 | |
180 | 0 | MOZ_ASSERT_UNREACHABLE("Metadata decode yielded for an unexpected reason"); |
181 | 0 | } |
182 | | |
183 | | |
184 | | /////////////////////////////////////////////////////////////////////////////// |
185 | | // AnonymousDecodingTask implementation. |
186 | | /////////////////////////////////////////////////////////////////////////////// |
187 | | |
188 | | AnonymousDecodingTask::AnonymousDecodingTask(NotNull<Decoder*> aDecoder) |
189 | | : mDecoder(aDecoder) |
190 | 0 | { } |
191 | | |
192 | | void |
193 | | AnonymousDecodingTask::Run() |
194 | 0 | { |
195 | 0 | while (true) { |
196 | 0 | LexerResult result = mDecoder->Decode(WrapNotNull(this)); |
197 | 0 |
|
198 | 0 | if (result.is<TerminalState>()) { |
199 | 0 | return; // We're done. |
200 | 0 | } |
201 | 0 | |
202 | 0 | if (result == LexerResult(Yield::NEED_MORE_DATA)) { |
203 | 0 | // We can't make any more progress right now. Let the caller decide how to |
204 | 0 | // handle it. |
205 | 0 | return; |
206 | 0 | } |
207 | 0 | |
208 | 0 | // Right now we don't do anything special for other kinds of yields, so just |
209 | 0 | // keep working. |
210 | 0 | MOZ_ASSERT(result.is<Yield>()); |
211 | 0 | } |
212 | 0 | } |
213 | | |
214 | | } // namespace image |
215 | | } // namespace mozilla |