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