diff options
author | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-26 04:40:59 +0000 |
---|---|---|
committer | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-26 04:40:59 +0000 |
commit | c4388a32fd9fc6bc8975c61685a7a21bba2e1fd7 (patch) | |
tree | 93125ce67b43a5fe1338cb1bd55f06668fdbc9e0 /webkit | |
parent | 3da61e534205ebd44bbaa6d7416b6253ef627fcf (diff) | |
download | chromium_src-c4388a32fd9fc6bc8975c61685a7a21bba2e1fd7.zip chromium_src-c4388a32fd9fc6bc8975c61685a7a21bba2e1fd7.tar.gz chromium_src-c4388a32fd9fc6bc8975c61685a7a21bba2e1fd7.tar.bz2 |
Add ScopedFile class which supports scope-out deletion and/or callbacks
and re-implement ShareableFileReference using ScopedFile.
A follow-up change to change existing code to use the new
ScopedFile is uploaded here:
https://codereview.chromium.org/14075016/
BUG=162598
Review URL: https://codereview.chromium.org/14261015
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@196611 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r-- | webkit/blob/scoped_file.cc | 83 | ||||
-rw-r--r-- | webkit/blob/scoped_file.h | 94 | ||||
-rw-r--r-- | webkit/blob/shareable_file_reference.cc | 68 | ||||
-rw-r--r-- | webkit/blob/shareable_file_reference.h | 58 | ||||
-rw-r--r-- | webkit/blob/webkit_blob.gypi | 2 |
5 files changed, 238 insertions, 67 deletions
diff --git a/webkit/blob/scoped_file.cc b/webkit/blob/scoped_file.cc new file mode 100644 index 0000000..72e0982 --- /dev/null +++ b/webkit/blob/scoped_file.cc @@ -0,0 +1,83 @@ +// Copyright (c) 2013 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 "webkit/blob/scoped_file.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/file_util_proxy.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/task_runner.h" + +namespace webkit_blob { + +ScopedFile::ScopedFile() + : scope_out_policy_(DONT_DELETE_ON_SCOPE_OUT) { +} + +ScopedFile::ScopedFile( + const base::FilePath& path, ScopeOutPolicy policy, + base::TaskRunner* file_task_runner) + : path_(path), + scope_out_policy_(policy), + file_task_runner_(file_task_runner) { + DCHECK(path.empty() || policy != DELETE_ON_SCOPE_OUT || file_task_runner) + << "path:" << path.value() + << " policy:" << policy + << " runner:" << file_task_runner; +} + +ScopedFile::ScopedFile(RValue other) { + MoveFrom(*other.object); +} + +ScopedFile::~ScopedFile() { + Reset(); +} + +void ScopedFile::AddScopeOutCallback( + const ScopeOutCallback& callback, + base::TaskRunner* callback_runner) { + if (!callback_runner) + callback_runner = base::MessageLoopProxy::current(); + scope_out_callbacks_.push_back(std::make_pair(callback, callback_runner)); +} + +base::FilePath ScopedFile::Release() { + base::FilePath path = path_; + path_.clear(); + scope_out_callbacks_.clear(); + scope_out_policy_ = DONT_DELETE_ON_SCOPE_OUT; + return path; +} + +void ScopedFile::Reset() { + if (path_.empty()) + return; + + for (ScopeOutCallbackList::iterator iter = scope_out_callbacks_.begin(); + iter != scope_out_callbacks_.end(); ++iter) { + iter->second->PostTask(FROM_HERE, base::Bind(iter->first, path_)); + } + + if (scope_out_policy_ == DELETE_ON_SCOPE_OUT) { + base::FileUtilProxy::Delete(file_task_runner_, path_, false /* recursive */, + base::FileUtilProxy::StatusCallback()); + } + + // Clear all fields. + Release(); +} + +void ScopedFile::MoveFrom(ScopedFile& other) { + Reset(); + + scope_out_policy_ = other.scope_out_policy_; + scope_out_callbacks_.swap(other.scope_out_callbacks_); + file_task_runner_ = other.file_task_runner_; + path_ = other.Release(); +} + +} // namespace webkit_blob diff --git a/webkit/blob/scoped_file.h b/webkit/blob/scoped_file.h new file mode 100644 index 0000000..3d68c40 --- /dev/null +++ b/webkit/blob/scoped_file.h @@ -0,0 +1,94 @@ +// Copyright (c) 2013 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. + +#ifndef WEBKIT_BLOB_SCOPED_FILE_H_ +#define WEBKIT_BLOB_SCOPED_FILE_H_ + +#include <map> + +#include "base/callback_forward.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/move.h" +#include "webkit/storage/webkit_storage_export.h" + +namespace base { +class TaskRunner; +} + +namespace webkit_blob { + +// A scoped reference for a FilePath that can optionally schedule the file +// to be deleted and/or to notify a consumer when it is going to be scoped out. +// This class supports move semantics, i.e. consumers can call Pass() to +// pass the ownership of ScopedFile. +// +// TODO(kinuko): Probably this can be moved under base or somewhere more +// common place. +class WEBKIT_STORAGE_EXPORT ScopedFile { + // To support destructive assignment from an l-value assignment. + // This provides Pass() method which creates an r-value for the current + // instance. (See base/move.h for details) + MOVE_ONLY_TYPE_FOR_CPP_03(ScopedFile, RValue) + + public: + typedef base::Callback<void(const base::FilePath&)> ScopeOutCallback; + typedef std::pair<ScopeOutCallback, scoped_refptr<base::TaskRunner> > + ScopeOutCallbackPair; + typedef std::vector<ScopeOutCallbackPair> ScopeOutCallbackList; + + enum ScopeOutPolicy { + DELETE_ON_SCOPE_OUT, + DONT_DELETE_ON_SCOPE_OUT, + }; + + ScopedFile(); + + // |file_task_runner| is used to schedule a file deletion if |policy| + // is DELETE_ON_SCOPE_OUT. + ScopedFile(const base::FilePath& path, + ScopeOutPolicy policy, + base::TaskRunner* file_task_runner); + + // Move constructor and operator. The data of r-value will be transfered + // in a destructive way. (See base/move.h) + ScopedFile(RValue other); + ScopedFile& operator=(RValue rhs) { + MoveFrom(*rhs.object); + return *this; + } + + ~ScopedFile(); + + // The |callback| is fired on |callback_runner| when the final reference + // of this instance is released. + // If release policy is DELETE_ON_SCOPE_OUT the + // callback task(s) is/are posted before the deletion is scheduled. + void AddScopeOutCallback(const ScopeOutCallback& callback, + base::TaskRunner* callback_runner); + + // The full file path. + const base::FilePath& path() const { return path_; } + + // Releases the file. After calling this, this instance will hold + // an empty file path and scoping out won't make any file deletion + // or callback dispatch. (If an owned pointer is attached to any of + // callbacks the pointer will be deleted.) + base::FilePath Release(); + + void Reset(); + + private: + // Performs destructive move from |other| to this. + void MoveFrom(ScopedFile& other); + + base::FilePath path_; + ScopeOutPolicy scope_out_policy_; + scoped_refptr<base::TaskRunner> file_task_runner_; + ScopeOutCallbackList scope_out_callbacks_; +}; + +} // namespace webkit_blob + +#endif // WEBKIT_BLOB_SCOPED_FILE_H_ diff --git a/webkit/blob/shareable_file_reference.cc b/webkit/blob/shareable_file_reference.cc index 07acac3..fd75917 100644 --- a/webkit/blob/shareable_file_reference.cc +++ b/webkit/blob/shareable_file_reference.cc @@ -6,22 +6,17 @@ #include <map> -#include "base/file_util.h" -#include "base/files/file_util_proxy.h" #include "base/lazy_instance.h" +#include "base/message_loop/message_loop_proxy.h" #include "base/task_runner.h" -#include "base/threading/thread_checker.h" +#include "base/threading/non_thread_safe.h" namespace webkit_blob { namespace { // A shareable file map with enforcement of thread checker. -// This map may get deleted on a different thread in AtExitManager at the -// very end on the main thread (at the point all other threads must be -// terminated), so we use ThreadChecker rather than NonThreadSafe and do not -// check thread in the dtor. -class ShareableFileMap { +class ShareableFileMap : public base::NonThreadSafe { public: typedef std::map<base::FilePath, ShareableFileReference*> FileMap; typedef FileMap::iterator iterator; @@ -30,6 +25,10 @@ class ShareableFileMap { ShareableFileMap() {} + ~ShareableFileMap() { + DetachFromThread(); + } + iterator Find(key_type key) { DCHECK(CalledOnValidThread()); return file_map_.find(key); @@ -50,13 +49,8 @@ class ShareableFileMap { file_map_.erase(key); } - bool CalledOnValidThread() const { - return thread_checker_.CalledOnValidThread(); - } - private: FileMap file_map_; - base::ThreadChecker thread_checker_; DISALLOW_COPY_AND_ASSIGN(ShareableFileMap); }; @@ -75,21 +69,33 @@ scoped_refptr<ShareableFileReference> ShareableFileReference::Get( // static scoped_refptr<ShareableFileReference> ShareableFileReference::GetOrCreate( - const base::FilePath& path, FinalReleasePolicy policy, + const base::FilePath& path, + FinalReleasePolicy policy, base::TaskRunner* file_task_runner) { - DCHECK(file_task_runner); - typedef std::pair<ShareableFileMap::iterator, bool> InsertResult; + return GetOrCreate( + ScopedFile(path, static_cast<ScopedFile::ScopeOutPolicy>(policy), + file_task_runner)); +} + +// static +scoped_refptr<ShareableFileReference> ShareableFileReference::GetOrCreate( + ScopedFile scoped_file) { + if (scoped_file.path().empty()) + return scoped_refptr<ShareableFileReference>(); + typedef std::pair<ShareableFileMap::iterator, bool> InsertResult; // Required for VS2010: http://connect.microsoft.com/VisualStudio/feedback/details/520043/error-converting-from-null-to-a-pointer-type-in-std-pair webkit_blob::ShareableFileReference* null_reference = NULL; InsertResult result = g_file_map.Get().Insert( - ShareableFileMap::value_type(path, null_reference)); - if (result.second == false) + ShareableFileMap::value_type(scoped_file.path(), null_reference)); + if (result.second == false) { + scoped_file.Release(); return scoped_refptr<ShareableFileReference>(result.first->second); + } // Wasn't in the map, create a new reference and store the pointer. scoped_refptr<ShareableFileReference> reference( - new ShareableFileReference(path, policy, file_task_runner)); + new ShareableFileReference(scoped_file.Pass())); result.first->second = reference.get(); return reference; } @@ -97,29 +103,17 @@ scoped_refptr<ShareableFileReference> ShareableFileReference::GetOrCreate( void ShareableFileReference::AddFinalReleaseCallback( const FinalReleaseCallback& callback) { DCHECK(g_file_map.Get().CalledOnValidThread()); - final_release_callbacks_.push_back(callback); + scoped_file_.AddScopeOutCallback(callback, NULL); } -ShareableFileReference::ShareableFileReference( - const base::FilePath& path, FinalReleasePolicy policy, - base::TaskRunner* file_task_runner) - : path_(path), - final_release_policy_(policy), - file_task_runner_(file_task_runner) { - DCHECK(g_file_map.Get().Find(path_)->second == NULL); +ShareableFileReference::ShareableFileReference(ScopedFile scoped_file) + : scoped_file_(scoped_file.Pass()) { + DCHECK(g_file_map.Get().Find(path())->second == NULL); } ShareableFileReference::~ShareableFileReference() { - DCHECK(g_file_map.Get().Find(path_)->second == this); - g_file_map.Get().Erase(path_); - - for (size_t i = 0; i < final_release_callbacks_.size(); i++) - final_release_callbacks_[i].Run(path_); - - if (final_release_policy_ == DELETE_ON_FINAL_RELEASE) { - base::FileUtilProxy::Delete(file_task_runner_, path_, false /* recursive */, - base::FileUtilProxy::StatusCallback()); - } + DCHECK(g_file_map.Get().Find(path())->second == this); + g_file_map.Get().Erase(path()); } } // namespace webkit_blob diff --git a/webkit/blob/shareable_file_reference.h b/webkit/blob/shareable_file_reference.h index 1eaff08..eb600ec 100644 --- a/webkit/blob/shareable_file_reference.h +++ b/webkit/blob/shareable_file_reference.h @@ -7,29 +7,22 @@ #include <vector> -#include "base/callback.h" -#include "base/files/file_path.h" -#include "base/memory/ref_counted.h" -#include "webkit/storage/webkit_storage_export.h" - -namespace base { -class TaskRunner; -} +#include "webkit/blob/scoped_file.h" namespace webkit_blob { -// A refcounted wrapper around a FilePath that can optionally schedule -// the file to be deleted upon final release and/or to notify a consumer -// when final release occurs. This class is single-threaded and should -// only be invoked on the IO thread in chrome. +// ShareableFileReference allows consumers to share FileReference for the +// same path if it already exists in its internal map. +// This class is non-thread-safe and all methods must be called on a single +// thread. class WEBKIT_STORAGE_EXPORT ShareableFileReference : public base::RefCounted<ShareableFileReference> { public: - typedef base::Callback<void(const base::FilePath&)> FinalReleaseCallback; + typedef ScopedFile::ScopeOutCallback FinalReleaseCallback; enum FinalReleasePolicy { - DELETE_ON_FINAL_RELEASE, - DONT_DELETE_ON_FINAL_RELEASE, + DELETE_ON_FINAL_RELEASE = ScopedFile::DELETE_ON_SCOPE_OUT, + DONT_DELETE_ON_FINAL_RELEASE = ScopedFile::DONT_DELETE_ON_SCOPE_OUT, }; // Returns a ShareableFileReference for the given path, if no reference @@ -38,35 +31,40 @@ class WEBKIT_STORAGE_EXPORT ShareableFileReference // Returns a ShareableFileReference for the given path, creating a new // reference if none yet exists. If there's a pre-existing reference for - // the path, the deletable parameter of this method is ignored. + // the path, the policy parameter of this method is ignored. static scoped_refptr<ShareableFileReference> GetOrCreate( const base::FilePath& path, FinalReleasePolicy policy, base::TaskRunner* file_task_runner); - // The full file path. - const base::FilePath& path() const { return path_; } + // Returns a ShareableFileReference for the given path of the |scoped_file|, + // creating a new reference if none yet exists. The ownership of |scoped_file| + // is passed to this reference. + // If there's a pre-existing reference for the path, the scope out policy + // and scope-out-callbacks of the given |scoped_file| is ignored. + // If the given scoped_file has an empty path (e.g. maybe already + // released) this returns NULL reference. + // + // TODO(kinuko): Make sure if this behavior is ok, we could alternatively + // merge callbacks to the existing one. + static scoped_refptr<ShareableFileReference> GetOrCreate( + ScopedFile scoped_file); - // Whether it's to be deleted on final release. - FinalReleasePolicy final_release_policy() const { - return final_release_policy_; - } + // The full file path. + const base::FilePath& path() const { return scoped_file_.path(); } + // The |callback| is fired when the final reference of this instance + // is released. If release policy is DELETE_ON_FINAL_RELEASE the + // callback task(s) is/are posted before the deletion is scheduled. void AddFinalReleaseCallback(const FinalReleaseCallback& callback); private: friend class base::RefCounted<ShareableFileReference>; - ShareableFileReference( - const base::FilePath& path, - FinalReleasePolicy policy, - base::TaskRunner* file_task_runner); + ShareableFileReference(ScopedFile scoped_file); ~ShareableFileReference(); - const base::FilePath path_; - const FinalReleasePolicy final_release_policy_; - const scoped_refptr<base::TaskRunner> file_task_runner_; - std::vector<FinalReleaseCallback> final_release_callbacks_; + ScopedFile scoped_file_; DISALLOW_COPY_AND_ASSIGN(ShareableFileReference); }; diff --git a/webkit/blob/webkit_blob.gypi b/webkit/blob/webkit_blob.gypi index e15e4b0..afb45d7 100644 --- a/webkit/blob/webkit_blob.gypi +++ b/webkit/blob/webkit_blob.gypi @@ -15,6 +15,8 @@ '../blob/blob_url_request_job_factory.h', '../blob/local_file_stream_reader.cc', '../blob/local_file_stream_reader.h', + '../blob/scoped_file.cc', + '../blob/scoped_file.h', '../blob/shareable_file_reference.cc', '../blob/shareable_file_reference.h', '../blob/view_blob_internals_job.cc', |