// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/sync_file_system/sync_task_manager.h" #include "base/debug/trace_event.h" #include "base/location.h" #include "chrome/browser/sync_file_system/sync_file_metadata.h" using fileapi::FileSystemURL; namespace sync_file_system { class SyncTaskManager::TaskToken { public: explicit TaskToken(const base::WeakPtr& manager) : manager_(manager) { } void UpdateTask(const tracked_objects::Location& location) { location_ = location; DVLOG(2) << "Token updated: " << location_.ToString(); } const tracked_objects::Location& location() const { return location_; } ~TaskToken() { // All task on Client must hold TaskToken instance to ensure // no other tasks are running. Also, as soon as a task finishes to work, // it must return the token to TaskManager. // Destroying a token with valid |client| indicates the token was // dropped by a task without returning. if (manager_.get() && manager_->client_.get()) { NOTREACHED() << "Unexpected TaskToken deletion from: " << location_.ToString(); // Reinitializes the token. manager_->NotifyTaskDone( make_scoped_ptr(new TaskToken(manager_)), SYNC_STATUS_OK); } } private: base::WeakPtr manager_; tracked_objects::Location location_; DISALLOW_COPY_AND_ASSIGN(TaskToken); }; SyncTaskManager::PendingTask::PendingTask() {} SyncTaskManager::PendingTask::PendingTask( const base::Closure& task, Priority pri, int seq) : task(task), priority(pri), seq(seq) {} SyncTaskManager::PendingTask::~PendingTask() {} bool SyncTaskManager::PendingTaskComparator::operator()( const PendingTask& left, const PendingTask& right) const { if (left.priority != right.priority) return left.priority < right.priority; return left.seq > right.seq; } SyncTaskManager::SyncTaskManager( base::WeakPtr client) : client_(client), last_operation_status_(SYNC_STATUS_OK), pending_task_seq_(0) { } SyncTaskManager::~SyncTaskManager() { client_.reset(); token_.reset(); } void SyncTaskManager::Initialize(SyncStatusCode status) { DCHECK(!token_); NotifyTaskDone(make_scoped_ptr(new TaskToken(AsWeakPtr())), status); } void SyncTaskManager::ScheduleTask( const Task& task, const SyncStatusCallback& callback) { ScheduleTaskAtPriority(task, PRIORITY_MED, callback); } void SyncTaskManager::ScheduleSyncTask( scoped_ptr task, const SyncStatusCallback& callback) { ScheduleSyncTaskAtPriority(task.Pass(), PRIORITY_MED, callback); } void SyncTaskManager::ScheduleTaskAtPriority( const Task& task, Priority priority, const SyncStatusCallback& callback) { scoped_ptr token(GetToken(FROM_HERE)); if (!token) { PushPendingTask( base::Bind(&SyncTaskManager::ScheduleTask, AsWeakPtr(), task, callback), priority); return; } task.Run(CreateCompletionCallback(token.Pass(), callback)); } void SyncTaskManager::ScheduleSyncTaskAtPriority( scoped_ptr task, Priority priority, const SyncStatusCallback& callback) { scoped_ptr token(GetToken(FROM_HERE)); if (!token) { PushPendingTask( base::Bind(&SyncTaskManager::ScheduleSyncTask, AsWeakPtr(), base::Passed(&task), callback), priority); return; } DCHECK(!running_task_); running_task_ = task.Pass(); running_task_->Run(CreateCompletionCallback(token.Pass(), callback)); } bool SyncTaskManager::ScheduleTaskIfIdle(const Task& task, const SyncStatusCallback& callback) { scoped_ptr token(GetToken(FROM_HERE)); if (!token) return false; task.Run(CreateCompletionCallback(token.Pass(), callback)); return true; } bool SyncTaskManager::ScheduleSyncTaskIfIdle( scoped_ptr task, const SyncStatusCallback& callback) { scoped_ptr token(GetToken(FROM_HERE)); if (!token) return false; DCHECK(!running_task_); running_task_ = task.Pass(); running_task_->Run(CreateCompletionCallback(token.Pass(), callback)); return true; } void SyncTaskManager::NotifyTaskDone( scoped_ptr token, SyncStatusCode status) { DCHECK(token); last_operation_status_ = status; token_ = token.Pass(); scoped_ptr task = running_task_.Pass(); TRACE_EVENT_ASYNC_END0("Sync FileSystem", "GetToken", this); DVLOG(3) << "NotifyTaskDone: " << "finished with status=" << status << " (" << SyncStatusCodeToString(status) << ")" << " " << token_->location().ToString(); bool task_used_network = false; if (task) task_used_network = task->used_network(); if (client_) client_->NotifyLastOperationStatus(last_operation_status_, task_used_network); if (!current_callback_.is_null()) { SyncStatusCallback callback = current_callback_; current_callback_.Reset(); callback.Run(status); } if (!pending_tasks_.empty()) { base::Closure closure = pending_tasks_.top().task; pending_tasks_.pop(); closure.Run(); return; } if (client_) client_->MaybeScheduleNextTask(); } scoped_ptr SyncTaskManager::GetToken( const tracked_objects::Location& from_here) { if (!token_) return scoped_ptr(); TRACE_EVENT_ASYNC_BEGIN1("Sync FileSystem", "GetToken", this, "where", from_here.ToString()); token_->UpdateTask(from_here); return token_.Pass(); } SyncStatusCallback SyncTaskManager::CreateCompletionCallback( scoped_ptr token, const SyncStatusCallback& callback) { DCHECK(token); DCHECK(current_callback_.is_null()); current_callback_ = callback; return base::Bind(&SyncTaskManager::NotifyTaskDone, AsWeakPtr(), base::Passed(&token)); } void SyncTaskManager::PushPendingTask( const base::Closure& closure, Priority priority) { pending_tasks_.push(PendingTask(closure, priority, pending_task_seq_++)); } } // namespace sync_file_system