| // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/synchronization/atomic_flag.h" | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/logging.h" | 
 | #include "base/single_thread_task_runner.h" | 
 | #include "base/synchronization/waitable_event.h" | 
 | #include "base/test/gtest_util.h" | 
 | #include "base/threading/platform_thread.h" | 
 | #include "base/threading/thread.h" | 
 | #include "build_config.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | namespace base { | 
 |  | 
 | namespace { | 
 |  | 
 | void ExpectSetFlagDeath(AtomicFlag* flag) { | 
 |   ASSERT_TRUE(flag); | 
 |   EXPECT_DCHECK_DEATH(flag->Set()); | 
 | } | 
 |  | 
 | // Busy waits (to explicitly avoid using synchronization constructs that would | 
 | // defeat the purpose of testing atomics) until |tested_flag| is set and then | 
 | // verifies that non-atomic |*expected_after_flag| is true and sets |*done_flag| | 
 | // before returning if it's non-null. | 
 | void BusyWaitUntilFlagIsSet(AtomicFlag* tested_flag, bool* expected_after_flag, | 
 |                             AtomicFlag* done_flag) { | 
 |   while (!tested_flag->IsSet()) | 
 |     PlatformThread::YieldCurrentThread(); | 
 |  | 
 |   EXPECT_TRUE(*expected_after_flag); | 
 |   if (done_flag) | 
 |     done_flag->Set(); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | TEST(AtomicFlagTest, SimpleSingleThreadedTest) { | 
 |   AtomicFlag flag; | 
 |   ASSERT_FALSE(flag.IsSet()); | 
 |   flag.Set(); | 
 |   ASSERT_TRUE(flag.IsSet()); | 
 | } | 
 |  | 
 | TEST(AtomicFlagTest, DoubleSetTest) { | 
 |   AtomicFlag flag; | 
 |   ASSERT_FALSE(flag.IsSet()); | 
 |   flag.Set(); | 
 |   ASSERT_TRUE(flag.IsSet()); | 
 |   flag.Set(); | 
 |   ASSERT_TRUE(flag.IsSet()); | 
 | } | 
 |  | 
 | TEST(AtomicFlagTest, ReadFromDifferentThread) { | 
 |   // |tested_flag| is the one being tested below. | 
 |   AtomicFlag tested_flag; | 
 |   // |expected_after_flag| is used to confirm that sequential consistency is | 
 |   // obtained around |tested_flag|. | 
 |   bool expected_after_flag = false; | 
 |   // |reset_flag| is used to confirm the test flows as intended without using | 
 |   // synchronization constructs which would defeat the purpose of exercising | 
 |   // atomics. | 
 |   AtomicFlag reset_flag; | 
 |  | 
 |   Thread thread("AtomicFlagTest.ReadFromDifferentThread"); | 
 |   ASSERT_TRUE(thread.Start()); | 
 |   thread.task_runner()->PostTask(FROM_HERE, | 
 |                                  BindOnce(&BusyWaitUntilFlagIsSet, &tested_flag, | 
 |                                           &expected_after_flag, &reset_flag)); | 
 |  | 
 |   // To verify that IsSet() fetches the flag's value from memory every time it | 
 |   // is called (not just the first time that it is called on a thread), sleep | 
 |   // before setting the flag. | 
 |   PlatformThread::Sleep(TimeDelta::FromMilliseconds(20)); | 
 |  | 
 |   // |expected_after_flag| is used to verify that all memory operations | 
 |   // performed before |tested_flag| is Set() are visible to threads that can see | 
 |   // IsSet(). | 
 |   expected_after_flag = true; | 
 |   tested_flag.Set(); | 
 |  | 
 |   // Sleep again to give the busy loop time to observe the flag and verify | 
 |   // expectations. | 
 |   PlatformThread::Sleep(TimeDelta::FromMilliseconds(20)); | 
 |  | 
 |   // Use |reset_flag| to confirm that the above completed (which the rest of | 
 |   // this test assumes). | 
 |   while (!reset_flag.IsSet()) | 
 |     PlatformThread::YieldCurrentThread(); | 
 |  | 
 |   tested_flag.UnsafeResetForTesting(); | 
 |   EXPECT_FALSE(tested_flag.IsSet()); | 
 |   expected_after_flag = false; | 
 |  | 
 |   // Perform the same test again after the controlled UnsafeResetForTesting(), | 
 |   // |thread| is guaranteed to be synchronized past the | 
 |   // |UnsafeResetForTesting()| call when the task runs per the implicit | 
 |   // synchronization in the post task mechanism. | 
 |   thread.task_runner()->PostTask(FROM_HERE, | 
 |                                  BindOnce(&BusyWaitUntilFlagIsSet, &tested_flag, | 
 |                                           &expected_after_flag, nullptr)); | 
 |  | 
 |   PlatformThread::Sleep(TimeDelta::FromMilliseconds(20)); | 
 |  | 
 |   expected_after_flag = true; | 
 |   tested_flag.Set(); | 
 |  | 
 |   // The |thread|'s destructor will block until the posted task completes, so | 
 |   // the test will time out if it fails to see the flag be set. | 
 | } | 
 |  | 
 | TEST(AtomicFlagTest, SetOnDifferentSequenceDeathTest) { | 
 |   // Checks that Set() can't be called from another sequence after being called | 
 |   // on this one. AtomicFlag should die on a DCHECK if Set() is called again | 
 |   // from another sequence. | 
 |  | 
 |   // Note: flag must be declared before the Thread so that its destructor runs | 
 |   // later. Otherwise there's a race between destructing flag and running | 
 |   // ExpectSetFlagDeath. | 
 |   AtomicFlag flag; | 
 |  | 
 |   ::testing::FLAGS_gtest_death_test_style = "threadsafe"; | 
 |   Thread t("AtomicFlagTest.SetOnDifferentThreadDeathTest"); | 
 |   ASSERT_TRUE(t.Start()); | 
 |   EXPECT_TRUE(t.WaitUntilThreadStarted()); | 
 |  | 
 |   flag.Set(); | 
 |   t.task_runner()->PostTask(FROM_HERE, BindOnce(&ExpectSetFlagDeath, &flag)); | 
 | } | 
 |  | 
 | }  // namespace base |