diff options
-rw-r--r-- | base/atomic_flag.cc | 28 | ||||
-rw-r--r-- | base/atomic_flag.h | 37 | ||||
-rw-r--r-- | base/atomic_flag_unittest.cc | 79 | ||||
-rw-r--r-- | base/base.gyp | 3 |
4 files changed, 147 insertions, 0 deletions
diff --git a/base/atomic_flag.cc b/base/atomic_flag.cc new file mode 100644 index 0000000..3e02543 --- /dev/null +++ b/base/atomic_flag.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2009 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/atomic_flag.h" + +#include "base/dynamic_annotations.h" +#include "base/logging.h" + +namespace base { + +void AtomicFlag::Set() { + DCHECK(!flag_); + // Set() method can't be called if the flag has already been set. + + ANNOTATE_HAPPENS_BEFORE(&flag_); + base::subtle::Release_Store(&flag_, 1); +} + +bool AtomicFlag::IsSet() const { + bool ret = base::subtle::Acquire_Load(&flag_) != 0; + if (ret) { + ANNOTATE_HAPPENS_AFTER(&flag_); + } + return ret; +} + +} // namespace base diff --git a/base/atomic_flag.h b/base/atomic_flag.h new file mode 100644 index 0000000..ee33bf2 --- /dev/null +++ b/base/atomic_flag.h @@ -0,0 +1,37 @@ +// Copyright (c) 2009 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. + +#ifndef BASE_ATOMIC_FLAG_H_ +#define BASE_ATOMIC_FLAG_H_ + +#include "base/atomicops.h" + +namespace base { + +// AtomicFlag allows threads to notify each other for a single occurrence +// of a single event. It maintains an abstract boolean "flag" that transitions +// to true at most once. It provides calls to query the boolean. +// +// Memory ordering: For any threads X and Y, if X calls Set(), then any +// action taken by X before it calls Set() is visible to thread Y after +// Y receives a true return value from IsSet(). +class AtomicFlag { + public: + // Sets "flag_" to "initial_value". + explicit AtomicFlag(bool initial_value = false) + : flag_(initial_value ? 1 : 0) { } + ~AtomicFlag() {} + + void Set(); // Set "flag_" to true. May be called only once. + bool IsSet() const; // Return "flag_". + + private: + base::subtle::Atomic32 flag_; + + DISALLOW_COPY_AND_ASSIGN(AtomicFlag); +}; + +} // namespace base + +#endif // BASE_ATOMIC_FLAG_H_ diff --git a/base/atomic_flag_unittest.cc b/base/atomic_flag_unittest.cc new file mode 100644 index 0000000..6f53b4b --- /dev/null +++ b/base/atomic_flag_unittest.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2009 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. + +// Tests of AtomicFlag class. + +#include "base/atomic_flag.h" +#include "base/logging.h" +#include "base/spin_wait.h" +#include "base/time.h" +#include "base/thread.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +using base::AtomicFlag; +using base::TimeDelta; +using base::Thread; + +namespace { + +//------------------------------------------------------------------------------ +// Define our test class. +//------------------------------------------------------------------------------ + +class NotifyTask : public Task { + public: + explicit NotifyTask(AtomicFlag* flag) : flag_(flag) { + } + virtual void Run() { + flag_->Set(); + } + private: + AtomicFlag* flag_; +}; + +TEST(AtomicFlagTest, SimpleSingleThreadedTest) { + AtomicFlag flag; + CHECK(!flag.IsSet()); + flag.Set(); + CHECK(flag.IsSet()); +} + +TEST(AtomicFlagTest, SimpleSingleThreadedTestPrenotified) { + AtomicFlag flag(true); + CHECK(flag.IsSet()); +} + +#if defined(OS_WIN) +#define DISABLED_ON_WIN(x) DISABLED_##x +#else +#define DISABLED_ON_WIN(x) x +#endif + +// AtomicFlag should die on a DCHECK if Set() is called more than once. +// This test isn't Windows-friendly yet since ASSERT_DEATH doesn't catch tests +// failed on DCHECK. See http://crbug.com/24885 for the details. +TEST(AtomicFlagTest, DISABLED_ON_WIN(DoubleSetDeathTest)) { + // Checks that Set() can't be called more than once. + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + AtomicFlag flag; + flag.Set(); + ASSERT_DEBUG_DEATH(flag.Set(), ""); +} + +TEST(AtomicFlagTest, SimpleThreadedTest) { + Thread t("AtomicFlagTest.SimpleThreadedTest"); + EXPECT_TRUE(t.Start()); + EXPECT_TRUE(t.message_loop()); + EXPECT_TRUE(t.IsRunning()); + + AtomicFlag flag; + CHECK(!flag.IsSet()); + t.message_loop()->PostTask(FROM_HERE, new NotifyTask(&flag)); + SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromSeconds(10), + flag.IsSet()); + CHECK(flag.IsSet()); +} + +} // namespace diff --git a/base/base.gyp b/base/base.gyp index ed29a26..c423484 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -63,6 +63,8 @@ 'atomicops_internals_x86_gcc.cc', 'at_exit.cc', 'at_exit.h', + 'atomic_flag.h', + 'atomic_flag.cc', 'atomic_ref_count.h', 'atomic_sequence_num.h', 'atomicops.h', @@ -552,6 +554,7 @@ # Tests. 'at_exit_unittest.cc', + 'atomic_flag_unittest.cc', 'atomicops_unittest.cc', 'command_line_unittest.cc', 'condition_variable_unittest.cc', |