Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(624)

Unified Diff: base/synchronization/atomic_flag_unittest.cc

Issue 2187333002: Loosen/improve AtomicFlag semantics. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: side-effects Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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));
}

Powered by Google App Engine
This is Rietveld 408576698