Home | History | Annotate | Download | only in fileapi
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "storage/browser/fileapi/recursive_operation_delegate.h"
      6 
      7 #include "base/bind.h"
      8 #include "storage/browser/fileapi/file_system_context.h"
      9 #include "storage/browser/fileapi/file_system_operation_runner.h"
     10 
     11 namespace storage {
     12 
     13 namespace {
     14 // Don't start too many inflight operations.
     15 const int kMaxInflightOperations = 5;
     16 }
     17 
     18 RecursiveOperationDelegate::RecursiveOperationDelegate(
     19     FileSystemContext* file_system_context)
     20     : file_system_context_(file_system_context),
     21       inflight_operations_(0),
     22       canceled_(false) {
     23 }
     24 
     25 RecursiveOperationDelegate::~RecursiveOperationDelegate() {
     26 }
     27 
     28 void RecursiveOperationDelegate::Cancel() {
     29   canceled_ = true;
     30   OnCancel();
     31 }
     32 
     33 void RecursiveOperationDelegate::StartRecursiveOperation(
     34     const FileSystemURL& root,
     35     const StatusCallback& callback) {
     36   DCHECK(pending_directory_stack_.empty());
     37   DCHECK(pending_files_.empty());
     38   DCHECK_EQ(0, inflight_operations_);
     39 
     40   callback_ = callback;
     41   ++inflight_operations_;
     42   ProcessFile(
     43       root,
     44       base::Bind(&RecursiveOperationDelegate::DidTryProcessFile,
     45                  AsWeakPtr(), root));
     46 }
     47 
     48 FileSystemOperationRunner* RecursiveOperationDelegate::operation_runner() {
     49   return file_system_context_->operation_runner();
     50 }
     51 
     52 void RecursiveOperationDelegate::OnCancel() {
     53 }
     54 
     55 void RecursiveOperationDelegate::DidTryProcessFile(
     56     const FileSystemURL& root,
     57     base::File::Error error) {
     58   DCHECK(pending_directory_stack_.empty());
     59   DCHECK(pending_files_.empty());
     60   DCHECK_EQ(1, inflight_operations_);
     61 
     62   --inflight_operations_;
     63   if (canceled_ || error != base::File::FILE_ERROR_NOT_A_FILE) {
     64     Done(error);
     65     return;
     66   }
     67 
     68   pending_directory_stack_.push(std::queue<FileSystemURL>());
     69   pending_directory_stack_.top().push(root);
     70   ProcessNextDirectory();
     71 }
     72 
     73 void RecursiveOperationDelegate::ProcessNextDirectory() {
     74   DCHECK(pending_files_.empty());
     75   DCHECK(!pending_directory_stack_.empty());
     76   DCHECK(!pending_directory_stack_.top().empty());
     77   DCHECK_EQ(0, inflight_operations_);
     78 
     79   const FileSystemURL& url = pending_directory_stack_.top().front();
     80 
     81   ++inflight_operations_;
     82   ProcessDirectory(
     83       url,
     84       base::Bind(
     85           &RecursiveOperationDelegate::DidProcessDirectory, AsWeakPtr()));
     86 }
     87 
     88 void RecursiveOperationDelegate::DidProcessDirectory(
     89     base::File::Error error) {
     90   DCHECK(pending_files_.empty());
     91   DCHECK(!pending_directory_stack_.empty());
     92   DCHECK(!pending_directory_stack_.top().empty());
     93   DCHECK_EQ(1, inflight_operations_);
     94 
     95   --inflight_operations_;
     96   if (canceled_ || error != base::File::FILE_OK) {
     97     Done(error);
     98     return;
     99   }
    100 
    101   const FileSystemURL& parent = pending_directory_stack_.top().front();
    102   pending_directory_stack_.push(std::queue<FileSystemURL>());
    103   operation_runner()->ReadDirectory(
    104       parent,
    105       base::Bind(&RecursiveOperationDelegate::DidReadDirectory,
    106                  AsWeakPtr(), parent));
    107 }
    108 
    109 void RecursiveOperationDelegate::DidReadDirectory(
    110     const FileSystemURL& parent,
    111     base::File::Error error,
    112     const FileEntryList& entries,
    113     bool has_more) {
    114   DCHECK(!pending_directory_stack_.empty());
    115   DCHECK_EQ(0, inflight_operations_);
    116 
    117   if (canceled_ || error != base::File::FILE_OK) {
    118     Done(error);
    119     return;
    120   }
    121 
    122   for (size_t i = 0; i < entries.size(); i++) {
    123     FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
    124         parent.origin(),
    125         parent.mount_type(),
    126         parent.virtual_path().Append(entries[i].name));
    127     if (entries[i].is_directory)
    128       pending_directory_stack_.top().push(url);
    129     else
    130       pending_files_.push(url);
    131   }
    132 
    133   // Wait for next entries.
    134   if (has_more)
    135     return;
    136 
    137   ProcessPendingFiles();
    138 }
    139 
    140 void RecursiveOperationDelegate::ProcessPendingFiles() {
    141   DCHECK(!pending_directory_stack_.empty());
    142 
    143   if ((pending_files_.empty() || canceled_) && inflight_operations_ == 0) {
    144     ProcessSubDirectory();
    145     return;
    146   }
    147 
    148   // Do not post any new tasks.
    149   if (canceled_)
    150     return;
    151 
    152   // Run ProcessFile in parallel (upto kMaxInflightOperations).
    153   scoped_refptr<base::MessageLoopProxy> current_message_loop =
    154       base::MessageLoopProxy::current();
    155   while (!pending_files_.empty() &&
    156          inflight_operations_ < kMaxInflightOperations) {
    157     ++inflight_operations_;
    158     current_message_loop->PostTask(
    159         FROM_HERE,
    160         base::Bind(&RecursiveOperationDelegate::ProcessFile,
    161                    AsWeakPtr(), pending_files_.front(),
    162                    base::Bind(&RecursiveOperationDelegate::DidProcessFile,
    163                               AsWeakPtr())));
    164     pending_files_.pop();
    165   }
    166 }
    167 
    168 void RecursiveOperationDelegate::DidProcessFile(
    169     base::File::Error error) {
    170   --inflight_operations_;
    171   if (error != base::File::FILE_OK) {
    172     // If an error occurs, invoke Done immediately (even if there remain
    173     // running operations). It is because in the callback, this instance is
    174     // deleted.
    175     Done(error);
    176     return;
    177   }
    178 
    179   ProcessPendingFiles();
    180 }
    181 
    182 void RecursiveOperationDelegate::ProcessSubDirectory() {
    183   DCHECK(pending_files_.empty());
    184   DCHECK(!pending_directory_stack_.empty());
    185   DCHECK_EQ(0, inflight_operations_);
    186 
    187   if (canceled_) {
    188     Done(base::File::FILE_ERROR_ABORT);
    189     return;
    190   }
    191 
    192   if (!pending_directory_stack_.top().empty()) {
    193     // There remain some sub directories. Process them first.
    194     ProcessNextDirectory();
    195     return;
    196   }
    197 
    198   // All subdirectories are processed.
    199   pending_directory_stack_.pop();
    200   if (pending_directory_stack_.empty()) {
    201     // All files/directories are processed.
    202     Done(base::File::FILE_OK);
    203     return;
    204   }
    205 
    206   DCHECK(!pending_directory_stack_.top().empty());
    207   ++inflight_operations_;
    208   PostProcessDirectory(
    209       pending_directory_stack_.top().front(),
    210       base::Bind(&RecursiveOperationDelegate::DidPostProcessDirectory,
    211                  AsWeakPtr()));
    212 }
    213 
    214 void RecursiveOperationDelegate::DidPostProcessDirectory(
    215     base::File::Error error) {
    216   DCHECK(pending_files_.empty());
    217   DCHECK(!pending_directory_stack_.empty());
    218   DCHECK(!pending_directory_stack_.top().empty());
    219   DCHECK_EQ(1, inflight_operations_);
    220 
    221   --inflight_operations_;
    222   pending_directory_stack_.top().pop();
    223   if (canceled_ || error != base::File::FILE_OK) {
    224     Done(error);
    225     return;
    226   }
    227 
    228   ProcessSubDirectory();
    229 }
    230 
    231 void RecursiveOperationDelegate::Done(base::File::Error error) {
    232   if (canceled_ && error == base::File::FILE_OK) {
    233     callback_.Run(base::File::FILE_ERROR_ABORT);
    234   } else {
    235     callback_.Run(error);
    236   }
    237 }
    238 
    239 }  // namespace storage
    240