• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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