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

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 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 {
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_; 48 syncer::WorkCallback work_;
49 const scoped_refptr<HistoryModelWorker> history_model_worker_;
maxbogue 2017/03/20 19:55:50 No longer needed I think? Or is this just to ensur
fdoray 2017/03/21 17:21:31 Added comment. This is required to make sure that
47 syncer::ScopedEventSignal scoped_event_signal_; 50 syncer::ScopedEventSignal scoped_event_signal_;
48 syncer::SyncerError* error_; 51 syncer::SyncerError* error_;
49 }; 52 };
50 53
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( 54 HistoryModelWorker::HistoryModelWorker(
94 const base::WeakPtr<history::HistoryService>& history_service, 55 const base::WeakPtr<history::HistoryService>& history_service,
95 const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread) 56 const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread)
96 : history_service_(history_service), ui_thread_(ui_thread) { 57 : history_service_(history_service),
58 ui_thread_(ui_thread),
59 work_done_or_abandoned_(base::WaitableEvent::ResetPolicy::MANUAL,
60 base::WaitableEvent::InitialState::NOT_SIGNALED) {
97 CHECK(history_service.get()); 61 CHECK(history_service.get());
98 DCHECK(ui_thread_->BelongsToCurrentThread()); 62 DCHECK(ui_thread_->BelongsToCurrentThread());
63 sequence_checker_.DetachFromSequence();
99 cancelable_tracker_.reset(new base::CancelableTaskTracker); 64 cancelable_tracker_.reset(new base::CancelableTaskTracker);
100 } 65 }
101 66
102 syncer::SyncerError HistoryModelWorker::DoWorkAndWaitUntilDoneImpl( 67 syncer::SyncerError HistoryModelWorker::DoWorkAndWaitUntilDoneImpl(
103 const syncer::WorkCallback& work) { 68 const syncer::WorkCallback& work) {
69 DCHECK(sequence_checker_.CalledOnValidSequence());
104 syncer::SyncerError error = syncer::UNSET; 70 syncer::SyncerError error = syncer::UNSET;
105 71
106 // Signaled after the task runs or when it is abandoned. 72 {
107 base::WaitableEvent work_done_or_abandoned( 73 // If RequestStop() was called, return immediately. Otherwise, set a flag so
108 base::WaitableEvent::ResetPolicy::AUTOMATIC, 74 // that this method returns immediately if it is waiting for a UI task and
109 base::WaitableEvent::InitialState::NOT_SIGNALED); 75 // RequestStop() is called (the UI thread doesn't run tasks after that).
76 base::AutoLock auto_lock(lock_);
77 if (IsStopped())
maxbogue 2017/03/20 19:55:50 Is this needed? IsStopped() is checked immediately
fdoray 2017/03/21 17:21:31 Checking IsStopped() and resetting |work_done_or_a
78 return syncer::CANNOT_DO_WORK;
79 work_done_or_abandoned_.Reset();
80 DCHECK(!ui_task_pending_);
81 ui_task_pending_ = true;
82 }
110 83
111 if (ui_thread_->PostTask(FROM_HERE, 84 // Post a task to the UI thread that itself posts |work| to the history DB
112 base::Bind(&PostWorkerTask, history_service_, work, 85 // thread.
113 base::Passed(syncer::ScopedEventSignal( 86 if (!ui_thread_->PostTask(
114 &work_done_or_abandoned)), 87 FROM_HERE,
115 cancelable_tracker_.get(), &error))) { 88 base::Bind(
116 work_done_or_abandoned.Wait(); 89 &HistoryModelWorker::ScheduleHistoryDBTaskFromUIThread, this,
117 } else { 90 base::Passed(base::MakeUnique<WorkerTask>(
118 error = syncer::CANNOT_DO_WORK; 91 work, this,
92 syncer::ScopedEventSignal(&work_done_or_abandoned_), &error)),
93 base::Unretained(&error)))) {
94 return syncer::CANNOT_DO_WORK;
119 } 95 }
96
97 work_done_or_abandoned_.Wait();
98
120 return error; 99 return error;
121 } 100 }
122 101
102 void HistoryModelWorker::RequestStop() {
103 DCHECK(ui_thread_->BelongsToCurrentThread());
104
105 base::AutoLock auto_lock(lock_);
106 ModelSafeWorker::RequestStop();
107
108 // The UI thread doesn't run tasks after RequestStop(). Therefore, unblock
109 // DoWorkAndWaitUntilDoneImpl() if it is waiting for a UI task.
110 if (ui_task_pending_)
111 work_done_or_abandoned_.Signal();
112 }
113
123 syncer::ModelSafeGroup HistoryModelWorker::GetModelSafeGroup() { 114 syncer::ModelSafeGroup HistoryModelWorker::GetModelSafeGroup() {
124 return syncer::GROUP_HISTORY; 115 return syncer::GROUP_HISTORY;
125 } 116 }
126 117
127 bool HistoryModelWorker::IsOnModelThread() { 118 bool HistoryModelWorker::IsOnModelThread() {
128 // Ideally HistoryService would expose a way to check whether this is the 119 // 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 120 // history DB thread. Since it doesn't, just return true to bypass a CHECK in
130 // the sync code. 121 // the sync code.
131 return true; 122 return true;
132 } 123 }
133 124
134 HistoryModelWorker::~HistoryModelWorker() { 125 HistoryModelWorker::~HistoryModelWorker() {
135 // The base::CancelableTaskTracker class is not thread-safe and must only be 126 // 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 127 // 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. 128 // the UI thread, so delete it from the UI thread.
138 ui_thread_->DeleteSoon(FROM_HERE, cancelable_tracker_.release()); 129 ui_thread_->DeleteSoon(FROM_HERE, cancelable_tracker_.release());
139 } 130 }
140 131
132 void HistoryModelWorker::ScheduleHistoryDBTaskFromUIThread(
133 std::unique_ptr<history::HistoryDBTask> worker_task,
134 syncer::SyncerError* error) {
135 if (history_service_) {
136 history_service_->ScheduleDBTask(std::move(worker_task),
137 cancelable_tracker_.get());
138 } else {
139 *error = syncer::CANNOT_DO_WORK;
140 }
141
142 base::AutoLock auto_lock(lock_);
143 DCHECK(ui_task_pending_);
144 ui_task_pending_ = false;
145 }
146
141 } // namespace browser_sync 147 } // namespace browser_sync
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698