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