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

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: self-review 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 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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698