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

Side by Side Diff: net/url_request/url_request_file_job.cc

Issue 10695110: Avoid disk accesses on the wrong thread in URLRequestFileJob (Closed) Base URL: https://src.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 5 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 | « net/url_request/url_request_file_job.h ('k') | net/url_request/url_request_unittest.cc » ('j') | 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 // For loading files, we make use of overlapped i/o to ensure that reading from 5 // For loading files, we make use of overlapped i/o to ensure that reading from
6 // the filesystem (e.g., a network filesystem) does not block the calling 6 // the filesystem (e.g., a network filesystem) does not block the calling
7 // thread. An alternative approach would be to use a background thread or pool 7 // thread. An alternative approach would be to use a background thread or pool
8 // of threads, but it seems better to leverage the operating system's ability 8 // of threads, but it seems better to leverage the operating system's ability
9 // to do background file reads for us. 9 // to do background file reads for us.
10 // 10 //
(...skipping 24 matching lines...) Expand all
35 #include "net/base/net_errors.h" 35 #include "net/base/net_errors.h"
36 #include "net/base/net_util.h" 36 #include "net/base/net_util.h"
37 #include "net/http/http_util.h" 37 #include "net/http/http_util.h"
38 #include "net/url_request/url_request.h" 38 #include "net/url_request/url_request.h"
39 #include "net/url_request/url_request_context.h" 39 #include "net/url_request/url_request_context.h"
40 #include "net/url_request/url_request_error_job.h" 40 #include "net/url_request/url_request_error_job.h"
41 #include "net/url_request/url_request_file_dir_job.h" 41 #include "net/url_request/url_request_file_dir_job.h"
42 42
43 namespace net { 43 namespace net {
44 44
45 class URLRequestFileJob::AsyncResolver 45 namespace {
46 : public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> {
47 public:
48 explicit AsyncResolver(URLRequestFileJob* owner)
49 : owner_(owner), owner_loop_(MessageLoop::current()) {
50 }
51 46
52 void Resolve(const FilePath& file_path) { 47 void Resolve(const FilePath& file_path,
53 base::PlatformFileInfo file_info; 48 bool* exists,
54 bool exists = file_util::GetFileInfo(file_path, &file_info); 49 base::PlatformFileInfo* file_info) {
55 base::AutoLock locked(lock_); 50 *exists = file_util::GetFileInfo(file_path, file_info);
56 if (owner_loop_) { 51 }
57 owner_loop_->PostTask(
58 FROM_HERE,
59 base::Bind(&AsyncResolver::ReturnResults, this, exists, file_info));
60 }
61 }
62 52
63 void Cancel() { 53 }
64 owner_ = NULL;
65 54
66 base::AutoLock locked(lock_);
67 owner_loop_ = NULL;
68 }
69
70 private:
71 friend class base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver>;
72
73 ~AsyncResolver() {}
74
75 void ReturnResults(bool exists, const base::PlatformFileInfo& file_info) {
76 if (owner_)
77 owner_->DidResolve(exists, file_info);
78 }
79
80 URLRequestFileJob* owner_;
81
82 base::Lock lock_;
83 MessageLoop* owner_loop_;
84 };
85 55
86 URLRequestFileJob::URLRequestFileJob(URLRequest* request, 56 URLRequestFileJob::URLRequestFileJob(URLRequest* request,
87 const FilePath& file_path) 57 const FilePath& file_path)
88 : URLRequestJob(request, request->context()->network_delegate()), 58 : URLRequestJob(request, request->context()->network_delegate()),
89 file_path_(file_path), 59 file_path_(file_path),
90 stream_(NULL), 60 stream_(NULL),
91 is_directory_(false), 61 is_directory_(false),
eroman 2012/07/10 23:50:56 Could you initialize file_size_ to something like
92 remaining_bytes_(0) { 62 remaining_bytes_(0),
63 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
93 } 64 }
94 65
95 // static 66 // static
96 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request, 67 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request,
97 const std::string& scheme) { 68 const std::string& scheme) {
98 FilePath file_path; 69 FilePath file_path;
99 const bool is_file = FileURLToFilePath(request->url(), &file_path); 70 const bool is_file = FileURLToFilePath(request->url(), &file_path);
100 71
101 // Check file access permissions. 72 // Check file access permissions.
102 if (!IsFileAccessAllowed(*request, file_path)) 73 if (!IsFileAccessAllowed(*request, file_path))
103 return new URLRequestErrorJob(request, ERR_ACCESS_DENIED); 74 return new URLRequestErrorJob(request, ERR_ACCESS_DENIED);
104 75
105 // We need to decide whether to create URLRequestFileJob for file access or 76 // We need to decide whether to create URLRequestFileJob for file access or
106 // URLRequestFileDirJob for directory access. To avoid accessing the 77 // URLRequestFileDirJob for directory access. To avoid accessing the
107 // filesystem, we only look at the path string here. 78 // filesystem, we only look at the path string here.
108 // The code in the URLRequestFileJob::Start() method discovers that a path, 79 // The code in the URLRequestFileJob::Start() method discovers that a path,
109 // which doesn't end with a slash, should really be treated as a directory, 80 // which doesn't end with a slash, should really be treated as a directory,
110 // and it then redirects to the URLRequestFileDirJob. 81 // and it then redirects to the URLRequestFileDirJob.
111 if (is_file && 82 if (is_file &&
112 file_util::EndsWithSeparator(file_path) && 83 file_util::EndsWithSeparator(file_path) &&
113 file_path.IsAbsolute()) 84 file_path.IsAbsolute())
114 return new URLRequestFileDirJob(request, file_path); 85 return new URLRequestFileDirJob(request, file_path);
115 86
116 // Use a regular file request job for all non-directories (including invalid 87 // Use a regular file request job for all non-directories (including invalid
117 // file names). 88 // file names).
118 return new URLRequestFileJob(request, file_path); 89 return new URLRequestFileJob(request, file_path);
119 } 90 }
120 91
121 void URLRequestFileJob::Start() { 92 void URLRequestFileJob::Start() {
122 DCHECK(!async_resolver_); 93 bool* exists = new bool;
123 async_resolver_ = new AsyncResolver(this); 94 base::PlatformFileInfo* file_info = new base::PlatformFileInfo;
124 base::WorkerPool::PostTask( 95 base::WorkerPool::PostTaskAndReply(
125 FROM_HERE, 96 FROM_HERE,
126 base::Bind(&AsyncResolver::Resolve, async_resolver_.get(), file_path_), 97 base::Bind(&Resolve, file_path_,
98 base::Unretained(exists), base::Unretained(file_info)),
99 base::Bind(&URLRequestFileJob::DidResolve,
100 weak_ptr_factory_.GetWeakPtr(),
101 base::Owned(exists), base::Owned(file_info)),
127 true); 102 true);
128 } 103 }
129 104
130 void URLRequestFileJob::Kill() { 105 void URLRequestFileJob::Kill() {
131 // URL requests should not block on the disk! 106 stream_.CloseAndCancelAsync();
132 // http://code.google.com/p/chromium/issues/detail?id=59849 107 weak_ptr_factory_.InvalidateWeakPtrs();
133 base::ThreadRestrictions::ScopedAllowIO allow_io;
134 stream_.CloseSync();
135
136 if (async_resolver_) {
137 async_resolver_->Cancel();
138 async_resolver_ = NULL;
139 }
140 108
141 URLRequestJob::Kill(); 109 URLRequestJob::Kill();
142 } 110 }
143 111
144 bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size, 112 bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size,
145 int *bytes_read) { 113 int *bytes_read) {
146 DCHECK_NE(dest_size, 0); 114 DCHECK_NE(dest_size, 0);
147 DCHECK(bytes_read); 115 DCHECK(bytes_read);
148 DCHECK_GE(remaining_bytes_, 0); 116 DCHECK_GE(remaining_bytes_, 0);
149 117
150 if (remaining_bytes_ < dest_size) 118 if (remaining_bytes_ < dest_size)
151 dest_size = static_cast<int>(remaining_bytes_); 119 dest_size = static_cast<int>(remaining_bytes_);
152 120
153 // If we should copy zero bytes because |remaining_bytes_| is zero, short 121 // If we should copy zero bytes because |remaining_bytes_| is zero, short
154 // circuit here. 122 // circuit here.
155 if (!dest_size) { 123 if (!dest_size) {
156 *bytes_read = 0; 124 *bytes_read = 0;
157 return true; 125 return true;
158 } 126 }
159 127
160 int rv = stream_.Read(dest, dest_size, 128 int rv = stream_.Read(dest, dest_size,
161 base::Bind(&URLRequestFileJob::DidRead, 129 base::Bind(&URLRequestFileJob::DidRead,
162 base::Unretained(this))); 130 weak_ptr_factory_.GetWeakPtr()));
163 if (rv >= 0) { 131 if (rv >= 0) {
164 // Data is immediately available. 132 // Data is immediately available.
165 *bytes_read = rv; 133 *bytes_read = rv;
166 remaining_bytes_ -= rv; 134 remaining_bytes_ -= rv;
167 DCHECK_GE(remaining_bytes_, 0); 135 DCHECK_GE(remaining_bytes_, 0);
168 return true; 136 return true;
169 } 137 }
170 138
171 // Otherwise, a read error occured. We may just need to wait... 139 // Otherwise, a read error occured. We may just need to wait...
172 if (rv == ERR_IO_PENDING) { 140 if (rv == ERR_IO_PENDING) {
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
256 const URLRequestContext* context = request.context(); 224 const URLRequestContext* context = request.context();
257 if (!context) 225 if (!context)
258 return false; 226 return false;
259 const NetworkDelegate* delegate = context->network_delegate(); 227 const NetworkDelegate* delegate = context->network_delegate();
260 if (delegate) 228 if (delegate)
261 return delegate->CanAccessFile(request, path); 229 return delegate->CanAccessFile(request, path);
262 return false; 230 return false;
263 } 231 }
264 232
265 URLRequestFileJob::~URLRequestFileJob() { 233 URLRequestFileJob::~URLRequestFileJob() {
266 DCHECK(!async_resolver_);
267 } 234 }
268 235
269 void URLRequestFileJob::DidResolve( 236 void URLRequestFileJob::DidResolve(const bool* exists,
270 bool exists, const base::PlatformFileInfo& file_info) { 237 const base::PlatformFileInfo* file_info) {
271 async_resolver_ = NULL;
272
273 // We may have been orphaned... 238 // We may have been orphaned...
274 if (!request_) 239 if (!request_)
eroman 2012/07/10 23:50:56 I wander if this is still true, or if Kill() will
275 return; 240 return;
276 241
277 is_directory_ = file_info.is_directory; 242 is_directory_ = file_info->is_directory;
243 file_size_ = file_info->size;
278 244
279 int rv = OK;
280 // We use URLRequestFileJob to handle files as well as directories without 245 // We use URLRequestFileJob to handle files as well as directories without
281 // trailing slash. 246 // trailing slash.
282 // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise, 247 // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise,
283 // we will append trailing slash and redirect to FileDirJob. 248 // we will append trailing slash and redirect to FileDirJob.
284 // A special case is "\" on Windows. We should resolve as invalid. 249 // A special case is "\" on Windows. We should resolve as invalid.
285 // However, Windows resolves "\" to "C:\", thus reports it as existent. 250 // However, Windows resolves "\" to "C:\", thus reports it as existent.
286 // So what happens is we append it with trailing slash and redirect it to 251 // So what happens is we append it with trailing slash and redirect it to
287 // FileDirJob where it is resolved as invalid. 252 // FileDirJob where it is resolved as invalid.
288 if (!exists) { 253 if (!(*exists)) {
289 rv = ERR_FILE_NOT_FOUND; 254 DidOpen(ERR_FILE_NOT_FOUND);
eroman 2012/07/10 23:50:56 [optional] Rather than an if/else chain, I like to
290 } else if (!is_directory_) { 255 } else if (!is_directory_) {
291 // URL requests should not block on the disk!
292 // http://code.google.com/p/chromium/issues/detail?id=59849
293 base::ThreadRestrictions::ScopedAllowIO allow_io;
294
295 int flags = base::PLATFORM_FILE_OPEN | 256 int flags = base::PLATFORM_FILE_OPEN |
296 base::PLATFORM_FILE_READ | 257 base::PLATFORM_FILE_READ |
297 base::PLATFORM_FILE_ASYNC; 258 base::PLATFORM_FILE_ASYNC;
298 rv = stream_.OpenSync(file_path_, flags); 259 int rv = stream_.Open(file_path_, flags,
260 base::Bind(&URLRequestFileJob::DidOpen,
261 weak_ptr_factory_.GetWeakPtr()));
262 if (rv != ERR_IO_PENDING)
263 DidOpen(rv);
264 } else {
265 DidOpen(OK);
299 } 266 }
267 }
300 268
301 if (rv != OK) { 269 void URLRequestFileJob::DidOpen(int result) {
302 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); 270 if (result != OK) {
271 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
303 return; 272 return;
304 } 273 }
305 274
306 if (!byte_range_.ComputeBounds(file_info.size)) { 275 if (!byte_range_.ComputeBounds(file_size_)) {
307 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 276 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
308 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); 277 ERR_REQUEST_RANGE_NOT_SATISFIABLE));
309 return; 278 return;
310 } 279 }
311 280
312 remaining_bytes_ = byte_range_.last_byte_position() - 281 remaining_bytes_ = byte_range_.last_byte_position() -
313 byte_range_.first_byte_position() + 1; 282 byte_range_.first_byte_position() + 1;
314 DCHECK_GE(remaining_bytes_, 0); 283 DCHECK_GE(remaining_bytes_, 0);
315 284
316 // URL requests should not block on the disk! 285 if (remaining_bytes_ > 0 && byte_range_.first_byte_position() != 0) {
317 // http://code.google.com/p/chromium/issues/detail?id=59849 286 int rv = stream_.Seek(FROM_BEGIN, byte_range_.first_byte_position(),
318 { 287 base::Bind(&URLRequestFileJob::DidSeek,
319 base::ThreadRestrictions::ScopedAllowIO allow_io; 288 weak_ptr_factory_.GetWeakPtr()));
320 // Do the seek at the beginning of the request. 289 if (rv != ERR_IO_PENDING)
321 if (remaining_bytes_ > 0 && 290 DidSeek(-1);
322 byte_range_.first_byte_position() != 0 && 291 } else {
323 byte_range_.first_byte_position() != 292 DidSeek(byte_range_.first_byte_position());
324 stream_.SeekSync(FROM_BEGIN, byte_range_.first_byte_position())) { 293 }
325 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 294 }
326 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); 295
327 return; 296 void URLRequestFileJob::DidSeek(int64 result) {
328 } 297 if (result != byte_range_.first_byte_position()) {
298 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
299 ERR_REQUEST_RANGE_NOT_SATISFIABLE));
300 return;
329 } 301 }
330 302
331 set_expected_content_size(remaining_bytes_); 303 set_expected_content_size(remaining_bytes_);
332 NotifyHeadersComplete(); 304 NotifyHeadersComplete();
333 } 305 }
334 306
335 void URLRequestFileJob::DidRead(int result) { 307 void URLRequestFileJob::DidRead(int result) {
336 if (result > 0) { 308 if (result > 0) {
337 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status 309 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status
338 } else if (result == 0) { 310 } else if (result == 0) {
339 NotifyDone(URLRequestStatus()); 311 NotifyDone(URLRequestStatus());
340 } else { 312 } else {
341 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); 313 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
342 } 314 }
343 315
344 remaining_bytes_ -= result; 316 remaining_bytes_ -= result;
345 DCHECK_GE(remaining_bytes_, 0); 317 DCHECK_GE(remaining_bytes_, 0);
346 318
347 NotifyReadComplete(result); 319 NotifyReadComplete(result);
348 } 320 }
349 321
350 } // namespace net 322 } // namespace net
OLDNEW
« no previous file with comments | « net/url_request/url_request_file_job.h ('k') | net/url_request/url_request_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698