Chromium Code Reviews| Index: base/synchronization/atomic_flag_unittest.cc |
| diff --git a/base/synchronization/atomic_flag_unittest.cc b/base/synchronization/atomic_flag_unittest.cc |
| index b0dfd47e4653970789739e24126946bd5c790a1f..7bcc680ff6b474cebbc552ee56d6efeea2eb5047 100644 |
| --- a/base/synchronization/atomic_flag_unittest.cc |
| +++ b/base/synchronization/atomic_flag_unittest.cc |
| @@ -31,9 +31,14 @@ void ExpectSetFlagDeath(AtomicFlag* flag) { |
| EXPECT_DCHECK_DEATH(flag->Set(), ""); |
| } |
| -void BusyWaitUntilFlagIsSet(AtomicFlag* flag) { |
| +// Busy waits (to explicitly avoid using synchronization constructs that would |
| +// defeat the purpose of testing atomics) until |flag| is set and then invokes |
| +// |callback|. |
| +void BusyWaitUntilFlagIsSet(AtomicFlag* flag, const base::Closure& callback) { |
|
danakj
2016/07/28 19:57:22
nit: this would be easier to read if you just pass
gab
2016/08/01 17:46:45
Done.
|
| while (!flag->IsSet()) |
| PlatformThread::YieldCurrentThread(); |
| + |
| + callback.Run(); |
| } |
| } // namespace |
| @@ -55,32 +60,79 @@ TEST(AtomicFlagTest, DoubleSetTest) { |
| } |
| TEST(AtomicFlagTest, ReadFromDifferentThread) { |
| - AtomicFlag flag; |
| + // |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; |
| + |
| + const base::Closure verify_and_signal = Bind( |
| + [](bool* expected_after_flag, AtomicFlag* reset_flag) { |
| + EXPECT_TRUE(*expected_after_flag); |
| + reset_flag->Set(); |
| + }, |
| + &expected_after_flag, &reset_flag); |
| Thread thread("AtomicFlagTest.ReadFromDifferentThread"); |
| ASSERT_TRUE(thread.Start()); |
| - thread.task_runner()->PostTask(FROM_HERE, |
| - Bind(&BusyWaitUntilFlagIsSet, &flag)); |
| + thread.task_runner()->PostTask( |
| + FROM_HERE, |
| + Bind(&BusyWaitUntilFlagIsSet, &tested_flag, verify_and_signal)); |
| // 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(25)); |
| + PlatformThread::Sleep(TimeDelta::FromMilliseconds(20)); |
| - flag.Set(); |
| + // |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). |
| + ASSERT_TRUE(reset_flag.IsSet()); |
| + |
| + tested_flag.UnsafeReset(); |
| + EXPECT_FALSE(tested_flag.IsSet()); |
| + expected_after_flag = false; |
| + |
| + // Perform the same test again after the controlled UnsafeReset(), |thread| is |
| + // guaranteed to be synchronized past the |UnsafeReset()| call when the task |
| + // runs per the implicit synchronization in the post task mechanism. |
| + thread.task_runner()->PostTask( |
| + FROM_HERE, |
| + Bind(&BusyWaitUntilFlagIsSet, &tested_flag, verify_and_signal)); |
| + |
| + 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, SetOnDifferentThreadDeathTest) { |
| - // Checks that Set() can't be called from any other thread. AtomicFlag should |
| - // die on a DCHECK if Set() is called from other thread. |
| +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. |
| ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| Thread t("AtomicFlagTest.SetOnDifferentThreadDeathTest"); |
| ASSERT_TRUE(t.Start()); |
| + EXPECT_TRUE(t.WaitUntilThreadStarted()); |
| AtomicFlag flag; |
| + flag.Set(); |
| t.task_runner()->PostTask(FROM_HERE, Bind(&ExpectSetFlagDeath, &flag)); |
| } |