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

base / synchronization / atomic_flag_unittest.cc [blame]

// 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