| Index: net/base/directory_lister.cc
|
| diff --git a/net/base/directory_lister.cc b/net/base/directory_lister.cc
|
| index 841f454c814bafa3865fc92c69304f75f2a36aa4..f746f9b2e476576098d8d5ea9c7400c4674c5ecb 100644
|
| --- a/net/base/directory_lister.cc
|
| +++ b/net/base/directory_lister.cc
|
| @@ -5,12 +5,12 @@
|
| #include "net/base/directory_lister.h"
|
|
|
| #include <algorithm>
|
| -#include <vector>
|
|
|
| #include "base/bind.h"
|
| #include "base/files/file_enumerator.h"
|
| #include "base/files/file_util.h"
|
| #include "base/i18n/file_util_icu.h"
|
| +#include "base/logging.h"
|
| #include "base/message_loop/message_loop.h"
|
| #include "base/threading/thread_restrictions.h"
|
| #include "base/threading/worker_pool.h"
|
| @@ -73,22 +73,23 @@ void SortData(std::vector<DirectoryLister::DirectoryListerData>* data,
|
| DirectoryLister::SortType sort_type) {
|
| // Sort the results. See the TODO below (this sort should be removed and we
|
| // should do it from JS).
|
| - if (sort_type == DirectoryLister::DATE)
|
| + if (sort_type == DirectoryLister::DATE) {
|
| std::sort(data->begin(), data->end(), CompareDate);
|
| - else if (sort_type == DirectoryLister::FULL_PATH)
|
| + } else if (sort_type == DirectoryLister::FULL_PATH) {
|
| std::sort(data->begin(), data->end(), CompareFullPath);
|
| - else if (sort_type == DirectoryLister::ALPHA_DIRS_FIRST)
|
| + } else if (sort_type == DirectoryLister::ALPHA_DIRS_FIRST) {
|
| std::sort(data->begin(), data->end(), CompareAlphaDirsFirst);
|
| - else
|
| + } else {
|
| DCHECK_EQ(DirectoryLister::NO_SORT, sort_type);
|
| + }
|
| }
|
|
|
| } // namespace
|
|
|
| DirectoryLister::DirectoryLister(const base::FilePath& dir,
|
| DirectoryListerDelegate* delegate)
|
| - : core_(new Core(dir, false, ALPHA_DIRS_FIRST, this)),
|
| - delegate_(delegate) {
|
| + : delegate_(delegate) {
|
| + core_ = new Core(dir, false, ALPHA_DIRS_FIRST, this);
|
| DCHECK(delegate_);
|
| DCHECK(!dir.value().empty());
|
| }
|
| @@ -97,8 +98,8 @@ DirectoryLister::DirectoryLister(const base::FilePath& dir,
|
| bool recursive,
|
| SortType sort,
|
| DirectoryListerDelegate* delegate)
|
| - : core_(new Core(dir, recursive, sort, this)),
|
| - delegate_(delegate) {
|
| + : delegate_(delegate) {
|
| + core_ = new Core(dir, recursive, sort, this);
|
| DCHECK(delegate_);
|
| DCHECK(!dir.value().empty());
|
| }
|
| @@ -108,11 +109,14 @@ DirectoryLister::~DirectoryLister() {
|
| }
|
|
|
| bool DirectoryLister::Start() {
|
| - return core_->Start();
|
| + return base::WorkerPool::PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&Core::Start, core_),
|
| + true);
|
| }
|
|
|
| void DirectoryLister::Cancel() {
|
| - return core_->Cancel();
|
| + core_->CancelOnOriginThread();
|
| }
|
|
|
| DirectoryLister::Core::Core(const base::FilePath& dir,
|
| @@ -122,29 +126,32 @@ DirectoryLister::Core::Core(const base::FilePath& dir,
|
| : dir_(dir),
|
| recursive_(recursive),
|
| sort_(sort),
|
| - lister_(lister) {
|
| + origin_loop_(base::MessageLoopProxy::current()),
|
| + lister_(lister),
|
| + cancelled_(0) {
|
| DCHECK(lister_);
|
| }
|
|
|
| DirectoryLister::Core::~Core() {}
|
|
|
| -bool DirectoryLister::Core::Start() {
|
| - origin_loop_ = base::MessageLoopProxy::current();
|
| -
|
| - return base::WorkerPool::PostTask(
|
| - FROM_HERE, base::Bind(&Core::StartInternal, this), true);
|
| -}
|
| +void DirectoryLister::Core::CancelOnOriginThread() {
|
| + DCHECK(origin_loop_->BelongsToCurrentThread());
|
|
|
| -void DirectoryLister::Core::Cancel() {
|
| - lister_ = NULL;
|
| + base::subtle::NoBarrier_Store(&cancelled_, 1);
|
| + // Core must not call into |lister_| after cancellation, as the |lister_| may
|
| + // have been destroyed. Setting |lister_| to NULL ensures any such access will
|
| + // cause a crash.
|
| + lister_ = nullptr;
|
| }
|
|
|
| -void DirectoryLister::Core::StartInternal() {
|
| +void DirectoryLister::Core::Start() {
|
| + scoped_ptr<DirectoryList> directory_list(new DirectoryList());
|
|
|
| if (!base::DirectoryExists(dir_)) {
|
| origin_loop_->PostTask(
|
| FROM_HERE,
|
| - base::Bind(&DirectoryLister::Core::OnDone, this, ERR_FILE_NOT_FOUND));
|
| + base::Bind(&Core::DoneOnOriginThread, this,
|
| + base::Passed(directory_list.Pass()), ERR_FILE_NOT_FOUND));
|
| return;
|
| }
|
|
|
| @@ -155,12 +162,16 @@ void DirectoryLister::Core::StartInternal() {
|
| base::FileEnumerator file_enum(dir_, recursive_, types);
|
|
|
| base::FilePath path;
|
| - std::vector<DirectoryListerData> file_data;
|
| - while (lister_ && !(path = file_enum.Next()).empty()) {
|
| + while (!(path = file_enum.Next()).empty()) {
|
| + // Abort on cancellation. This is purely for performance reasons.
|
| + // Correctness guarantees are made by checks in DoneOnOriginThread.
|
| + if (IsCancelled())
|
| + return;
|
| +
|
| DirectoryListerData data;
|
| data.info = file_enum.GetInfo();
|
| data.path = path;
|
| - file_data.push_back(data);
|
| + directory_list->push_back(data);
|
|
|
| /* TODO(brettw) bug 24107: It would be nice to send incremental updates.
|
| We gather them all so they can be sorted, but eventually the sorting
|
| @@ -178,36 +189,40 @@ void DirectoryLister::Core::StartInternal() {
|
| */
|
| }
|
|
|
| - SortData(&file_data, sort_);
|
| - origin_loop_->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&DirectoryLister::Core::SendData, this, file_data));
|
| + SortData(directory_list.get(), sort_);
|
|
|
| origin_loop_->PostTask(
|
| FROM_HERE,
|
| - base::Bind(&DirectoryLister::Core::OnDone, this, OK));
|
| + base::Bind(&Core::DoneOnOriginThread, this,
|
| + base::Passed(directory_list.Pass()), OK));
|
| }
|
|
|
| -void DirectoryLister::Core::SendData(
|
| - const std::vector<DirectoryLister::DirectoryListerData>& data) {
|
| - DCHECK(origin_loop_->BelongsToCurrentThread());
|
| - // We need to check for cancellation (indicated by NULL'ing of |lister_|)
|
| - // which can happen during each callback.
|
| - for (size_t i = 0; lister_ && i < data.size(); ++i)
|
| - lister_->OnReceivedData(data[i]);
|
| +bool DirectoryLister::Core::IsCancelled() const {
|
| + return !!base::subtle::NoBarrier_Load(&cancelled_);
|
| }
|
|
|
| -void DirectoryLister::Core::OnDone(int error) {
|
| +void DirectoryLister::Core::DoneOnOriginThread(
|
| + scoped_ptr<DirectoryList> directory_list, int error) const {
|
| DCHECK(origin_loop_->BelongsToCurrentThread());
|
| - if (lister_)
|
| - lister_->OnDone(error);
|
| +
|
| + // Need to check if the operation was before first callback.
|
| + if (IsCancelled())
|
| + return;
|
| +
|
| + for (const auto& lister_data : *directory_list) {
|
| + lister_->OnListFile(lister_data);
|
| + // Need to check if the operation was cancelled during the callback.
|
| + if (IsCancelled())
|
| + return;
|
| + }
|
| + lister_->OnListDone(error);
|
| }
|
|
|
| -void DirectoryLister::OnReceivedData(const DirectoryListerData& data) {
|
| +void DirectoryLister::OnListFile(const DirectoryListerData& data) {
|
| delegate_->OnListFile(data);
|
| }
|
|
|
| -void DirectoryLister::OnDone(int error) {
|
| +void DirectoryLister::OnListDone(int error) {
|
| delegate_->OnListDone(error);
|
| }
|
|
|
|
|