summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorpaivanof@gmail.com <paivanof@gmail.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-19 23:28:44 +0000
committerpaivanof@gmail.com <paivanof@gmail.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-19 23:28:44 +0000
commit70c04ac456eb35e82ba2bcd834895d1b3f087347 (patch)
tree58bce868bee002a1e4c52977822469efdfd1dc1d /net
parent814ec4b60cbcfd497ff2c79b4fac86e73660b3da (diff)
downloadchromium_src-70c04ac456eb35e82ba2bcd834895d1b3f087347.zip
chromium_src-70c04ac456eb35e82ba2bcd834895d1b3f087347.tar.gz
chromium_src-70c04ac456eb35e82ba2bcd834895d1b3f087347.tar.bz2
Avoid disk accesses on the wrong thread in URLRequestFileJob
while opening and positioning in the file. Patch from Pavel Ivanov <paivanof@gmail.com> BUG=59849,114783 TEST=net_unittests Review URL: https://chromiumcodereview.appspot.com/10695110 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@168626 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/url_request/url_request_file_job.cc177
-rw-r--r--net/url_request/url_request_file_job.h41
-rw-r--r--net/url_request/url_request_unittest.cc18
3 files changed, 133 insertions, 103 deletions
diff --git a/net/url_request/url_request_file_job.cc b/net/url_request/url_request_file_job.cc
index c60b313..0c703e1 100644
--- a/net/url_request/url_request_file_job.cc
+++ b/net/url_request/url_request_file_job.cc
@@ -45,54 +45,21 @@
namespace net {
-class URLRequestFileJob::AsyncResolver
- : public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> {
- public:
- explicit AsyncResolver(URLRequestFileJob* owner)
- : owner_(owner), owner_loop_(MessageLoop::current()) {
- }
-
- void Resolve(const FilePath& file_path) {
- base::PlatformFileInfo file_info;
- bool exists = file_util::GetFileInfo(file_path, &file_info);
- base::AutoLock locked(lock_);
- if (owner_loop_) {
- owner_loop_->PostTask(
- FROM_HERE,
- base::Bind(&AsyncResolver::ReturnResults, this, exists, file_info));
- }
- }
-
- void Cancel() {
- owner_ = NULL;
-
- base::AutoLock locked(lock_);
- owner_loop_ = NULL;
- }
-
- private:
- friend class base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver>;
-
- ~AsyncResolver() {}
-
- void ReturnResults(bool exists, const base::PlatformFileInfo& file_info) {
- if (owner_)
- owner_->DidResolve(exists, file_info);
- }
-
- URLRequestFileJob* owner_;
-
- base::Lock lock_;
- MessageLoop* owner_loop_;
-};
+URLRequestFileJob::FileMetaInfo::FileMetaInfo()
+ : file_size(0),
+ mime_type_result(false),
+ file_exists(false),
+ is_directory(false) {
+}
URLRequestFileJob::URLRequestFileJob(URLRequest* request,
NetworkDelegate* network_delegate,
const FilePath& file_path)
: URLRequestJob(request, network_delegate),
file_path_(file_path),
- is_directory_(false),
- remaining_bytes_(0) {
+ stream_(new FileStream(NULL)),
+ remaining_bytes_(0),
+ weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
}
// static
@@ -124,21 +91,20 @@ URLRequestJob* URLRequestFileJob::Factory(URLRequest* request,
}
void URLRequestFileJob::Start() {
- DCHECK(!async_resolver_);
- async_resolver_ = new AsyncResolver(this);
- base::WorkerPool::PostTask(
+ FileMetaInfo* meta_info = new FileMetaInfo();
+ base::WorkerPool::PostTaskAndReply(
FROM_HERE,
- base::Bind(&AsyncResolver::Resolve, async_resolver_.get(), file_path_),
+ base::Bind(&URLRequestFileJob::FetchMetaInfo, file_path_,
+ base::Unretained(meta_info)),
+ base::Bind(&URLRequestFileJob::DidFetchMetaInfo,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Owned(meta_info)),
true);
}
void URLRequestFileJob::Kill() {
stream_.reset();
-
- if (async_resolver_) {
- async_resolver_->Cancel();
- async_resolver_ = NULL;
- }
+ weak_ptr_factory_.InvalidateWeakPtrs();
URLRequestJob::Kill();
}
@@ -161,7 +127,7 @@ bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size,
int rv = stream_->Read(dest, dest_size,
base::Bind(&URLRequestFileJob::DidRead,
- base::Unretained(this)));
+ weak_ptr_factory_.GetWeakPtr()));
if (rv >= 0) {
// Data is immediately available.
*bytes_read = rv;
@@ -181,7 +147,7 @@ bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size,
bool URLRequestFileJob::IsRedirectResponse(GURL* location,
int* http_status_code) {
- if (is_directory_) {
+ if (meta_info_.is_directory) {
// This happens when we discovered the file is a directory, so needs a
// slash at the end of the path.
std::string new_path = request_->url().path();
@@ -223,12 +189,12 @@ Filter* URLRequestFileJob::SetupFilter() const {
}
bool URLRequestFileJob::GetMimeType(std::string* mime_type) const {
- // URL requests should not block on the disk! On Windows this goes to the
- // registry.
- // http://code.google.com/p/chromium/issues/detail?id=59849
- base::ThreadRestrictions::ScopedAllowIO allow_io;
DCHECK(request_);
- return GetMimeTypeFromFile(file_path_, mime_type);
+ if (meta_info_.mime_type_result) {
+ *mime_type = meta_info_.mime_type;
+ return true;
+ }
+ return false;
}
void URLRequestFileJob::SetExtraRequestHeaders(
@@ -253,20 +219,25 @@ void URLRequestFileJob::SetExtraRequestHeaders(
}
URLRequestFileJob::~URLRequestFileJob() {
- DCHECK(!async_resolver_);
}
-void URLRequestFileJob::DidResolve(
- bool exists, const base::PlatformFileInfo& file_info) {
- async_resolver_ = NULL;
-
- // We may have been orphaned...
- if (!request_)
- return;
+void URLRequestFileJob::FetchMetaInfo(const FilePath& file_path,
+ FileMetaInfo* meta_info) {
+ base::PlatformFileInfo platform_info;
+ meta_info->file_exists = file_util::GetFileInfo(file_path, &platform_info);
+ if (meta_info->file_exists) {
+ meta_info->file_size = platform_info.size;
+ meta_info->is_directory = platform_info.is_directory;
+ }
+ // On Windows GetMimeTypeFromFile() goes to the registry. Thus it should be
+ // done in WorkerPool.
+ meta_info->mime_type_result = GetMimeTypeFromFile(file_path,
+ &meta_info->mime_type);
+}
- is_directory_ = file_info.is_directory;
+void URLRequestFileJob::DidFetchMetaInfo(const FileMetaInfo* meta_info) {
+ meta_info_ = *meta_info;
- int rv = OK;
// We use URLRequestFileJob to handle files as well as directories without
// trailing slash.
// If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise,
@@ -275,27 +246,32 @@ void URLRequestFileJob::DidResolve(
// However, Windows resolves "\" to "C:\", thus reports it as existent.
// So what happens is we append it with trailing slash and redirect it to
// FileDirJob where it is resolved as invalid.
- if (!exists) {
- rv = ERR_FILE_NOT_FOUND;
- } else if (!is_directory_) {
- stream_.reset(new FileStream(NULL));
-
- // URL requests should not block on the disk!
- // http://code.google.com/p/chromium/issues/detail?id=59849
- base::ThreadRestrictions::ScopedAllowIO allow_io;
-
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_ASYNC;
- rv = stream_->OpenSync(file_path_, flags);
+ if (!meta_info_.file_exists) {
+ DidOpen(ERR_FILE_NOT_FOUND);
+ return;
+ }
+ if (meta_info_.is_directory) {
+ DidOpen(OK);
+ return;
}
- if (rv != OK) {
- NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
+ int flags = base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_ASYNC;
+ int rv = stream_->Open(file_path_, flags,
+ base::Bind(&URLRequestFileJob::DidOpen,
+ weak_ptr_factory_.GetWeakPtr()));
+ if (rv != ERR_IO_PENDING)
+ DidOpen(rv);
+}
+
+void URLRequestFileJob::DidOpen(int result) {
+ if (result != OK) {
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
return;
}
- if (!byte_range_.ComputeBounds(file_info.size)) {
+ if (!byte_range_.ComputeBounds(meta_info_.file_size)) {
NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
ERR_REQUEST_RANGE_NOT_SATISFIABLE));
return;
@@ -305,19 +281,28 @@ void URLRequestFileJob::DidResolve(
byte_range_.first_byte_position() + 1;
DCHECK_GE(remaining_bytes_, 0);
- // URL requests should not block on the disk!
- // http://code.google.com/p/chromium/issues/detail?id=59849
- {
- base::ThreadRestrictions::ScopedAllowIO allow_io;
- // Do the seek at the beginning of the request.
- if (remaining_bytes_ > 0 &&
- byte_range_.first_byte_position() != 0 &&
- byte_range_.first_byte_position() !=
- stream_->SeekSync(FROM_BEGIN, byte_range_.first_byte_position())) {
- NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
- ERR_REQUEST_RANGE_NOT_SATISFIABLE));
- return;
+ if (remaining_bytes_ > 0 && byte_range_.first_byte_position() != 0) {
+ int rv = stream_->Seek(FROM_BEGIN, byte_range_.first_byte_position(),
+ base::Bind(&URLRequestFileJob::DidSeek,
+ weak_ptr_factory_.GetWeakPtr()));
+ if (rv != ERR_IO_PENDING) {
+ // stream_->Seek() failed, so pass an intentionally erroneous value
+ // into DidSeek().
+ DidSeek(-1);
}
+ } else {
+ // We didn't need to call stream_->Seek() at all, so we pass to DidSeek()
+ // the value that would mean seek success. This way we skip the code
+ // handling seek failure.
+ DidSeek(byte_range_.first_byte_position());
+ }
+}
+
+void URLRequestFileJob::DidSeek(int64 result) {
+ if (result != byte_range_.first_byte_position()) {
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
+ ERR_REQUEST_RANGE_NOT_SATISFIABLE));
+ return;
}
set_expected_content_size(remaining_bytes_);
diff --git a/net/url_request/url_request_file_job.h b/net/url_request/url_request_file_job.h
index 15c7d62..9c2199c 100644
--- a/net/url_request/url_request_file_job.h
+++ b/net/url_request/url_request_file_job.h
@@ -9,6 +9,7 @@
#include <vector>
#include "base/file_path.h"
+#include "base/memory/weak_ptr.h"
#include "net/base/net_export.h"
#include "net/http/http_byte_range.h"
#include "net/url_request/url_request.h"
@@ -54,23 +55,49 @@ class NET_EXPORT URLRequestFileJob : public URLRequestJob {
FilePath file_path_;
private:
+ // Meta information about the file. It's used as a member in the
+ // URLRequestFileJob and also passed between threads because disk access is
+ // necessary to obtain it.
+ struct FileMetaInfo {
+ FileMetaInfo();
+
+ // Size of the file.
+ int64 file_size;
+ // Mime type associated with the file.
+ std::string mime_type;
+ // Result returned from GetMimeTypeFromFile(), i.e. flag showing whether
+ // obtaining of the mime type was successful.
+ bool mime_type_result;
+ // Flag showing whether the file exists.
+ bool file_exists;
+ // Flag showing whether the file name actually refers to a directory.
+ bool is_directory;
+ };
+
+ // Fetches file info on a background thread.
+ static void FetchMetaInfo(const FilePath& file_path,
+ FileMetaInfo* meta_info);
+
// Callback after fetching file info on a background thread.
- void DidResolve(bool exists, const base::PlatformFileInfo& file_info);
+ void DidFetchMetaInfo(const FileMetaInfo* meta_info);
+
+ // Callback after opening file on a background thread.
+ void DidOpen(int result);
+
+ // Callback after seeking to the beginning of |byte_range_| in the file
+ // on a background thread.
+ void DidSeek(int64 result);
// Callback after data is asynchronously read from the file.
void DidRead(int result);
scoped_ptr<FileStream> stream_;
- bool is_directory_;
+ FileMetaInfo meta_info_;
HttpByteRange byte_range_;
int64 remaining_bytes_;
- // The initial file metadata is fetched on a background thread.
- // AsyncResolver runs that task.
- class AsyncResolver;
- friend class AsyncResolver;
- scoped_refptr<AsyncResolver> async_resolver_;
+ base::WeakPtrFactory<URLRequestFileJob> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(URLRequestFileJob);
};
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 5486a90..57198c5 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -563,6 +563,24 @@ TEST_F(URLRequestTest, FileTest) {
}
}
+TEST_F(URLRequestTest, FileTestCancel) {
+ FilePath app_path;
+ PathService::Get(base::FILE_EXE, &app_path);
+ GURL app_url = FilePathToFileURL(app_path);
+
+ TestDelegate d;
+ {
+ URLRequest r(app_url, &d, &default_context_);
+
+ r.Start();
+ EXPECT_TRUE(r.is_pending());
+ r.Cancel();
+ }
+ // Async cancelation should be safe even when URLRequest has been already
+ // destroyed.
+ MessageLoop::current()->RunUntilIdle();
+}
+
TEST_F(URLRequestTest, FileTestFullSpecifiedRange) {
const size_t buffer_size = 4000;
scoped_array<char> buffer(new char[buffer_size]);