• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/synchronization/atomic_flag.h"
6 
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/synchronization/waitable_event.h"
11 #include "base/test/gtest_util.h"
12 #include "base/threading/platform_thread.h"
13 #include "base/threading/thread.h"
14 #include "build/build_config.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 namespace base {
18 
19 namespace {
20 
ExpectSetFlagDeath(AtomicFlag * flag)21 void ExpectSetFlagDeath(AtomicFlag* flag) {
22   ASSERT_TRUE(flag);
23   EXPECT_DCHECK_DEATH(flag->Set());
24 }
25 
26 // Busy waits (to explicitly avoid using synchronization constructs that would
27 // defeat the purpose of testing atomics) until |tested_flag| is set and then
28 // verifies that non-atomic |*expected_after_flag| is true and sets |*done_flag|
29 // before returning if it's non-null.
BusyWaitUntilFlagIsSet(AtomicFlag * tested_flag,bool * expected_after_flag,AtomicFlag * done_flag)30 void BusyWaitUntilFlagIsSet(AtomicFlag* tested_flag, bool* expected_after_flag,
31                             AtomicFlag* done_flag) {
32   while (!tested_flag->IsSet())
33     PlatformThread::YieldCurrentThread();
34 
35   EXPECT_TRUE(*expected_after_flag);
36   if (done_flag)
37     done_flag->Set();
38 }
39 
40 }  // namespace
41 
TEST(AtomicFlagTest,SimpleSingleThreadedTest)42 TEST(AtomicFlagTest, SimpleSingleThreadedTest) {
43   AtomicFlag flag;
44   ASSERT_FALSE(flag.IsSet());
45   flag.Set();
46   ASSERT_TRUE(flag.IsSet());
47 }
48 
TEST(AtomicFlagTest,DoubleSetTest)49 TEST(AtomicFlagTest, DoubleSetTest) {
50   AtomicFlag flag;
51   ASSERT_FALSE(flag.IsSet());
52   flag.Set();
53   ASSERT_TRUE(flag.IsSet());
54   flag.Set();
55   ASSERT_TRUE(flag.IsSet());
56 }
57 
TEST(AtomicFlagTest,ReadFromDifferentThread)58 TEST(AtomicFlagTest, ReadFromDifferentThread) {
59   // |tested_flag| is the one being tested below.
60   AtomicFlag tested_flag;
61   // |expected_after_flag| is used to confirm that sequential consistency is
62   // obtained around |tested_flag|.
63   bool expected_after_flag = false;
64   // |reset_flag| is used to confirm the test flows as intended without using
65   // synchronization constructs which would defeat the purpose of exercising
66   // atomics.
67   AtomicFlag reset_flag;
68 
69   Thread thread("AtomicFlagTest.ReadFromDifferentThread");
70   ASSERT_TRUE(thread.Start());
71   thread.task_runner()->PostTask(FROM_HERE,
72                                  BindOnce(&BusyWaitUntilFlagIsSet, &tested_flag,
73                                           &expected_after_flag, &reset_flag));
74 
75   // To verify that IsSet() fetches the flag's value from memory every time it
76   // is called (not just the first time that it is called on a thread), sleep
77   // before setting the flag.
78   PlatformThread::Sleep(TimeDelta::FromMilliseconds(20));
79 
80   // |expected_after_flag| is used to verify that all memory operations
81   // performed before |tested_flag| is Set() are visible to threads that can see
82   // IsSet().
83   expected_after_flag = true;
84   tested_flag.Set();
85 
86   // Sleep again to give the busy loop time to observe the flag and verify
87   // expectations.
88   PlatformThread::Sleep(TimeDelta::FromMilliseconds(20));
89 
90   // Use |reset_flag| to confirm that the above completed (which the rest of
91   // this test assumes).
92   while (!reset_flag.IsSet())
93     PlatformThread::YieldCurrentThread();
94 
95   tested_flag.UnsafeResetForTesting();
96   EXPECT_FALSE(tested_flag.IsSet());
97   expected_after_flag = false;
98 
99   // Perform the same test again after the controlled UnsafeResetForTesting(),
100   // |thread| is guaranteed to be synchronized past the
101   // |UnsafeResetForTesting()| call when the task runs per the implicit
102   // synchronization in the post task mechanism.
103   thread.task_runner()->PostTask(FROM_HERE,
104                                  BindOnce(&BusyWaitUntilFlagIsSet, &tested_flag,
105                                           &expected_after_flag, nullptr));
106 
107   PlatformThread::Sleep(TimeDelta::FromMilliseconds(20));
108 
109   expected_after_flag = true;
110   tested_flag.Set();
111 
112   // The |thread|'s destructor will block until the posted task completes, so
113   // the test will time out if it fails to see the flag be set.
114 }
115 
TEST(AtomicFlagTest,SetOnDifferentSequenceDeathTest)116 TEST(AtomicFlagTest, SetOnDifferentSequenceDeathTest) {
117   // Checks that Set() can't be called from another sequence after being called
118   // on this one. AtomicFlag should die on a DCHECK if Set() is called again
119   // from another sequence.
120 
121   // Note: flag must be declared before the Thread so that its destructor runs
122   // later. Otherwise there's a race between destructing flag and running
123   // ExpectSetFlagDeath.
124   AtomicFlag flag;
125 
126   ::testing::FLAGS_gtest_death_test_style = "threadsafe";
127   Thread t("AtomicFlagTest.SetOnDifferentThreadDeathTest");
128   ASSERT_TRUE(t.Start());
129   EXPECT_TRUE(t.WaitUntilThreadStarted());
130 
131   flag.Set();
132   t.task_runner()->PostTask(FROM_HERE, BindOnce(&ExpectSetFlagDeath, &flag));
133 }
134 
135 }  // namespace base
136