Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(214)

Side by Side Diff: chrome/browser/image_decoder.cc

Issue 1068743002: Fix two race conditions in ImageDecoder. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Review comments. Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/image_decoder.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/image_decoder.h" 5 #include "chrome/browser/image_decoder.h"
6 6
7 #include <set>
8
7 #include "base/bind.h" 9 #include "base/bind.h"
8 #include "chrome/browser/browser_process.h" 10 #include "chrome/browser/browser_process.h"
9 #include "chrome/common/chrome_utility_messages.h" 11 #include "chrome/common/chrome_utility_messages.h"
10 #include "chrome/grit/generated_resources.h" 12 #include "chrome/grit/generated_resources.h"
11 #include "content/public/browser/browser_thread.h" 13 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/utility_process_host.h" 14 #include "content/public/browser/utility_process_host.h"
13 #include "ui/base/l10n/l10n_util.h" 15 #include "ui/base/l10n/l10n_util.h"
14 16
15 using content::BrowserThread; 17 using content::BrowserThread;
16 using content::UtilityProcessHost; 18 using content::UtilityProcessHost;
17 19
18 namespace { 20 namespace {
19 21
20 // static, Leaky to allow access from any thread. 22 // static, Leaky to allow access from any thread.
21 base::LazyInstance<ImageDecoder>::Leaky g_decoder = LAZY_INSTANCE_INITIALIZER; 23 base::LazyInstance<ImageDecoder>::Leaky g_decoder = LAZY_INSTANCE_INITIALIZER;
22 24
23 // How long to wait after the last request has been received before ending 25 // How long to wait after the last request has been received before ending
24 // batch mode. 26 // batch mode.
25 const int kBatchModeTimeoutSeconds = 5; 27 const int kBatchModeTimeoutSeconds = 5;
26 28
27 } // namespace 29 } // namespace
28 30
31 class ImageDecoder::Job : public base::RefCountedThreadSafe<ImageDecoder::Job> {
32 public:
33 explicit Job(ImageDecoder::ImageRequest* request);
34
35 void Cancel();
36 void NotifyDecodeSucceeded(const SkBitmap& decoded_image);
37 void NotifyDecodeFailed();
38
39 ImageDecoder::ImageRequest* image_request() const { return request_; }
40
41 private:
42 friend class base::RefCountedThreadSafe<ImageDecoder::Job>;
43 ~Job() = default;
44
45 // Runs on the request's task runner.
46 void OnSuccess(const SkBitmap& decoded_image);
47 void OnFailed();
48
49 // Must only be dereferenced while |is_cancelled_| == false.
50 ImageDecoder::ImageRequest* const request_;
51
52 base::Lock lock_;
53 bool is_cancelled_;
54 };
55
56 ImageDecoder::Job::Job(ImageDecoder::ImageRequest* request)
57 : request_(request), is_cancelled_(false) {
58 }
59
60 void ImageDecoder::Job::Cancel() {
61 {
62 base::AutoLock lock(lock_);
63 is_cancelled_ = true;
64 }
65 g_decoder.Pointer()->RemoveJob(this);
66 }
67
68 void ImageDecoder::Job::NotifyDecodeSucceeded(const SkBitmap& decoded_image) {
69 base::AutoLock lock(lock_);
70 if (is_cancelled_)
71 return;
72
73 request_->task_runner()->PostTask(
74 FROM_HERE,
75 base::Bind(&ImageDecoder::Job::OnSuccess, this, decoded_image));
76 }
77
78 void ImageDecoder::Job::NotifyDecodeFailed() {
79 base::AutoLock lock(lock_);
80 if (is_cancelled_)
81 return;
82
83 request_->task_runner()->PostTask(
84 FROM_HERE, base::Bind(&ImageDecoder::Job::OnFailed, this));
85 }
86
87 void ImageDecoder::Job::OnSuccess(const SkBitmap& decoded_image) {
88 base::AutoLock lock(lock_);
89 if (is_cancelled_)
90 return;
91
92 DCHECK(request_->task_runner()->RunsTasksOnCurrentThread());
93 g_decoder.Pointer()->RemoveJob(this);
94 request_->OnImageDecoded(decoded_image);
95 }
96
97 void ImageDecoder::Job::OnFailed() {
98 base::AutoLock lock(lock_);
99 if (is_cancelled_)
100 return;
101
102 DCHECK(request_->task_runner()->RunsTasksOnCurrentThread());
103 g_decoder.Pointer()->RemoveJob(this);
104 request_->OnDecodeImageFailed();
105 }
106
29 ImageDecoder::ImageDecoder() 107 ImageDecoder::ImageDecoder()
30 : image_request_id_counter_(0), last_request_(base::TimeTicks::Now()) { 108 : image_request_id_counter_(0), last_request_(base::TimeTicks::Now()) {
31 // A single ImageDecoder instance should live for the life of the program. 109 // A single ImageDecoder instance should live for the life of the program.
32 // Explicitly add a reference so the object isn't deleted. 110 // Explicitly add a reference so the object isn't deleted.
33 AddRef(); 111 AddRef();
34 } 112 }
35 113
36 ImageDecoder::~ImageDecoder() { 114 ImageDecoder::~ImageDecoder() {
37 } 115 }
38 116
(...skipping 10 matching lines...) Expand all
49 void ImageDecoder::Start(ImageRequest* image_request, 127 void ImageDecoder::Start(ImageRequest* image_request,
50 const std::string& image_data) { 128 const std::string& image_data) {
51 StartWithOptions(image_request, image_data, DEFAULT_CODEC, false); 129 StartWithOptions(image_request, image_data, DEFAULT_CODEC, false);
52 } 130 }
53 131
54 // static 132 // static
55 void ImageDecoder::StartWithOptions(ImageRequest* image_request, 133 void ImageDecoder::StartWithOptions(ImageRequest* image_request,
56 const std::string& image_data, 134 const std::string& image_data,
57 ImageCodec image_codec, 135 ImageCodec image_codec,
58 bool shrink_to_fit) { 136 bool shrink_to_fit) {
137 g_decoder.Pointer()->StartWithOptionsImpl(image_request, image_data,
138 image_codec, shrink_to_fit);
139 }
140
141 void ImageDecoder::StartWithOptionsImpl(ImageRequest* image_request,
142 const std::string& image_data,
143 ImageCodec image_codec,
144 bool shrink_to_fit) {
59 DCHECK(image_request); 145 DCHECK(image_request);
60 DCHECK(image_request->task_runner()); 146 DCHECK(image_request->task_runner());
147
148 scoped_refptr<Job> job(new Job(image_request));
149 int request_id;
150 {
151 base::AutoLock lock(map_lock_);
152 request_id = image_request_id_counter_++;
153 job_id_map_.insert(std::make_pair(request_id, job));
154 }
61 BrowserThread::PostTask( 155 BrowserThread::PostTask(
62 BrowserThread::IO, FROM_HERE, 156 BrowserThread::IO, FROM_HERE,
63 base::Bind( 157 base::Bind(
64 &ImageDecoder::DecodeImageInSandbox, 158 &ImageDecoder::DecodeImageInSandbox, this, request_id,
65 g_decoder.Pointer(), image_request,
66 std::vector<unsigned char>(image_data.begin(), image_data.end()), 159 std::vector<unsigned char>(image_data.begin(), image_data.end()),
67 image_codec, shrink_to_fit)); 160 image_codec, shrink_to_fit));
68 } 161 }
69 162
70 // static 163 // static
71 void ImageDecoder::Cancel(ImageRequest* image_request) { 164 void ImageDecoder::Cancel(ImageRequest* image_request) {
72 DCHECK(image_request); 165 DCHECK(image_request);
73 g_decoder.Pointer()->CancelImpl(image_request); 166 g_decoder.Pointer()->CancelImpl(image_request);
74 } 167 }
75 168
76 void ImageDecoder::DecodeImageInSandbox( 169 void ImageDecoder::DecodeImageInSandbox(
77 ImageRequest* image_request, 170 int request_id,
78 const std::vector<unsigned char>& image_data, 171 const std::vector<unsigned char>& image_data,
79 ImageCodec image_codec, 172 ImageCodec image_codec,
80 bool shrink_to_fit) { 173 bool shrink_to_fit) {
81 DCHECK_CURRENTLY_ON(BrowserThread::IO); 174 DCHECK_CURRENTLY_ON(BrowserThread::IO);
82 if (!utility_process_host_) { 175 if (!utility_process_host_) {
83 StartBatchMode(); 176 StartBatchMode();
84 } 177 }
85 if (!utility_process_host_) { 178 if (!utility_process_host_) {
86 // Utility process failed to start; notify delegate and return. 179 // Utility process failed to start; notify delegate and return.
87 // Without this check, we were seeing crashes on startup. Further 180 // Without this check, we were seeing crashes on startup. Further
88 // investigation is needed to determine why the utility process 181 // investigation is needed to determine why the utility process
89 // is failing to start. See crbug.com/472272 182 // is failing to start. See crbug.com/472272
90 image_request->task_runner()->PostTask( 183 OnDecodeImageFailed(request_id);
91 FROM_HERE, base::Bind(&ImageRequest::OnDecodeImageFailed,
92 base::Unretained(image_request)));
93 return; 184 return;
94 } 185 }
95 186
96 last_request_ = base::TimeTicks::Now(); 187 last_request_ = base::TimeTicks::Now();
97 base::AutoLock lock(map_lock_);
98 image_request_id_map_.insert(
99 std::make_pair(image_request_id_counter_, image_request));
100 188
101 switch (image_codec) { 189 switch (image_codec) {
102 case ROBUST_JPEG_CODEC: 190 case ROBUST_JPEG_CODEC:
103 utility_process_host_->Send(new ChromeUtilityMsg_RobustJPEGDecodeImage( 191 utility_process_host_->Send(
104 image_data, image_request_id_counter_)); 192 new ChromeUtilityMsg_RobustJPEGDecodeImage(image_data, request_id));
105 break; 193 break;
106 case DEFAULT_CODEC: 194 case DEFAULT_CODEC:
107 utility_process_host_->Send(new ChromeUtilityMsg_DecodeImage( 195 utility_process_host_->Send(new ChromeUtilityMsg_DecodeImage(
108 image_data, shrink_to_fit, image_request_id_counter_)); 196 image_data, shrink_to_fit, request_id));
109 break; 197 break;
110 } 198 }
111
112 ++image_request_id_counter_;
113 } 199 }
114 200
115 void ImageDecoder::CancelImpl(ImageRequest* image_request) { 201 void ImageDecoder::CancelImpl(ImageRequest* image_request) {
116 base::AutoLock lock(map_lock_); 202 std::set<scoped_refptr<Job>> jobs;
117 for (auto it = image_request_id_map_.begin(); 203
118 it != image_request_id_map_.end();) { 204 {
119 if (it->second == image_request) { 205 base::AutoLock lock(map_lock_);
120 image_request_id_map_.erase(it++); 206 for (const auto& request : job_id_map_) {
121 } else { 207 if (request.second->image_request() == image_request) {
122 ++it; 208 jobs.insert(request.second);
209 // Don't erase the job from the map here, but let Job::Cancel do it
210 // instead. This is mainly to maintain consistency with how decode
211 // success/fail are handled.
212 }
123 } 213 }
124 } 214 }
215
216 // Will block if |OnImageDecoded| or |OnDecodeImageFailed| is running.
217 for (auto& job : jobs)
218 job->Cancel();
125 } 219 }
126 220
127 void ImageDecoder::StartBatchMode() { 221 void ImageDecoder::StartBatchMode() {
128 DCHECK_CURRENTLY_ON(BrowserThread::IO); 222 DCHECK_CURRENTLY_ON(BrowserThread::IO);
129 utility_process_host_ = 223 utility_process_host_ =
130 UtilityProcessHost::Create(this, base::MessageLoopProxy::current().get()) 224 UtilityProcessHost::Create(this, base::MessageLoopProxy::current().get())
131 ->AsWeakPtr(); 225 ->AsWeakPtr();
132 utility_process_host_->SetName(l10n_util::GetStringUTF16( 226 utility_process_host_->SetName(l10n_util::GetStringUTF16(
133 IDS_UTILITY_PROCESS_IMAGE_DECODER_NAME)); 227 IDS_UTILITY_PROCESS_IMAGE_DECODER_NAME));
134 if (!utility_process_host_->StartBatchMode()) { 228 if (!utility_process_host_->StartBatchMode()) {
(...skipping 29 matching lines...) Expand all
164 OnDecodeImageFailed) 258 OnDecodeImageFailed)
165 IPC_MESSAGE_UNHANDLED(handled = false) 259 IPC_MESSAGE_UNHANDLED(handled = false)
166 IPC_END_MESSAGE_MAP() 260 IPC_END_MESSAGE_MAP()
167 return handled; 261 return handled;
168 } 262 }
169 263
170 void ImageDecoder::OnDecodeImageSucceeded( 264 void ImageDecoder::OnDecodeImageSucceeded(
171 const SkBitmap& decoded_image, 265 const SkBitmap& decoded_image,
172 int request_id) { 266 int request_id) {
173 DCHECK_CURRENTLY_ON(BrowserThread::IO); 267 DCHECK_CURRENTLY_ON(BrowserThread::IO);
174 base::AutoLock lock(map_lock_);
175 auto it = image_request_id_map_.find(request_id);
176 if (it != image_request_id_map_.end()) {
177 ImageRequest* image_request = it->second;
178 image_request->task_runner()->PostTask(
179 FROM_HERE, base::Bind(&ImageRequest::OnImageDecoded,
180 base::Unretained(image_request), decoded_image));
181 268
182 image_request_id_map_.erase(it); 269 scoped_refptr<Job> job;
270 {
271 base::AutoLock lock(map_lock_);
272 auto it = job_id_map_.find(request_id);
273 if (it != job_id_map_.end()) {
274 job = it->second;
275 // NOTE: The job isn't removed from the map because it can still be
276 // cancelled.
277 }
183 } 278 }
279 // Call Job's functions outside |map_lock_| to avoid a potential deadlock with
280 // |Job.lock_|.
281 if (job)
282 job->NotifyDecodeSucceeded(decoded_image);
184 } 283 }
185 284
186 void ImageDecoder::OnDecodeImageFailed(int request_id) { 285 void ImageDecoder::OnDecodeImageFailed(int request_id) {
187 DCHECK_CURRENTLY_ON(BrowserThread::IO); 286 DCHECK_CURRENTLY_ON(BrowserThread::IO);
287
288 scoped_refptr<Job> job;
289 {
290 base::AutoLock lock(map_lock_);
291 auto it = job_id_map_.find(request_id);
292 if (it != job_id_map_.end()) {
293 job = it->second;
294 }
295 }
296 if (job)
297 job->NotifyDecodeFailed();
298 }
299
300 void ImageDecoder::RemoveJob(const scoped_refptr<Job>& job) {
188 base::AutoLock lock(map_lock_); 301 base::AutoLock lock(map_lock_);
189 auto it = image_request_id_map_.find(request_id); 302 for (auto it = job_id_map_.begin(); it != job_id_map_.end(); ++it) {
190 if (it != image_request_id_map_.end()) { 303 if (it->second == job) {
191 ImageRequest* image_request = it->second; 304 job_id_map_.erase(it);
192 image_request->task_runner()->PostTask( 305 break;
193 FROM_HERE, base::Bind(&ImageRequest::OnDecodeImageFailed, 306 }
194 base::Unretained(image_request)));
195
196 image_request_id_map_.erase(it);
197 } 307 }
198 } 308 }
OLDNEW
« no previous file with comments | « chrome/browser/image_decoder.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698