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