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

Side by Side Diff: components/history/core/browser/history_model_worker_unittest.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
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/history/core/browser/history_model_worker.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/macros.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/run_loop.h"
15 #include "base/synchronization/atomic_flag.h"
16 #include "base/test/test_simple_task_runner.h"
17 #include "base/threading/platform_thread.h"
18 #include "base/threading/thread.h"
19 #include "base/threading/thread_task_runner_handle.h"
20 #include "components/history/core/browser/history_db_task.h"
21 #include "components/history/core/browser/history_service.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23
24 namespace browser_sync {
25 namespace {
26
27 class HistoryServiceMock : public history::HistoryService {
28 public:
29 HistoryServiceMock() = default;
30
31 base::CancelableTaskTracker::TaskId ScheduleDBTask(
32 std::unique_ptr<history::HistoryDBTask> task,
33 base::CancelableTaskTracker* tracker) override {
34 history::HistoryDBTask* task_raw = task.get();
35 task_runner_->PostTaskAndReply(
36 FROM_HERE,
37 base::Bind(base::IgnoreResult(&history::HistoryDBTask::RunOnDBThread),
38 base::Unretained(task_raw), nullptr, nullptr),
39 base::Bind(&history::HistoryDBTask::DoneRunOnMainThread,
40 base::Passed(std::move(task))));
41 return base::CancelableTaskTracker::kBadTaskId; // Unused.
42 }
43
44 scoped_refptr<base::TestSimpleTaskRunner> task_runner() const {
45 return task_runner_;
46 }
47
48 private:
49 const scoped_refptr<base::TestSimpleTaskRunner> task_runner_ =
50 new base::TestSimpleTaskRunner();
51
52 DISALLOW_COPY_AND_ASSIGN(HistoryServiceMock);
53 };
54
55 syncer::WorkCallback ClosureToWorkCallback(base::Closure work) {
56 return base::Bind(
57 [](scoped_refptr<base::SingleThreadTaskRunner> thread_verifier,
58 base::Closure work) {
59 EXPECT_TRUE(thread_verifier->BelongsToCurrentThread());
60 work.Run();
61 return syncer::SYNCER_OK;
62 },
63 base::ThreadTaskRunnerHandle::Get(), std::move(work));
64 }
65
66 class HistoryModelWorkerTest : public testing::Test {
67 public:
68 HistoryModelWorkerTest()
69 : thread_task_runner_handle_(ui_thread_task_runner_),
70 sync_thread_("SyncThreadForTest"),
71 history_service_factory_(&history_service_) {
72 sync_thread_.Start();
73 worker_ = new HistoryModelWorker(history_service_factory_.GetWeakPtr(),
74 ui_thread_task_runner_);
75 }
76
77 protected:
78 void DoWorkAndWaitUntilDoneOnSyncThread(base::Closure work) {
79 sync_thread_.task_runner()->PostTask(
80 FROM_HERE, base::Bind(base::IgnoreResult(
81 &HistoryModelWorker::DoWorkAndWaitUntilDone),
82 worker_, ClosureToWorkCallback(work)));
83 }
84
85 scoped_refptr<base::TestSimpleTaskRunner> ui_thread_task_runner_ =
86 new base::TestSimpleTaskRunner();
87
88 scoped_refptr<HistoryModelWorker> worker_;
89
90 base::Thread sync_thread_;
91
92 base::ThreadTaskRunnerHandle thread_task_runner_handle_;
maxbogue 2017/03/20 16:52:37 Does this overwrite base::ThreadTaskRunnerHandle::
fdoray 2017/03/20 18:54:54 Done.
93
94 HistoryServiceMock history_service_;
95
96 private:
97 base::WeakPtrFactory<HistoryServiceMock> history_service_factory_;
98
99 DISALLOW_COPY_AND_ASSIGN(HistoryModelWorkerTest);
100 };
101
102 } // namespace
103
104 TEST_F(HistoryModelWorkerTest, JoinSyncThreadAfterUITaskDestroyed) {
105 // Post a DoWorkAndWaitUntilDone() task to the sync thread.
106 // DoWorkAndWaitUntilDone() posts a task to the UI thread that itself posts a
107 // task to the history DB thread. It blocks until either the history DB task
108 // runs or is abandoned.
109 DoWorkAndWaitUntilDoneOnSyncThread(base::Bind(&base::DoNothing));
110
111 // Wait until DoWorkAndWaitUntilDone() posts to the UI thread.
112 while (!ui_thread_task_runner_->HasPendingTask())
113 base::PlatformThread::YieldCurrentThread();
114
115 // Destroying the task posted to the UI thread should unblock
116 // DoWorkAndWaitUntilDone() on the sync thread.
117 ui_thread_task_runner_->ClearPendingTasks();
118 sync_thread_.Stop();
119 }
120
121 TEST_F(HistoryModelWorkerTest, JoinSyncThreadAfterRequestStop) {
122 // Post a DoWorkAndWaitUntilDone() task to the sync thread.
123 // DoWorkAndWaitUntilDone() posts a task to the UI thread that itself posts a
124 // task to the history DB thread. It blocks until either the history DB task
125 // runs or is abandoned.
126 DoWorkAndWaitUntilDoneOnSyncThread(base::Bind(&base::DoNothing));
127
128 // Wait until DoWorkAndWaitUntilDone() posts to the UI thread.
129 while (!ui_thread_task_runner_->HasPendingTask())
130 base::PlatformThread::YieldCurrentThread();
131
132 // Calling RequestStop() on HistoryModelWorker should unblock
133 // DoWorkAndWaitUntilDone() on the sync thread.
134 worker_->RequestStop();
135 sync_thread_.Stop();
136 }
137
138 TEST_F(HistoryModelWorkerTest, RequestStopAfterUITaskBeforeHistoryTask) {
139 base::AtomicFlag do_work_and_wait_until_done_unblocked;
140
141 // Post a DoWorkAndWaitUntilDone() task to the sync thread.
142 // DoWorkAndWaitUntilDone() posts a task to the UI thread that itself posts a
143 // task to the history DB thread. It blocks until either the history DB task
144 // runs or is abandoned.
145 DoWorkAndWaitUntilDoneOnSyncThread(base::Bind(
146 [](base::AtomicFlag* do_work_and_wait_until_done_unblocked) {
147 // DoWorkAndWaitUntilDone() should not return while a WorkCallback is
148 // running on the history DB thread.
149 EXPECT_FALSE(do_work_and_wait_until_done_unblocked->IsSet());
150 },
151 base::Unretained(&do_work_and_wait_until_done_unblocked)));
152 sync_thread_.task_runner()->PostTask(
153 FROM_HERE,
154 base::Bind(&base::AtomicFlag::Set,
155 base::Unretained(&do_work_and_wait_until_done_unblocked)));
156
157 // Wait until DoWorkAndWaitUntilDone() posts to the UI thread and run it.
158 while (!ui_thread_task_runner_->HasPendingTask())
159 base::PlatformThread::YieldCurrentThread();
160 ui_thread_task_runner_->RunUntilIdle();
161
162 // The UI thread should have posted a task to the history DB thread. Run it to
163 // unblock DoWorkAndWaitUntilDone() on the sync thread.
164 EXPECT_TRUE(history_service_.task_runner()->HasPendingTask());
165 history_service_.task_runner()->RunUntilIdle();
166
167 sync_thread_.Stop();
maxbogue 2017/03/20 16:52:37 Is this guaranteed to run the task posted to it or
fdoray 2017/03/20 18:54:54 Done.
168
169 EXPECT_TRUE(do_work_and_wait_until_done_unblocked.IsSet());
170 }
171
172 } // namespace browser_sync
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698