1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
// Copyright 2011 The Chromium Authors
// 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/check_op.h"
#include "base/functional/bind.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/gtest_util.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "build/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(Milliseconds(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(Milliseconds(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(Milliseconds(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;
GTEST_FLAG_SET(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
|