sync_task_manager.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
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.
4
5#include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
6
7#include "base/bind.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"
12
13using fileapi::FileSystemURL;
14
15namespace sync_file_system {
16namespace drive_backend {
17
18namespace {
19
20class SyncTaskAdapter : public ExclusiveTask {
21 public:
22  explicit SyncTaskAdapter(const SyncTaskManager::Task& task) : task_(task) {}
23  virtual ~SyncTaskAdapter() {}
24
25  virtual void RunExclusive(const SyncStatusCallback& callback) OVERRIDE {
26    task_.Run(callback);
27  }
28
29 private:
30  SyncTaskManager::Task task_;
31
32  DISALLOW_COPY_AND_ASSIGN(SyncTaskAdapter);
33};
34
35}  // namespace
36
37SyncTaskManager::PendingTask::PendingTask() {}
38
39SyncTaskManager::PendingTask::PendingTask(
40    const base::Closure& task, Priority pri, int seq)
41    : task(task), priority(pri), seq(seq) {}
42
43SyncTaskManager::PendingTask::~PendingTask() {}
44
45bool 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;
51}
52
53SyncTaskManager::SyncTaskManager(
54    base::WeakPtr<Client> client,
55    size_t maximum_background_task)
56    : client_(client),
57      maximum_background_task_(maximum_background_task),
58      pending_task_seq_(0),
59      task_token_seq_(SyncTaskToken::kMinimumBackgroundTaskTokenID) {
60}
61
62SyncTaskManager::~SyncTaskManager() {
63  client_.reset();
64  token_.reset();
65}
66
67void SyncTaskManager::Initialize(SyncStatusCode status) {
68  DCHECK(!token_);
69  NotifyTaskDone(SyncTaskToken::CreateForForegroundTask(AsWeakPtr()),
70                 status);
71}
72
73void SyncTaskManager::ScheduleTask(
74    const tracked_objects::Location& from_here,
75    const Task& task,
76    Priority priority,
77    const SyncStatusCallback& callback) {
78  ScheduleSyncTask(from_here,
79                   scoped_ptr<SyncTask>(new SyncTaskAdapter(task)),
80                   priority,
81                   callback);
82}
83
84void SyncTaskManager::ScheduleSyncTask(
85    const tracked_objects::Location& from_here,
86    scoped_ptr<SyncTask> task,
87    Priority priority,
88    const SyncStatusCallback& callback) {
89  scoped_ptr<SyncTaskToken> token(GetToken(from_here, callback));
90  if (!token) {
91    PushPendingTask(
92        base::Bind(&SyncTaskManager::ScheduleSyncTask, AsWeakPtr(), from_here,
93                   base::Passed(&task), priority, callback),
94        priority);
95    return;
96  }
97  RunTask(token.Pass(), task.Pass());
98}
99
100bool SyncTaskManager::ScheduleTaskIfIdle(
101        const tracked_objects::Location& from_here,
102        const Task& task,
103        const SyncStatusCallback& callback) {
104  return ScheduleSyncTaskIfIdle(
105      from_here,
106      scoped_ptr<SyncTask>(new SyncTaskAdapter(task)),
107      callback);
108}
109
110bool 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));
115  if (!token)
116    return false;
117  RunTask(token.Pass(), task.Pass());
118  return true;
119}
120
121// static
122void SyncTaskManager::NotifyTaskDone(scoped_ptr<SyncTaskToken> token,
123                                     SyncStatusCode status) {
124  DCHECK(token);
125
126  SyncTaskManager* manager = token->manager();
127  if (manager)
128    manager->NotifyTaskDoneBody(token.Pass(), status);
129}
130
131// static
132void SyncTaskManager::MoveTaskToBackground(
133    scoped_ptr<SyncTaskToken> token,
134    scoped_ptr<BlockingFactor> blocking_factor,
135    const Continuation& continuation) {
136  DCHECK(token);
137
138  SyncTaskManager* manager = token->manager();
139  if (!manager)
140    return;
141  manager->MoveTaskToBackgroundBody(token.Pass(), blocking_factor.Pass(),
142                                    continuation);
143}
144
145bool SyncTaskManager::IsRunningTask(int64 token_id) const {
146  // If the client is gone, all task should be aborted.
147  if (!client_)
148    return false;
149
150  if (token_id == SyncTaskToken::kForegroundTaskTokenID)
151    return true;
152
153  return ContainsKey(running_background_task_, token_id);
154}
155
156void SyncTaskManager::NotifyTaskDoneBody(scoped_ptr<SyncTaskToken> token,
157                                         SyncStatusCode status) {
158  DCHECK(token);
159
160  DVLOG(3) << "NotifyTaskDone: " << "finished with status=" << status
161           << " (" << SyncStatusCodeToString(status) << ")"
162           << " " << token_->location().ToString();
163
164  if (token->blocking_factor()) {
165    dependency_manager_.Erase(*token->blocking_factor());
166    token->clear_blocking_factor();
167  }
168
169  scoped_ptr<SyncTask> task;
170  SyncStatusCallback callback = token->callback();
171  token->clear_callback();
172  if (token->token_id() == SyncTaskToken::kForegroundTaskTokenID) {
173    token_ = token.Pass();
174    task = running_task_.Pass();
175  } else {
176    task = running_background_task_.take_and_erase(token->token_id());
177  }
178
179  bool task_used_network = false;
180  if (task)
181    task_used_network = task->used_network();
182
183  if (client_)
184    client_->NotifyLastOperationStatus(status, task_used_network);
185
186  if (!callback.is_null())
187    callback.Run(status);
188
189  StartNextTask();
190}
191
192void SyncTaskManager::MoveTaskToBackgroundBody(
193    scoped_ptr<SyncTaskToken> token,
194    scoped_ptr<BlockingFactor> blocking_factor,
195    const Continuation& continuation) {
196  if (!maximum_background_task_) {
197    continuation.Run(token.Pass());
198    return;
199  }
200
201  if (running_background_task_.size() >= maximum_background_task_ ||
202      !dependency_manager_.Insert(*blocking_factor)) {
203    DCHECK(!running_background_task_.empty());
204
205    // Wait for NotifyTaskDone to release a |blocking_factor|.
206    pending_backgrounding_task_ =
207        base::Bind(&SyncTaskManager::MoveTaskToBackground,
208                   base::Passed(&token), base::Passed(&blocking_factor),
209                   continuation);
210    return;
211  }
212
213  tracked_objects::Location from_here = token->location();
214  SyncStatusCallback callback = token->callback();
215  token->clear_callback();
216
217  scoped_ptr<SyncTaskToken> background_task_token =
218      SyncTaskToken::CreateForBackgroundTask(
219          AsWeakPtr(), task_token_seq_++, blocking_factor.Pass());
220  background_task_token->UpdateTask(from_here, callback);
221
222  NotifyTaskBackgrounded(token.Pass(), *background_task_token);
223  continuation.Run(background_task_token.Pass());
224}
225
226void SyncTaskManager::NotifyTaskBackgrounded(
227    scoped_ptr<SyncTaskToken> foreground_task_token,
228    const SyncTaskToken& background_task_token) {
229  token_ = foreground_task_token.Pass();
230  running_background_task_.set(background_task_token.token_id(),
231                               running_task_.Pass());
232
233  StartNextTask();
234}
235
236scoped_ptr<SyncTaskToken> SyncTaskManager::GetToken(
237    const tracked_objects::Location& from_here,
238    const SyncStatusCallback& callback) {
239  if (!token_)
240    return scoped_ptr<SyncTaskToken>();
241  token_->UpdateTask(from_here, callback);
242  return token_.Pass();
243}
244
245void SyncTaskManager::PushPendingTask(
246    const base::Closure& closure, Priority priority) {
247  pending_tasks_.push(PendingTask(closure, priority, pending_task_seq_++));
248}
249
250void SyncTaskManager::RunTask(scoped_ptr<SyncTaskToken> token,
251                              scoped_ptr<SyncTask> task) {
252  DCHECK(!running_task_);
253  running_task_ = task.Pass();
254  running_task_->RunPreflight(token.Pass());
255}
256
257void SyncTaskManager::StartNextTask() {
258  if (!pending_backgrounding_task_.is_null()) {
259    base::Closure closure = pending_backgrounding_task_;
260    pending_backgrounding_task_.Reset();
261    closure.Run();
262    return;
263  }
264
265  if (!pending_tasks_.empty()) {
266    base::Closure closure = pending_tasks_.top().task;
267    pending_tasks_.pop();
268    closure.Run();
269    return;
270  }
271
272  if (client_)
273    client_->MaybeScheduleNextTask();
274}
275
276}  // namespace drive_backend
277}  // namespace sync_file_system
278