Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(417)

Side by Side Diff: components/history/core/browser/history_model_worker.cc

Issue 2757193003: [Sync] Do not deadlock when joining sync thread with a pending HistoryModelWorker task. (Closed)
Patch Set: fix comment Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698