Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 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 "components/history/core/browser/history_model_worker.h" | 5 #include "components/history/core/browser/history_model_worker.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/synchronization/waitable_event.h" | 9 #include "base/memory/ptr_util.h" |
| 10 #include "components/history/core/browser/history_db_task.h" | 10 #include "components/history/core/browser/history_db_task.h" |
| 11 #include "components/history/core/browser/history_service.h" | 11 #include "components/history/core/browser/history_service.h" |
| 12 #include "components/sync/base/scoped_event_signal.h" | 12 #include "components/sync/base/scoped_event_signal.h" |
| 13 | 13 |
| 14 namespace browser_sync { | 14 namespace browser_sync { |
| 15 | 15 |
| 16 class WorkerTask : public history::HistoryDBTask { | 16 class HistoryModelWorker::WorkerTask : public history::HistoryDBTask { |
| 17 public: | 17 public: |
| 18 WorkerTask(const syncer::WorkCallback& work, | 18 WorkerTask(const syncer::WorkCallback& work, |
| 19 syncer::ScopedEventSignal scoped_event_signal, | 19 syncer::ScopedEventSignal scoped_event_signal, |
| 20 scoped_refptr<HistoryModelWorker> history_model_worker, | |
| 20 syncer::SyncerError* error) | 21 syncer::SyncerError* error) |
| 21 : work_(work), | 22 : work_(work), |
| 22 scoped_event_signal_(std::move(scoped_event_signal)), | 23 scoped_event_signal_(std::move(scoped_event_signal)), |
| 24 history_model_worker_(std::move(history_model_worker)), | |
| 23 error_(error) {} | 25 error_(error) {} |
| 24 | 26 |
| 27 ~WorkerTask() override { | |
| 28 // The event in |scoped_event_signal_| is signaled at the end of this | |
| 29 // scope if this is destroyed before RunOnDBThread runs. | |
| 30 } | |
| 31 | |
| 25 bool RunOnDBThread(history::HistoryBackend* backend, | 32 bool RunOnDBThread(history::HistoryBackend* backend, |
| 26 history::HistoryDatabase* db) override { | 33 history::HistoryDatabase* db) override { |
| 27 // Signal the completion event at the end of this scope. | 34 // Signal the completion event at the end of this scope. |
| 28 auto scoped_event_signal = std::move(scoped_event_signal_); | 35 auto scoped_event_signal = std::move(scoped_event_signal_); |
| 29 | 36 |
| 30 // Run the task. | 37 // Run the task if RequestStop() hasn't been called. |
| 31 *error_ = work_.Run(); | 38 if (history_model_worker_->WillRunWorkCallback()) { |
| 39 *error_ = work_.Run(); | |
| 40 history_model_worker_->DidRunWorkCallback(); | |
| 41 } else { | |
| 42 *error_ = syncer::CANNOT_DO_WORK; | |
| 43 } | |
| 32 | 44 |
| 33 return true; | 45 return true; |
| 34 } | 46 } |
| 35 | 47 |
| 36 // Since the DoWorkAndWaitUntilDone() is synchronous, we don't need to run | 48 // Since the DoWorkAndWaitUntilDone() is synchronous, we don't need to run |
| 37 // any code asynchronously on the main thread after completion. | 49 // any code asynchronously on the main thread after completion. |
| 38 void DoneRunOnMainThread() override {} | 50 void DoneRunOnMainThread() override {} |
| 39 | 51 |
| 40 protected: | 52 protected: |
| 41 ~WorkerTask() override { | |
| 42 // The event in |scoped_event_signal_| is signaled at the end of this | |
| 43 // scope if this is destroyed before RunOnDBThread runs. | |
| 44 } | |
| 45 | |
| 46 syncer::WorkCallback work_; | 53 syncer::WorkCallback work_; |
| 47 syncer::ScopedEventSignal scoped_event_signal_; | 54 syncer::ScopedEventSignal scoped_event_signal_; |
| 55 const scoped_refptr<HistoryModelWorker> history_model_worker_; | |
| 48 syncer::SyncerError* error_; | 56 syncer::SyncerError* error_; |
| 49 }; | 57 }; |
| 50 | 58 |
| 51 class AddDBThreadObserverTask : public history::HistoryDBTask { | |
| 52 public: | |
| 53 explicit AddDBThreadObserverTask(base::Closure register_callback) | |
| 54 : register_callback_(register_callback) {} | |
| 55 | |
| 56 bool RunOnDBThread(history::HistoryBackend* backend, | |
| 57 history::HistoryDatabase* db) override { | |
| 58 register_callback_.Run(); | |
| 59 return true; | |
| 60 } | |
| 61 | |
| 62 void DoneRunOnMainThread() override {} | |
| 63 | |
| 64 private: | |
| 65 ~AddDBThreadObserverTask() override {} | |
| 66 | |
| 67 base::Closure register_callback_; | |
| 68 }; | |
| 69 | |
| 70 namespace { | |
| 71 | |
| 72 // Post the work task on |history_service|'s DB thread from the UI | |
| 73 // thread. | |
| 74 void PostWorkerTask( | |
| 75 const base::WeakPtr<history::HistoryService>& history_service, | |
| 76 const syncer::WorkCallback& work, | |
| 77 syncer::ScopedEventSignal scoped_event_signal, | |
| 78 base::CancelableTaskTracker* cancelable_tracker, | |
| 79 syncer::SyncerError* error) { | |
| 80 if (history_service.get()) { | |
| 81 std::unique_ptr<history::HistoryDBTask> task( | |
| 82 new WorkerTask(work, std::move(scoped_event_signal), error)); | |
| 83 history_service->ScheduleDBTask(std::move(task), cancelable_tracker); | |
| 84 } else { | |
| 85 *error = syncer::CANNOT_DO_WORK; | |
| 86 // The event in |scoped_event_signal| is signaled at the end of this | |
| 87 // scope. | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 } // namespace | |
| 92 | |
| 93 HistoryModelWorker::HistoryModelWorker( | 59 HistoryModelWorker::HistoryModelWorker( |
| 94 const base::WeakPtr<history::HistoryService>& history_service, | 60 const base::WeakPtr<history::HistoryService>& history_service, |
| 95 const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread) | 61 const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread) |
| 96 : history_service_(history_service), ui_thread_(ui_thread) { | 62 : history_service_(history_service), |
| 63 ui_thread_(ui_thread), | |
| 64 work_done_or_abandoned_(base::WaitableEvent::ResetPolicy::MANUAL, | |
| 65 base::WaitableEvent::InitialState::NOT_SIGNALED), | |
| 66 stop_requested_(base::WaitableEvent::ResetPolicy::MANUAL, | |
| 67 base::WaitableEvent::InitialState::NOT_SIGNALED) { | |
| 97 CHECK(history_service.get()); | 68 CHECK(history_service.get()); |
| 98 DCHECK(ui_thread_->BelongsToCurrentThread()); | 69 DCHECK(ui_thread_->BelongsToCurrentThread()); |
| 70 sequence_checker_.DetachFromSequence(); | |
| 99 cancelable_tracker_.reset(new base::CancelableTaskTracker); | 71 cancelable_tracker_.reset(new base::CancelableTaskTracker); |
| 100 } | 72 } |
| 101 | 73 |
| 102 syncer::SyncerError HistoryModelWorker::DoWorkAndWaitUntilDoneImpl( | 74 syncer::SyncerError HistoryModelWorker::DoWorkAndWaitUntilDoneImpl( |
| 103 const syncer::WorkCallback& work) { | 75 const syncer::WorkCallback& work) { |
| 76 DCHECK(sequence_checker_.CalledOnValidSequence()); | |
| 104 syncer::SyncerError error = syncer::UNSET; | 77 syncer::SyncerError error = syncer::UNSET; |
| 78 work_done_or_abandoned_.Reset(); | |
| 105 | 79 |
| 106 // Signaled after the task runs or when it is abandoned. | 80 // Post a task to the UI thread that itself posts |work| to the history DB |
| 107 base::WaitableEvent work_done_or_abandoned( | 81 // thread. |
| 108 base::WaitableEvent::ResetPolicy::AUTOMATIC, | 82 if (!ui_thread_->PostTask( |
| 109 base::WaitableEvent::InitialState::NOT_SIGNALED); | 83 FROM_HERE, |
| 84 base::Bind( | |
| 85 [](const base::WeakPtr<history::HistoryService>& history_service, | |
|
maxbogue
2017/03/20 16:52:37
Optional: I love lambdas as much as anyone, but wi
fdoray
2017/03/20 18:54:54
Done.
| |
| 86 std::unique_ptr<history::HistoryDBTask> worker_task, | |
| 87 base::CancelableTaskTracker* cancelable_tracker, | |
| 88 syncer::SyncerError* error) { | |
| 89 if (history_service.get()) { | |
|
maxbogue
2017/03/20 16:52:37
nit: do you need .get() here? WeakPtr implements t
fdoray
2017/03/20 18:54:54
Done.
| |
| 90 history_service->ScheduleDBTask(std::move(worker_task), | |
| 91 cancelable_tracker); | |
| 92 } else { | |
| 93 *error = syncer::CANNOT_DO_WORK; | |
| 94 } | |
| 95 }, | |
| 96 history_service_, | |
| 97 base::Passed(base::MakeUnique<WorkerTask>( | |
| 98 work, syncer::ScopedEventSignal(&work_done_or_abandoned_), | |
| 99 this, &error)), | |
| 100 base::Unretained(cancelable_tracker_.get()), | |
| 101 base::Unretained(&error)))) { | |
| 102 error = syncer::CANNOT_DO_WORK; | |
| 103 return error; | |
| 104 } | |
| 110 | 105 |
| 111 if (ui_thread_->PostTask(FROM_HERE, | 106 base::WaitableEvent* events[] = {&work_done_or_abandoned_, &stop_requested_}; |
| 112 base::Bind(&PostWorkerTask, history_service_, work, | 107 base::WaitableEvent::WaitMany(events, arraysize(events)); |
| 113 base::Passed(syncer::ScopedEventSignal( | 108 |
| 114 &work_done_or_abandoned)), | |
| 115 cancelable_tracker_.get(), &error))) { | |
| 116 work_done_or_abandoned.Wait(); | |
| 117 } else { | |
| 118 error = syncer::CANNOT_DO_WORK; | |
| 119 } | |
| 120 return error; | 109 return error; |
| 121 } | 110 } |
| 122 | 111 |
| 112 void HistoryModelWorker::RequestStop() { | |
| 113 DCHECK(ui_thread_->BelongsToCurrentThread()); | |
| 114 ModelSafeWorker::RequestStop(); | |
| 115 | |
| 116 base::AutoLock auto_lock(lock_); | |
| 117 | |
| 118 // If a WorkCallback is running on the history DB thread, do nothing. | |
| 119 // DoWorkAndWaitUntilDoneImpl() will return when the WorkCallback completes. | |
| 120 // | |
| 121 // Note: Returning from DoWorkAndWaitUntilDoneImpl() when a WorkCallback is | |
| 122 // running could result in invalid memory accesses. | |
| 123 if (work_callback_running_) | |
| 124 return; | |
| 125 | |
| 126 // Else, signal |stop_requested_| to unblock DoWorkAndWaitUntilDoneImpl() and | |
| 127 // prevent any WorkCallback from being scheduled in the future. | |
| 128 stop_requested_.Signal(); | |
|
maxbogue
2017/03/20 16:52:37
"and prevent any WorkCallback from being scheduled
fdoray
2017/03/20 18:54:54
Done.
| |
| 129 } | |
| 130 | |
| 123 syncer::ModelSafeGroup HistoryModelWorker::GetModelSafeGroup() { | 131 syncer::ModelSafeGroup HistoryModelWorker::GetModelSafeGroup() { |
| 124 return syncer::GROUP_HISTORY; | 132 return syncer::GROUP_HISTORY; |
| 125 } | 133 } |
| 126 | 134 |
| 127 bool HistoryModelWorker::IsOnModelThread() { | 135 bool HistoryModelWorker::IsOnModelThread() { |
| 128 // Ideally HistoryService would expose a way to check whether this is the | 136 // Ideally HistoryService would expose a way to check whether this is the |
| 129 // history DB thread. Since it doesn't, just return true to bypass a CHECK in | 137 // history DB thread. Since it doesn't, just return true to bypass a CHECK in |
| 130 // the sync code. | 138 // the sync code. |
| 131 return true; | 139 return true; |
| 132 } | 140 } |
| 133 | 141 |
| 134 HistoryModelWorker::~HistoryModelWorker() { | 142 HistoryModelWorker::~HistoryModelWorker() { |
| 135 // The base::CancelableTaskTracker class is not thread-safe and must only be | 143 // The base::CancelableTaskTracker class is not thread-safe and must only be |
| 136 // used from a single thread but the current object may not be destroyed from | 144 // used from a single thread but the current object may not be destroyed from |
| 137 // the UI thread, so delete it from the UI thread. | 145 // the UI thread, so delete it from the UI thread. |
| 138 ui_thread_->DeleteSoon(FROM_HERE, cancelable_tracker_.release()); | 146 ui_thread_->DeleteSoon(FROM_HERE, cancelable_tracker_.release()); |
| 139 } | 147 } |
| 140 | 148 |
| 149 bool HistoryModelWorker::WillRunWorkCallback() { | |
| 150 base::AutoLock auto_lock(lock_); | |
| 151 | |
| 152 if (stop_requested_.IsSignaled()) | |
| 153 return false; | |
| 154 | |
| 155 work_callback_running_ = true; | |
| 156 return true; | |
| 157 } | |
| 158 | |
| 159 void HistoryModelWorker::DidRunWorkCallback() { | |
| 160 base::AutoLock auto_lock(lock_); | |
| 161 DCHECK(work_callback_running_); | |
| 162 work_callback_running_ = false; | |
| 163 } | |
| 164 | |
| 141 } // namespace browser_sync | 165 } // namespace browser_sync |
| OLD | NEW |