diff options
author | rvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-15 19:50:05 +0000 |
---|---|---|
committer | rvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-15 19:50:05 +0000 |
commit | 39f31bdd23cfcdad842fd63b4f1211d06cd25670 (patch) | |
tree | 628cffe28ef83a55abafcb141198dd02c44c5c5d | |
parent | b5913fd53a6246ea5ac1e00552e2920ed9d26a3c (diff) | |
download | chromium_src-39f31bdd23cfcdad842fd63b4f1211d06cd25670.zip chromium_src-39f31bdd23cfcdad842fd63b4f1211d06cd25670.tar.gz chromium_src-39f31bdd23cfcdad842fd63b4f1211d06cd25670.tar.bz2 |
Merge 243495 "Disk Cache: Use the old WorkerPool for Async IO on..."
> Disk Cache: Use the old WorkerPool for Async IO on iOS.
>
> This basically reverts r222408 for iOS
>
> BUG=330074
> TEST=net_unittests
>
> Review URL: https://codereview.chromium.org/103533012
TBR=rvargas@chromium.org
Review URL: https://codereview.chromium.org/137483004
git-svn-id: svn://svn.chromium.org/chrome/branches/1750/src@244964 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/disk_cache/backend_impl.cc | 2 | ||||
-rw-r--r-- | net/disk_cache/backend_unittest.cc | 9 | ||||
-rw-r--r-- | net/disk_cache/file.h | 3 | ||||
-rw-r--r-- | net/disk_cache/file_ios.cc | 312 | ||||
-rw-r--r-- | net/disk_cache/file_posix.cc | 5 | ||||
-rw-r--r-- | net/disk_cache/file_win.cc | 4 | ||||
-rw-r--r-- | net/net.gyp | 1 |
7 files changed, 335 insertions, 1 deletions
diff --git a/net/disk_cache/backend_impl.cc b/net/disk_cache/backend_impl.cc index a3250ff..9e21317 100644 --- a/net/disk_cache/backend_impl.cc +++ b/net/disk_cache/backend_impl.cc @@ -305,6 +305,8 @@ void BackendImpl::CleanupCache() { // This is a net_unittest, verify that we are not 'leaking' entries. File::WaitForPendingIO(&num_pending_io_); DCHECK(!num_refs_); + } else { + File::DropPendingIO(); } } block_files_.CloseFiles(); diff --git a/net/disk_cache/backend_unittest.cc b/net/disk_cache/backend_unittest.cc index b25001f..f3c02fd 100644 --- a/net/disk_cache/backend_unittest.cc +++ b/net/disk_cache/backend_unittest.cc @@ -518,9 +518,13 @@ void DiskCacheBackendTest::BackendShutdownWithPendingFileIO(bool fast) { base::MessageLoop::current()->RunUntilIdle(); +#if !defined(OS_IOS) // Wait for the actual operation to complete, or we'll keep a file handle that - // may cause issues later. + // may cause issues later. Note that on iOS systems even though this test + // uses a single thread, the actual IO is posted to a worker thread and the + // cache destructor breaks the link to reach cb when the operation completes. rv = cb.GetResult(rv); +#endif } TEST_F(DiskCacheBackendTest, ShutdownWithPendingFileIO) { @@ -543,6 +547,8 @@ TEST_F(DiskCacheBackendTest, ShutdownWithPendingFileIO_Fast) { } #endif +// See crbug.com/330074 +#if !defined(OS_IOS) // Tests that one cache instance is not affected by another one going away. TEST_F(DiskCacheBackendTest, MultipleInstancesWithPendingFileIO) { base::ScopedTempDir store; @@ -576,6 +582,7 @@ TEST_F(DiskCacheBackendTest, MultipleInstancesWithPendingFileIO) { // may cause issues later. rv = cb.GetResult(rv); } +#endif // Tests that we deal with background-thread pending operations. void DiskCacheBackendTest::BackendShutdownWithPendingIO(bool fast) { diff --git a/net/disk_cache/file.h b/net/disk_cache/file.h index eb9a9ec..190f7cb 100644 --- a/net/disk_cache/file.h +++ b/net/disk_cache/file.h @@ -70,6 +70,9 @@ class NET_EXPORT_PRIVATE File : public base::RefCounted<File> { // Blocks until |num_pending_io| IO operations complete. static void WaitForPendingIO(int* num_pending_io); + // Drops current pending operations without waiting for them to complete. + static void DropPendingIO(); + protected: virtual ~File(); diff --git a/net/disk_cache/file_ios.cc b/net/disk_cache/file_ios.cc new file mode 100644 index 0000000..9cad155 --- /dev/null +++ b/net/disk_cache/file_ios.cc @@ -0,0 +1,312 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/disk_cache/file.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/threading/worker_pool.h" +#include "net/base/net_errors.h" +#include "net/disk_cache/disk_cache.h" +#include "net/disk_cache/in_flight_io.h" + +namespace { + +// This class represents a single asynchronous IO operation while it is being +// bounced between threads. +class FileBackgroundIO : public disk_cache::BackgroundIO { + public: + // Other than the actual parameters for the IO operation (including the + // |callback| that must be notified at the end), we need the controller that + // is keeping track of all operations. When done, we notify the controller + // (we do NOT invoke the callback), in the worker thead that completed the + // operation. + FileBackgroundIO(disk_cache::File* file, const void* buf, size_t buf_len, + size_t offset, disk_cache::FileIOCallback* callback, + disk_cache::InFlightIO* controller) + : disk_cache::BackgroundIO(controller), callback_(callback), file_(file), + buf_(buf), buf_len_(buf_len), offset_(offset) { + } + + disk_cache::FileIOCallback* callback() { + return callback_; + } + + disk_cache::File* file() { + return file_; + } + + // Read and Write are the operations that can be performed asynchronously. + // The actual parameters for the operation are setup in the constructor of + // the object. Both methods should be called from a worker thread, by posting + // a task to the WorkerPool (they are RunnableMethods). When finished, + // controller->OnIOComplete() is called. + void Read(); + void Write(); + + private: + virtual ~FileBackgroundIO() {} + + disk_cache::FileIOCallback* callback_; + + disk_cache::File* file_; + const void* buf_; + size_t buf_len_; + size_t offset_; + + DISALLOW_COPY_AND_ASSIGN(FileBackgroundIO); +}; + + +// The specialized controller that keeps track of current operations. +class FileInFlightIO : public disk_cache::InFlightIO { + public: + FileInFlightIO() {} + virtual ~FileInFlightIO() {} + + // These methods start an asynchronous operation. The arguments have the same + // semantics of the File asynchronous operations, with the exception that the + // operation never finishes synchronously. + void PostRead(disk_cache::File* file, void* buf, size_t buf_len, + size_t offset, disk_cache::FileIOCallback* callback); + void PostWrite(disk_cache::File* file, const void* buf, size_t buf_len, + size_t offset, disk_cache::FileIOCallback* callback); + + protected: + // Invokes the users' completion callback at the end of the IO operation. + // |cancel| is true if the actual task posted to the thread is still + // queued (because we are inside WaitForPendingIO), and false if said task is + // the one performing the call. + virtual void OnOperationComplete(disk_cache::BackgroundIO* operation, + bool cancel) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(FileInFlightIO); +}; + +// --------------------------------------------------------------------------- + +// Runs on a worker thread. +void FileBackgroundIO::Read() { + if (file_->Read(const_cast<void*>(buf_), buf_len_, offset_)) { + result_ = static_cast<int>(buf_len_); + } else { + result_ = net::ERR_CACHE_READ_FAILURE; + } + NotifyController(); +} + +// Runs on a worker thread. +void FileBackgroundIO::Write() { + bool rv = file_->Write(buf_, buf_len_, offset_); + + result_ = rv ? static_cast<int>(buf_len_) : net::ERR_CACHE_WRITE_FAILURE; + NotifyController(); +} + +// --------------------------------------------------------------------------- + +void FileInFlightIO::PostRead(disk_cache::File *file, void* buf, size_t buf_len, + size_t offset, disk_cache::FileIOCallback *callback) { + scoped_refptr<FileBackgroundIO> operation( + new FileBackgroundIO(file, buf, buf_len, offset, callback, this)); + file->AddRef(); // Balanced on OnOperationComplete() + + base::WorkerPool::PostTask(FROM_HERE, + base::Bind(&FileBackgroundIO::Read, operation.get()), true); + OnOperationPosted(operation.get()); +} + +void FileInFlightIO::PostWrite(disk_cache::File* file, const void* buf, + size_t buf_len, size_t offset, + disk_cache::FileIOCallback* callback) { + scoped_refptr<FileBackgroundIO> operation( + new FileBackgroundIO(file, buf, buf_len, offset, callback, this)); + file->AddRef(); // Balanced on OnOperationComplete() + + base::WorkerPool::PostTask(FROM_HERE, + base::Bind(&FileBackgroundIO::Write, operation.get()), true); + OnOperationPosted(operation.get()); +} + +// Runs on the IO thread. +void FileInFlightIO::OnOperationComplete(disk_cache::BackgroundIO* operation, + bool cancel) { + FileBackgroundIO* op = static_cast<FileBackgroundIO*>(operation); + + disk_cache::FileIOCallback* callback = op->callback(); + int bytes = operation->result(); + + // Release the references acquired in PostRead / PostWrite. + op->file()->Release(); + callback->OnFileIOComplete(bytes); +} + +// A static object tha will broker all async operations. +FileInFlightIO* s_file_operations = NULL; + +// Returns the current FileInFlightIO. +FileInFlightIO* GetFileInFlightIO() { + if (!s_file_operations) { + s_file_operations = new FileInFlightIO; + } + return s_file_operations; +} + +// Deletes the current FileInFlightIO. +void DeleteFileInFlightIO() { + DCHECK(s_file_operations); + delete s_file_operations; + s_file_operations = NULL; +} + +} // namespace + +namespace disk_cache { + +File::File(base::PlatformFile file) + : init_(true), + mixed_(true), + platform_file_(file), + sync_platform_file_(base::kInvalidPlatformFileValue) { +} + +bool File::Init(const base::FilePath& name) { + if (init_) + return false; + + int flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_WRITE; + platform_file_ = base::CreatePlatformFile(name, flags, NULL, NULL); + if (platform_file_ < 0) { + platform_file_ = 0; + return false; + } + + init_ = true; + return true; +} + +base::PlatformFile File::platform_file() const { + return platform_file_; +} + +bool File::IsValid() const { + if (!init_) + return false; + return (base::kInvalidPlatformFileValue != platform_file_); +} + +bool File::Read(void* buffer, size_t buffer_len, size_t offset) { + DCHECK(init_); + if (buffer_len > static_cast<size_t>(kint32max) || + offset > static_cast<size_t>(kint32max)) { + return false; + } + + int ret = base::ReadPlatformFile(platform_file_, offset, + static_cast<char*>(buffer), buffer_len); + return (static_cast<size_t>(ret) == buffer_len); +} + +bool File::Write(const void* buffer, size_t buffer_len, size_t offset) { + DCHECK(init_); + if (buffer_len > static_cast<size_t>(kint32max) || + offset > static_cast<size_t>(kint32max)) { + return false; + } + + int ret = base::WritePlatformFile(platform_file_, offset, + static_cast<const char*>(buffer), + buffer_len); + return (static_cast<size_t>(ret) == buffer_len); +} + +// We have to increase the ref counter of the file before performing the IO to +// prevent the completion to happen with an invalid handle (if the file is +// closed while the IO is in flight). +bool File::Read(void* buffer, size_t buffer_len, size_t offset, + FileIOCallback* callback, bool* completed) { + DCHECK(init_); + if (!callback) { + if (completed) + *completed = true; + return Read(buffer, buffer_len, offset); + } + + if (buffer_len > ULONG_MAX || offset > ULONG_MAX) + return false; + + GetFileInFlightIO()->PostRead(this, buffer, buffer_len, offset, callback); + + *completed = false; + return true; +} + +bool File::Write(const void* buffer, size_t buffer_len, size_t offset, + FileIOCallback* callback, bool* completed) { + DCHECK(init_); + if (!callback) { + if (completed) + *completed = true; + return Write(buffer, buffer_len, offset); + } + + return AsyncWrite(buffer, buffer_len, offset, callback, completed); +} + +bool File::SetLength(size_t length) { + DCHECK(init_); + if (length > kuint32max) + return false; + + return base::TruncatePlatformFile(platform_file_, length); +} + +size_t File::GetLength() { + DCHECK(init_); + int64 len = base::SeekPlatformFile(platform_file_, + base::PLATFORM_FILE_FROM_END, 0); + + if (len > static_cast<int64>(kuint32max)) + return kuint32max; + + return static_cast<size_t>(len); +} + +// Static. +void File::WaitForPendingIO(int* num_pending_io) { + // We may be running unit tests so we should allow be able to reset the + // message loop. + GetFileInFlightIO()->WaitForPendingIO(); + DeleteFileInFlightIO(); +} + +// Static. +void File::DropPendingIO() { + GetFileInFlightIO()->DropPendingIO(); + DeleteFileInFlightIO(); +} + +File::~File() { + if (IsValid()) + base::ClosePlatformFile(platform_file_); +} + +bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset, + FileIOCallback* callback, bool* completed) { + DCHECK(init_); + if (buffer_len > ULONG_MAX || offset > ULONG_MAX) + return false; + + GetFileInFlightIO()->PostWrite(this, buffer, buffer_len, offset, callback); + + if (completed) + *completed = false; + return true; +} + +} // namespace disk_cache diff --git a/net/disk_cache/file_posix.cc b/net/disk_cache/file_posix.cc index a4d1964..e1ff6fc 100644 --- a/net/disk_cache/file_posix.cc +++ b/net/disk_cache/file_posix.cc @@ -167,6 +167,11 @@ void File::WaitForPendingIO(int* num_pending_io) { base::RunLoop().RunUntilIdle(); } +// Static. +void File::DropPendingIO() { +} + + File::~File() { if (IsValid()) base::ClosePlatformFile(platform_file_); diff --git a/net/disk_cache/file_win.cc b/net/disk_cache/file_win.cc index dbb34f3..1492c42 100644 --- a/net/disk_cache/file_win.cc +++ b/net/disk_cache/file_win.cc @@ -267,4 +267,8 @@ void File::WaitForPendingIO(int* num_pending_io) { } } +// Static. +void File::DropPendingIO() { +} + } // namespace disk_cache diff --git a/net/net.gyp b/net/net.gyp index 29aad0f..8296763 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -376,6 +376,7 @@ 'disk_cache/file.cc', 'disk_cache/file.h', 'disk_cache/file_block.h', + 'disk_cache/file_ios.cc', 'disk_cache/file_lock.cc', 'disk_cache/file_lock.h', 'disk_cache/file_posix.cc', |