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..48d55afde48e0b20bea76c1b670df5032a6f0dcf 100644 |
--- a/base/synchronization/atomic_flag_unittest.cc |
+++ b/base/synchronization/atomic_flag_unittest.cc |
@@ -31,9 +31,18 @@ void ExpectSetFlagDeath(AtomicFlag* flag) { |
EXPECT_DCHECK_DEATH(flag->Set(), ""); |
} |
-void BusyWaitUntilFlagIsSet(AtomicFlag* flag) { |
- while (!flag->IsSet()) |
+// 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 |
@@ -55,32 +64,75 @@ 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; |
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, &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(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.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, |
+ Bind(&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, 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)); |
} |