1 // Copyright 2013 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 "base/sequence_checker.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/macros.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/test/sequenced_worker_pool_owner.h"
20 #include "base/threading/thread.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 // Duplicated from base/sequence_checker.h so that we can be good citizens
24 // there and undef the macro.
25 #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
26 #define ENABLE_SEQUENCE_CHECKER 1
27 #else
28 #define ENABLE_SEQUENCE_CHECKER 0
29 #endif
30
31 namespace base {
32
33 namespace {
34
35 const size_t kNumWorkerThreads = 3;
36
37 // Simple class to exercise the basics of SequenceChecker.
38 // DoStuff should verify that it's called on a valid sequenced thread.
39 // SequenceCheckedObject can be destroyed on any thread (like WeakPtr).
40 class SequenceCheckedObject {
41 public:
SequenceCheckedObject()42 SequenceCheckedObject() {}
~SequenceCheckedObject()43 ~SequenceCheckedObject() {}
44
45 // Verifies that it was called on the same thread as the constructor.
DoStuff()46 void DoStuff() {
47 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
48 }
49
DetachFromSequence()50 void DetachFromSequence() {
51 sequence_checker_.DetachFromSequence();
52 }
53
54 private:
55 SequenceChecker sequence_checker_;
56
57 DISALLOW_COPY_AND_ASSIGN(SequenceCheckedObject);
58 };
59
60 class SequenceCheckerTest : public testing::Test {
61 public:
SequenceCheckerTest()62 SequenceCheckerTest() : other_thread_("sequence_checker_test_other_thread") {}
63
SetUp()64 void SetUp() override {
65 other_thread_.Start();
66 ResetPool();
67 }
68
TearDown()69 void TearDown() override {
70 other_thread_.Stop();
71 }
72
73 protected:
other_thread()74 base::Thread* other_thread() { return &other_thread_; }
75
pool()76 const scoped_refptr<SequencedWorkerPool>& pool() {
77 return pool_owner_->pool();
78 }
79
PostDoStuffToWorkerPool(SequenceCheckedObject * sequence_checked_object,const std::string & token_name)80 void PostDoStuffToWorkerPool(SequenceCheckedObject* sequence_checked_object,
81 const std::string& token_name) {
82 pool()->PostNamedSequencedWorkerTask(
83 token_name,
84 FROM_HERE,
85 base::Bind(&SequenceCheckedObject::DoStuff,
86 base::Unretained(sequence_checked_object)));
87 }
88
PostDoStuffToOtherThread(SequenceCheckedObject * sequence_checked_object)89 void PostDoStuffToOtherThread(
90 SequenceCheckedObject* sequence_checked_object) {
91 other_thread()->task_runner()->PostTask(
92 FROM_HERE, base::Bind(&SequenceCheckedObject::DoStuff,
93 base::Unretained(sequence_checked_object)));
94 }
95
PostDeleteToOtherThread(std::unique_ptr<SequenceCheckedObject> sequence_checked_object)96 void PostDeleteToOtherThread(
97 std::unique_ptr<SequenceCheckedObject> sequence_checked_object) {
98 other_thread()->message_loop()->task_runner()->DeleteSoon(
99 FROM_HERE, sequence_checked_object.release());
100 }
101
102 // Destroys the SequencedWorkerPool instance, blocking until it is fully shut
103 // down, and creates a new instance.
ResetPool()104 void ResetPool() {
105 pool_owner_.reset(new SequencedWorkerPoolOwner(kNumWorkerThreads, "test"));
106 }
107
108 void MethodOnDifferentThreadDeathTest();
109 void DetachThenCallFromDifferentThreadDeathTest();
110 void DifferentSequenceTokensDeathTest();
111 void WorkerPoolAndSimpleThreadDeathTest();
112 void TwoDifferentWorkerPoolsDeathTest();
113
114 private:
115 MessageLoop message_loop_; // Needed by SequencedWorkerPool to function.
116 base::Thread other_thread_;
117 std::unique_ptr<SequencedWorkerPoolOwner> pool_owner_;
118 };
119
TEST_F(SequenceCheckerTest,CallsAllowedOnSameThread)120 TEST_F(SequenceCheckerTest, CallsAllowedOnSameThread) {
121 std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
122 new SequenceCheckedObject);
123
124 // Verify that DoStuff doesn't assert.
125 sequence_checked_object->DoStuff();
126
127 // Verify that the destructor doesn't assert.
128 sequence_checked_object.reset();
129 }
130
TEST_F(SequenceCheckerTest,DestructorAllowedOnDifferentThread)131 TEST_F(SequenceCheckerTest, DestructorAllowedOnDifferentThread) {
132 std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
133 new SequenceCheckedObject);
134
135 // Verify the destructor doesn't assert when called on a different thread.
136 PostDeleteToOtherThread(std::move(sequence_checked_object));
137 other_thread()->Stop();
138 }
139
TEST_F(SequenceCheckerTest,DetachFromSequence)140 TEST_F(SequenceCheckerTest, DetachFromSequence) {
141 std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
142 new SequenceCheckedObject);
143
144 // Verify that DoStuff doesn't assert when called on a different thread after
145 // a call to DetachFromSequence.
146 sequence_checked_object->DetachFromSequence();
147
148 PostDoStuffToOtherThread(sequence_checked_object.get());
149 other_thread()->Stop();
150 }
151
TEST_F(SequenceCheckerTest,SameSequenceTokenValid)152 TEST_F(SequenceCheckerTest, SameSequenceTokenValid) {
153 std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
154 new SequenceCheckedObject);
155
156 sequence_checked_object->DetachFromSequence();
157 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
158 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
159 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
160 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
161 pool()->FlushForTesting();
162
163 PostDeleteToOtherThread(std::move(sequence_checked_object));
164 other_thread()->Stop();
165 }
166
TEST_F(SequenceCheckerTest,DetachSequenceTokenValid)167 TEST_F(SequenceCheckerTest, DetachSequenceTokenValid) {
168 std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
169 new SequenceCheckedObject);
170
171 sequence_checked_object->DetachFromSequence();
172 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
173 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
174 pool()->FlushForTesting();
175
176 sequence_checked_object->DetachFromSequence();
177 PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
178 PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
179 pool()->FlushForTesting();
180
181 PostDeleteToOtherThread(std::move(sequence_checked_object));
182 other_thread()->Stop();
183 }
184
185 #if GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER
186
MethodOnDifferentThreadDeathTest()187 void SequenceCheckerTest::MethodOnDifferentThreadDeathTest() {
188 std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
189 new SequenceCheckedObject);
190
191 // DoStuff should assert in debug builds only when called on a
192 // different thread.
193 PostDoStuffToOtherThread(sequence_checked_object.get());
194 other_thread()->Stop();
195 }
196
197 #if ENABLE_SEQUENCE_CHECKER
TEST_F(SequenceCheckerTest,MethodNotAllowedOnDifferentThreadDeathTestInDebug)198 TEST_F(SequenceCheckerTest, MethodNotAllowedOnDifferentThreadDeathTestInDebug) {
199 // The default style "fast" does not support multi-threaded tests.
200 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
201 ASSERT_DEATH({
202 MethodOnDifferentThreadDeathTest();
203 }, "");
204 }
205 #else
TEST_F(SequenceCheckerTest,MethodAllowedOnDifferentThreadDeathTestInRelease)206 TEST_F(SequenceCheckerTest, MethodAllowedOnDifferentThreadDeathTestInRelease) {
207 MethodOnDifferentThreadDeathTest();
208 }
209 #endif // ENABLE_SEQUENCE_CHECKER
210
DetachThenCallFromDifferentThreadDeathTest()211 void SequenceCheckerTest::DetachThenCallFromDifferentThreadDeathTest() {
212 std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
213 new SequenceCheckedObject);
214
215 // DoStuff doesn't assert when called on a different thread
216 // after a call to DetachFromSequence.
217 sequence_checked_object->DetachFromSequence();
218 PostDoStuffToOtherThread(sequence_checked_object.get());
219 other_thread()->Stop();
220
221 // DoStuff should assert in debug builds only after moving to
222 // another thread.
223 sequence_checked_object->DoStuff();
224 }
225
226 #if ENABLE_SEQUENCE_CHECKER
TEST_F(SequenceCheckerTest,DetachFromSequenceDeathTestInDebug)227 TEST_F(SequenceCheckerTest, DetachFromSequenceDeathTestInDebug) {
228 // The default style "fast" does not support multi-threaded tests.
229 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
230 ASSERT_DEATH({
231 DetachThenCallFromDifferentThreadDeathTest();
232 }, "");
233 }
234 #else
TEST_F(SequenceCheckerTest,DetachFromThreadDeathTestInRelease)235 TEST_F(SequenceCheckerTest, DetachFromThreadDeathTestInRelease) {
236 DetachThenCallFromDifferentThreadDeathTest();
237 }
238 #endif // ENABLE_SEQUENCE_CHECKER
239
DifferentSequenceTokensDeathTest()240 void SequenceCheckerTest::DifferentSequenceTokensDeathTest() {
241 std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
242 new SequenceCheckedObject);
243
244 sequence_checked_object->DetachFromSequence();
245 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
246 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
247 PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
248 PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
249 pool()->FlushForTesting();
250
251 PostDeleteToOtherThread(std::move(sequence_checked_object));
252 other_thread()->Stop();
253 }
254
255 #if ENABLE_SEQUENCE_CHECKER
TEST_F(SequenceCheckerTest,DifferentSequenceTokensDeathTestInDebug)256 TEST_F(SequenceCheckerTest, DifferentSequenceTokensDeathTestInDebug) {
257 // The default style "fast" does not support multi-threaded tests.
258 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
259 ASSERT_DEATH({
260 DifferentSequenceTokensDeathTest();
261 }, "");
262 }
263 #else
TEST_F(SequenceCheckerTest,DifferentSequenceTokensDeathTestInRelease)264 TEST_F(SequenceCheckerTest, DifferentSequenceTokensDeathTestInRelease) {
265 DifferentSequenceTokensDeathTest();
266 }
267 #endif // ENABLE_SEQUENCE_CHECKER
268
WorkerPoolAndSimpleThreadDeathTest()269 void SequenceCheckerTest::WorkerPoolAndSimpleThreadDeathTest() {
270 std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
271 new SequenceCheckedObject);
272
273 sequence_checked_object->DetachFromSequence();
274 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
275 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
276 pool()->FlushForTesting();
277
278 PostDoStuffToOtherThread(sequence_checked_object.get());
279 other_thread()->Stop();
280 }
281
282 #if ENABLE_SEQUENCE_CHECKER
TEST_F(SequenceCheckerTest,WorkerPoolAndSimpleThreadDeathTestInDebug)283 TEST_F(SequenceCheckerTest, WorkerPoolAndSimpleThreadDeathTestInDebug) {
284 // The default style "fast" does not support multi-threaded tests.
285 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
286 ASSERT_DEATH({
287 WorkerPoolAndSimpleThreadDeathTest();
288 }, "");
289 }
290 #else
TEST_F(SequenceCheckerTest,WorkerPoolAndSimpleThreadDeathTestInRelease)291 TEST_F(SequenceCheckerTest, WorkerPoolAndSimpleThreadDeathTestInRelease) {
292 WorkerPoolAndSimpleThreadDeathTest();
293 }
294 #endif // ENABLE_SEQUENCE_CHECKER
295
TwoDifferentWorkerPoolsDeathTest()296 void SequenceCheckerTest::TwoDifferentWorkerPoolsDeathTest() {
297 std::unique_ptr<SequenceCheckedObject> sequence_checked_object(
298 new SequenceCheckedObject);
299
300 sequence_checked_object->DetachFromSequence();
301 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
302 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
303 pool()->FlushForTesting();
304
305 SequencedWorkerPoolOwner second_pool_owner(kNumWorkerThreads, "test2");
306 second_pool_owner.pool()->PostNamedSequencedWorkerTask(
307 "A",
308 FROM_HERE,
309 base::Bind(&SequenceCheckedObject::DoStuff,
310 base::Unretained(sequence_checked_object.get())));
311 second_pool_owner.pool()->FlushForTesting();
312 }
313
314 #if ENABLE_SEQUENCE_CHECKER
TEST_F(SequenceCheckerTest,TwoDifferentWorkerPoolsDeathTestInDebug)315 TEST_F(SequenceCheckerTest, TwoDifferentWorkerPoolsDeathTestInDebug) {
316 // The default style "fast" does not support multi-threaded tests.
317 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
318 ASSERT_DEATH({
319 TwoDifferentWorkerPoolsDeathTest();
320 }, "");
321 }
322 #else
TEST_F(SequenceCheckerTest,TwoDifferentWorkerPoolsDeathTestInRelease)323 TEST_F(SequenceCheckerTest, TwoDifferentWorkerPoolsDeathTestInRelease) {
324 TwoDifferentWorkerPoolsDeathTest();
325 }
326 #endif // ENABLE_SEQUENCE_CHECKER
327
328 #endif // GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER
329
330 } // namespace
331
332 } // namespace base
333
334 // Just in case we ever get lumped together with other compilation units.
335 #undef ENABLE_SEQUENCE_CHECKER
336