1 // Copyright 2014 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.
5 #include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
8 #include "base/location.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
11 #include "chrome/browser/sync_file_system/sync_file_metadata.h"
13 using fileapi::FileSystemURL;
15 namespace sync_file_system {
16 namespace drive_backend {
20 class SyncTaskAdapter : public ExclusiveTask {
22 explicit SyncTaskAdapter(const SyncTaskManager::Task& task) : task_(task) {}
23 virtual ~SyncTaskAdapter() {}
25 virtual void RunExclusive(const SyncStatusCallback& callback) OVERRIDE {
30 SyncTaskManager::Task task_;
32 DISALLOW_COPY_AND_ASSIGN(SyncTaskAdapter);
37 SyncTaskManager::PendingTask::PendingTask() {}
39 SyncTaskManager::PendingTask::PendingTask(
40 const base::Closure& task, Priority pri, int seq)
41 : task(task), priority(pri), seq(seq) {}
43 SyncTaskManager::PendingTask::~PendingTask() {}
45 bool SyncTaskManager::PendingTaskComparator::operator()(
46 const PendingTask& left,
47 const PendingTask& right) const {
48 if (left.priority != right.priority)
49 return left.priority < right.priority;
50 return left.seq > right.seq;
53 SyncTaskManager::SyncTaskManager(
54 base::WeakPtr<Client> client,
55 size_t maximum_background_task)
57 maximum_background_task_(maximum_background_task),
59 task_token_seq_(SyncTaskToken::kMinimumBackgroundTaskTokenID) {
62 SyncTaskManager::~SyncTaskManager() {
67 void SyncTaskManager::Initialize(SyncStatusCode status) {
69 NotifyTaskDone(SyncTaskToken::CreateForForegroundTask(AsWeakPtr()),
73 void SyncTaskManager::ScheduleTask(
74 const tracked_objects::Location& from_here,
77 const SyncStatusCallback& callback) {
78 ScheduleSyncTask(from_here,
79 scoped_ptr<SyncTask>(new SyncTaskAdapter(task)),
84 void SyncTaskManager::ScheduleSyncTask(
85 const tracked_objects::Location& from_here,
86 scoped_ptr<SyncTask> task,
88 const SyncStatusCallback& callback) {
89 scoped_ptr<SyncTaskToken> token(GetToken(from_here, callback));
92 base::Bind(&SyncTaskManager::ScheduleSyncTask, AsWeakPtr(), from_here,
93 base::Passed(&task), priority, callback),
97 RunTask(token.Pass(), task.Pass());
100 bool SyncTaskManager::ScheduleTaskIfIdle(
101 const tracked_objects::Location& from_here,
103 const SyncStatusCallback& callback) {
104 return ScheduleSyncTaskIfIdle(
106 scoped_ptr<SyncTask>(new SyncTaskAdapter(task)),
110 bool SyncTaskManager::ScheduleSyncTaskIfIdle(
111 const tracked_objects::Location& from_here,
112 scoped_ptr<SyncTask> task,
113 const SyncStatusCallback& callback) {
114 scoped_ptr<SyncTaskToken> token(GetToken(from_here, callback));
117 RunTask(token.Pass(), task.Pass());
122 void SyncTaskManager::NotifyTaskDone(scoped_ptr<SyncTaskToken> token,
123 SyncStatusCode status) {
126 SyncTaskManager* manager = token->manager();
128 manager->NotifyTaskDoneBody(token.Pass(), status);
132 void SyncTaskManager::UpdateBlockingFactor(
133 scoped_ptr<SyncTaskToken> current_task_token,
134 scoped_ptr<BlockingFactor> blocking_factor,
135 const Continuation& continuation) {
136 DCHECK(current_task_token);
138 SyncTaskManager* manager = current_task_token->manager();
142 scoped_ptr<SyncTaskToken> foreground_task_token;
143 scoped_ptr<SyncTaskToken> background_task_token;
144 if (current_task_token->token_id() == SyncTaskToken::kForegroundTaskTokenID)
145 foreground_task_token = current_task_token.Pass();
147 background_task_token = current_task_token.Pass();
149 manager->UpdateBlockingFactorBody(foreground_task_token.Pass(),
150 background_task_token.Pass(),
151 blocking_factor.Pass(),
155 bool SyncTaskManager::IsRunningTask(int64 token_id) const {
156 // If the client is gone, all task should be aborted.
160 if (token_id == SyncTaskToken::kForegroundTaskTokenID)
163 return ContainsKey(running_background_tasks_, token_id);
166 void SyncTaskManager::NotifyTaskDoneBody(scoped_ptr<SyncTaskToken> token,
167 SyncStatusCode status) {
170 DVLOG(3) << "NotifyTaskDone: " << "finished with status=" << status
171 << " (" << SyncStatusCodeToString(status) << ")"
172 << " " << token_->location().ToString();
174 if (token->blocking_factor()) {
175 dependency_manager_.Erase(token->blocking_factor());
176 token->clear_blocking_factor();
179 scoped_ptr<SyncTask> task;
180 SyncStatusCallback callback = token->callback();
181 token->clear_callback();
182 if (token->token_id() == SyncTaskToken::kForegroundTaskTokenID) {
183 token_ = token.Pass();
184 task = running_foreground_task_.Pass();
186 task = running_background_tasks_.take_and_erase(token->token_id());
189 bool task_used_network = false;
191 task_used_network = task->used_network();
194 client_->NotifyLastOperationStatus(status, task_used_network);
196 if (!callback.is_null())
197 callback.Run(status);
202 void SyncTaskManager::UpdateBlockingFactorBody(
203 scoped_ptr<SyncTaskToken> foreground_task_token,
204 scoped_ptr<SyncTaskToken> background_task_token,
205 scoped_ptr<BlockingFactor> blocking_factor,
206 const Continuation& continuation) {
207 // Run the task directly if the parallelization is disabled.
208 if (!maximum_background_task_) {
209 DCHECK(foreground_task_token);
210 DCHECK(!background_task_token);
211 continuation.Run(foreground_task_token.Pass());
215 // Clear existing |blocking_factor| from |dependency_manager_| before
216 // getting |foreground_task_token|, so that we can avoid dead lock.
217 if (background_task_token && background_task_token->blocking_factor()) {
218 dependency_manager_.Erase(background_task_token->blocking_factor());
219 background_task_token->clear_blocking_factor();
222 // Try to get |foreground_task_token|. If it's not available, wait for
223 // current foreground task to finish.
224 if (!foreground_task_token) {
225 DCHECK(background_task_token);
226 foreground_task_token = GetToken(background_task_token->location(),
227 SyncStatusCallback());
228 if (!foreground_task_token) {
230 base::Bind(&SyncTaskManager::UpdateBlockingFactorBody,
232 base::Passed(&foreground_task_token),
233 base::Passed(&background_task_token),
234 base::Passed(&blocking_factor),
242 // Check if the task can run as a background task now.
243 // If there are too many task running or any other task blocks current
244 // task, wait for any other task to finish.
245 bool task_number_limit_exceeded =
246 !background_task_token &&
247 running_background_tasks_.size() >= maximum_background_task_;
248 if (task_number_limit_exceeded ||
249 !dependency_manager_.Insert(blocking_factor.get())) {
250 DCHECK(!running_background_tasks_.empty());
251 DCHECK(pending_backgrounding_task_.is_null());
253 // Wait for NotifyTaskDone to release a |blocking_factor|.
254 pending_backgrounding_task_ =
255 base::Bind(&SyncTaskManager::UpdateBlockingFactorBody,
257 base::Passed(&foreground_task_token),
258 base::Passed(&background_task_token),
259 base::Passed(&blocking_factor),
264 if (background_task_token) {
265 background_task_token->set_blocking_factor(blocking_factor.Pass());
267 tracked_objects::Location from_here = foreground_task_token->location();
268 SyncStatusCallback callback = foreground_task_token->callback();
269 foreground_task_token->clear_callback();
271 background_task_token =
272 SyncTaskToken::CreateForBackgroundTask(
275 blocking_factor.Pass());
276 background_task_token->UpdateTask(from_here, callback);
277 running_background_tasks_.set(background_task_token->token_id(),
278 running_foreground_task_.Pass());
281 token_ = foreground_task_token.Pass();
283 continuation.Run(background_task_token.Pass());
286 scoped_ptr<SyncTaskToken> SyncTaskManager::GetToken(
287 const tracked_objects::Location& from_here,
288 const SyncStatusCallback& callback) {
290 return scoped_ptr<SyncTaskToken>();
291 token_->UpdateTask(from_here, callback);
292 return token_.Pass();
295 void SyncTaskManager::PushPendingTask(
296 const base::Closure& closure, Priority priority) {
297 pending_tasks_.push(PendingTask(closure, priority, pending_task_seq_++));
300 void SyncTaskManager::RunTask(scoped_ptr<SyncTaskToken> token,
301 scoped_ptr<SyncTask> task) {
302 DCHECK(!running_foreground_task_);
303 running_foreground_task_ = task.Pass();
304 running_foreground_task_->RunPreflight(token.Pass());
307 void SyncTaskManager::StartNextTask() {
308 if (!pending_backgrounding_task_.is_null()) {
309 base::Closure closure = pending_backgrounding_task_;
310 pending_backgrounding_task_.Reset();
315 if (!pending_tasks_.empty()) {
316 base::Closure closure = pending_tasks_.top().task;
317 pending_tasks_.pop();
323 client_->MaybeScheduleNextTask();
326 } // namespace drive_backend
327 } // namespace sync_file_system