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 WorkerTask : public history::HistoryDBTask { |
17 public: | 17 public: |
18 WorkerTask(const syncer::WorkCallback& work, | 18 WorkerTask(const syncer::WorkCallback& work, |
| 19 scoped_refptr<HistoryModelWorker> history_model_worker, |
19 syncer::ScopedEventSignal scoped_event_signal, | 20 syncer::ScopedEventSignal scoped_event_signal, |
20 syncer::SyncerError* error) | 21 syncer::SyncerError* error) |
21 : work_(work), | 22 : work_(work), |
| 23 history_model_worker_(std::move(history_model_worker)), |
22 scoped_event_signal_(std::move(scoped_event_signal)), | 24 scoped_event_signal_(std::move(scoped_event_signal)), |
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. |
31 *error_ = work_.Run(); | 38 *error_ = work_.Run(); |
32 | 39 |
33 return true; | 40 return true; |
34 } | 41 } |
35 | 42 |
36 // Since the DoWorkAndWaitUntilDone() is synchronous, we don't need to run | 43 // Since the DoWorkAndWaitUntilDone() is synchronous, we don't need to run |
37 // any code asynchronously on the main thread after completion. | 44 // any code asynchronously on the main thread after completion. |
38 void DoneRunOnMainThread() override {} | 45 void DoneRunOnMainThread() override {} |
39 | 46 |
40 protected: | 47 protected: |
41 ~WorkerTask() override { | 48 syncer::WorkCallback work_; |
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 | 49 |
46 syncer::WorkCallback work_; | 50 // Keep a reference to the HistoryModelWorker that owns the WaitableEvent* in |
| 51 // |scoped_event_signal_| to prevent a use-after-free. |
| 52 const scoped_refptr<HistoryModelWorker> history_model_worker_; |
| 53 |
47 syncer::ScopedEventSignal scoped_event_signal_; | 54 syncer::ScopedEventSignal scoped_event_signal_; |
48 syncer::SyncerError* error_; | 55 syncer::SyncerError* error_; |
49 }; | 56 }; |
50 | 57 |
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( | 58 HistoryModelWorker::HistoryModelWorker( |
94 const base::WeakPtr<history::HistoryService>& history_service, | 59 const base::WeakPtr<history::HistoryService>& history_service, |
95 const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread) | 60 const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread) |
96 : history_service_(history_service), ui_thread_(ui_thread) { | 61 : history_service_(history_service), |
| 62 ui_thread_(ui_thread), |
| 63 work_done_or_abandoned_(base::WaitableEvent::ResetPolicy::MANUAL, |
| 64 base::WaitableEvent::InitialState::NOT_SIGNALED) { |
97 CHECK(history_service.get()); | 65 CHECK(history_service.get()); |
98 DCHECK(ui_thread_->BelongsToCurrentThread()); | 66 DCHECK(ui_thread_->BelongsToCurrentThread()); |
| 67 sequence_checker_.DetachFromSequence(); |
99 cancelable_tracker_.reset(new base::CancelableTaskTracker); | 68 cancelable_tracker_.reset(new base::CancelableTaskTracker); |
100 } | 69 } |
101 | 70 |
102 syncer::SyncerError HistoryModelWorker::DoWorkAndWaitUntilDoneImpl( | 71 syncer::SyncerError HistoryModelWorker::DoWorkAndWaitUntilDoneImpl( |
103 const syncer::WorkCallback& work) { | 72 const syncer::WorkCallback& work) { |
| 73 DCHECK(sequence_checker_.CalledOnValidSequence()); |
104 syncer::SyncerError error = syncer::UNSET; | 74 syncer::SyncerError error = syncer::UNSET; |
105 | 75 |
106 // Signaled after the task runs or when it is abandoned. | 76 { |
107 base::WaitableEvent work_done_or_abandoned( | 77 // Make sure that this method does not wait for a UI task after |
108 base::WaitableEvent::ResetPolicy::AUTOMATIC, | 78 // RequestStop() is called, because the UI thread doesn't run tasks after |
109 base::WaitableEvent::InitialState::NOT_SIGNALED); | 79 // RequestStop(). |
| 80 // |
| 81 // Checking IsStopped() and resetting |work_done_or_abandoned_| must be done |
| 82 // atomically to prevent this deadlock: |
| 83 // Thread Method Operation |
| 84 // Sync DoWorkAndWaitUntilDoneImpl() Sees that IsStopped() is false. |
| 85 // UI RequestStop() Sets IsStopped() to true. |
| 86 // UI RequestStop() Signals |work_done_or_abandoned_|. |
| 87 // Sync DoWorkAndWaitUntilDoneImpl() Resets |work_done_or_abandoned_|. |
| 88 // Sync DoWorkAndWaitUntilDoneImpl() Waits on |work_done_or_abandoned_| |
| 89 // forever. |
| 90 base::AutoLock auto_lock(lock_); |
| 91 if (IsStopped()) |
| 92 return syncer::CANNOT_DO_WORK; |
| 93 work_done_or_abandoned_.Reset(); |
| 94 DCHECK(!ui_task_pending_); |
| 95 ui_task_pending_ = true; |
| 96 } |
110 | 97 |
111 if (ui_thread_->PostTask(FROM_HERE, | 98 // Post a task to the UI thread that itself posts |work| to the history DB |
112 base::Bind(&PostWorkerTask, history_service_, work, | 99 // thread. |
113 base::Passed(syncer::ScopedEventSignal( | 100 if (!ui_thread_->PostTask( |
114 &work_done_or_abandoned)), | 101 FROM_HERE, |
115 cancelable_tracker_.get(), &error))) { | 102 base::Bind( |
116 work_done_or_abandoned.Wait(); | 103 &HistoryModelWorker::ScheduleHistoryDBTaskFromUIThread, this, |
117 } else { | 104 base::Passed(base::MakeUnique<WorkerTask>( |
118 error = syncer::CANNOT_DO_WORK; | 105 work, this, |
| 106 syncer::ScopedEventSignal(&work_done_or_abandoned_), &error)), |
| 107 base::Unretained(&error)))) { |
| 108 return syncer::CANNOT_DO_WORK; |
119 } | 109 } |
| 110 |
| 111 work_done_or_abandoned_.Wait(); |
| 112 |
120 return error; | 113 return error; |
121 } | 114 } |
122 | 115 |
| 116 void HistoryModelWorker::RequestStop() { |
| 117 DCHECK(ui_thread_->BelongsToCurrentThread()); |
| 118 |
| 119 base::AutoLock auto_lock(lock_); |
| 120 ModelSafeWorker::RequestStop(); |
| 121 |
| 122 // The UI thread doesn't run tasks after RequestStop(). Therefore, unblock |
| 123 // DoWorkAndWaitUntilDoneImpl() if it is waiting for a UI task. |
| 124 if (ui_task_pending_) |
| 125 work_done_or_abandoned_.Signal(); |
| 126 } |
| 127 |
123 syncer::ModelSafeGroup HistoryModelWorker::GetModelSafeGroup() { | 128 syncer::ModelSafeGroup HistoryModelWorker::GetModelSafeGroup() { |
124 return syncer::GROUP_HISTORY; | 129 return syncer::GROUP_HISTORY; |
125 } | 130 } |
126 | 131 |
127 bool HistoryModelWorker::IsOnModelThread() { | 132 bool HistoryModelWorker::IsOnModelThread() { |
128 // Ideally HistoryService would expose a way to check whether this is the | 133 // 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 | 134 // history DB thread. Since it doesn't, just return true to bypass a CHECK in |
130 // the sync code. | 135 // the sync code. |
131 return true; | 136 return true; |
132 } | 137 } |
133 | 138 |
134 HistoryModelWorker::~HistoryModelWorker() { | 139 HistoryModelWorker::~HistoryModelWorker() { |
135 // The base::CancelableTaskTracker class is not thread-safe and must only be | 140 // 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 | 141 // 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. | 142 // the UI thread, so delete it from the UI thread. |
138 ui_thread_->DeleteSoon(FROM_HERE, cancelable_tracker_.release()); | 143 ui_thread_->DeleteSoon(FROM_HERE, cancelable_tracker_.release()); |
139 } | 144 } |
140 | 145 |
| 146 void HistoryModelWorker::ScheduleHistoryDBTaskFromUIThread( |
| 147 std::unique_ptr<history::HistoryDBTask> worker_task, |
| 148 syncer::SyncerError* error) { |
| 149 if (history_service_) { |
| 150 history_service_->ScheduleDBTask(std::move(worker_task), |
| 151 cancelable_tracker_.get()); |
| 152 } else { |
| 153 *error = syncer::CANNOT_DO_WORK; |
| 154 } |
| 155 |
| 156 base::AutoLock auto_lock(lock_); |
| 157 DCHECK(ui_task_pending_); |
| 158 ui_task_pending_ = false; |
| 159 } |
| 160 |
141 } // namespace browser_sync | 161 } // namespace browser_sync |
OLD | NEW |