You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
4.6 KiB
136 lines
4.6 KiB
// 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/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
|