diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-27 19:58:00 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-27 19:58:00 +0000 |
commit | 399ed423f1c7f84190a7030496ac76e9eaf945f6 (patch) | |
tree | 651cc32e6b10cdb5800e5ffd9cb01f4280acac4b /base | |
parent | 5f061c2ccd015d5c62b0d95021383e53b47e04c5 (diff) | |
download | chromium_src-399ed423f1c7f84190a7030496ac76e9eaf945f6.zip chromium_src-399ed423f1c7f84190a7030496ac76e9eaf945f6.tar.gz chromium_src-399ed423f1c7f84190a7030496ac76e9eaf945f6.tar.bz2 |
Implement SequenceChecker, which is a generalization of ThreadChecker
SequenceChecker will be used in WeakPtr instead of ThreadChecker, since
WeakPtr needs only the guarantees of a SequencedTaskRunner.
Add NullTaskRunner implementation and make test_browser_context.cc
use it.
BUG=165590
Review URL: https://codereview.chromium.org/11550031
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@174693 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/base.gyp | 4 | ||||
-rw-r--r-- | base/base.gypi | 3 | ||||
-rw-r--r-- | base/sequence_checker.h | 80 | ||||
-rw-r--r-- | base/sequence_checker_impl.cc | 31 | ||||
-rw-r--r-- | base/sequence_checker_impl.h | 58 | ||||
-rw-r--r-- | base/sequence_checker_impl_unittest.cc | 218 | ||||
-rw-r--r-- | base/sequence_checker_unittest.cc | 29 | ||||
-rw-r--r-- | base/test/null_task_runner.cc | 31 | ||||
-rw-r--r-- | base/test/null_task_runner.h | 34 |
9 files changed, 488 insertions, 0 deletions
diff --git a/base/base.gyp b/base/base.gyp index 51122c4..a584ae4 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -512,6 +512,8 @@ 'rand_util_unittest.cc', 'scoped_native_library_unittest.cc', 'scoped_observer.h', + 'sequence_checker_unittest.cc', + 'sequence_checker_impl_unittest.cc', 'sha1_unittest.cc', 'shared_memory_unittest.cc', 'stl_util_unittest.cc', @@ -784,6 +786,8 @@ 'test/multiprocess_test.cc', 'test/multiprocess_test.h', 'test/multiprocess_test_android.cc', + 'test/null_task_runner.cc', + 'test/null_task_runner.h', 'test/perf_test_suite.cc', 'test/perf_test_suite.h', 'test/scoped_locale.cc', diff --git a/base/base.gypi b/base/base.gypi index 6ea3a07..99e16ab 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -353,6 +353,9 @@ 'safe_strerror_posix.h', 'scoped_native_library.cc', 'scoped_native_library.h', + 'sequence_checker.h', + 'sequence_checker_impl.cc', + 'sequence_checker_impl.h', 'sequenced_task_runner.cc', 'sequenced_task_runner.h', 'sequenced_task_runner_helpers.h', diff --git a/base/sequence_checker.h b/base/sequence_checker.h new file mode 100644 index 0000000..fbf146a --- /dev/null +++ b/base/sequence_checker.h @@ -0,0 +1,80 @@ +// Copyright (c) 2012 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_SEQUENCE_CHECKER_H_ +#define BASE_SEQUENCE_CHECKER_H_ + +#include "base/memory/ref_counted.h" + +// See comments for the similar block in thread_checker.h. +#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) +#define ENABLE_SEQUENCE_CHECKER 1 +#else +#define ENABLE_SEQUENCE_CHECKER 0 +#endif + +#if ENABLE_SEQUENCE_CHECKER +#include "base/sequence_checker_impl.h" +#endif + +namespace base { + +class SequencedTaskRunner; + +// Do nothing implementation, for use in release mode. +// +// Note: You should almost always use the SequenceChecker class to get +// the right version for your build configuration. +class SequenceCheckerDoNothing { + public: + bool CalledOnValidSequence() const { + return true; + } + + void ChangeSequence( + const scoped_refptr<SequencedTaskRunner>& sequenced_task_runner) {} +}; + +// SequenceChecker is a helper class used to help verify that some +// methods of a class are called in sequence -- that is, called from +// the same SequencedTaskRunner. It is a generalization of +// ThreadChecker; see comments in sequence_checker_impl.h for details. +// +// Example: +// class MyClass { +// public: +// explicit MyClass( +// const scoped_refptr<SequencedTaskRunner>& sequenced_task_runner) +// : sequence_checker_(sequenced_task_runner) {} +// +// void Foo() { +// DCHECK(sequence_checker_.CalledOnValidSequence()); +// ... (do stuff) ... +// } +// +// private: +// SequenceChecker sequence_checker_; +// } +// +// In Release mode, CalledOnValidSequence will always return true. +#if ENABLE_SEQUENCE_CHECKER +class SequenceChecker : public SequenceCheckerImpl { + public: + explicit SequenceChecker( + const scoped_refptr<SequencedTaskRunner>& sequenced_task_runner) + : SequenceCheckerImpl(sequenced_task_runner) {} +}; +#else +class SequenceChecker : public SequenceCheckerDoNothing { + public: + explicit SequenceChecker( + const scoped_refptr<SequencedTaskRunner>& sequenced_task_runner) {} +}; +#endif // ENABLE_SEQUENCE_CHECKER + +#undef ENABLE_SEQUENCE_CHECKER + +} // namespace base + +#endif // BASE_SEQUENCE_CHECKER_H_ diff --git a/base/sequence_checker_impl.cc b/base/sequence_checker_impl.cc new file mode 100644 index 0000000..24d9ed9 --- /dev/null +++ b/base/sequence_checker_impl.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2012 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/sequence_checker_impl.h" + +#include "base/sequenced_task_runner.h" + +namespace base { + +SequenceCheckerImpl::SequenceCheckerImpl( + const scoped_refptr<SequencedTaskRunner>& sequenced_task_runner) + : sequenced_task_runner_(sequenced_task_runner) {} + +SequenceCheckerImpl::~SequenceCheckerImpl() {} + +bool SequenceCheckerImpl::CalledOnValidSequence() const { + AutoLock auto_lock(lock_); + return sequenced_task_runner_.get() ? + sequenced_task_runner_->RunsTasksOnCurrentThread() : + thread_checker_.CalledOnValidThread(); +} + +void SequenceCheckerImpl::ChangeSequence( + const scoped_refptr<SequencedTaskRunner>& sequenced_task_runner) { + AutoLock auto_lock(lock_); + sequenced_task_runner_ = sequenced_task_runner; + thread_checker_.DetachFromThread(); +} + +} // namespace base diff --git a/base/sequence_checker_impl.h b/base/sequence_checker_impl.h new file mode 100644 index 0000000..ccd1198 --- /dev/null +++ b/base/sequence_checker_impl.h @@ -0,0 +1,58 @@ +// Copyright (c) 2012 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_SEQUENCE_CHECKER_IMPL_H_ +#define BASE_SEQUENCE_CHECKER_IMPL_H_ + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "base/threading/thread_checker_impl.h" + +namespace base { + +class SequencedTaskRunner; + +// SequenceCheckerImpl is used to help verify that some methods of a +// class are called in sequence -- that is, called from the same +// SequencedTaskRunner. It is a generalization of ThreadChecker; in +// particular, it behaves exactly like ThreadChecker if its passed a +// NULL SequencedTaskRunner. +class BASE_EXPORT SequenceCheckerImpl { + public: + // |sequenced_task_runner| can be NULL. In that case, this object + // behaves exactly like a ThreadChecker bound to the current thread, + // i.e. CalledOnValidSequence() behaves like CalledOnValidThread(). + explicit SequenceCheckerImpl( + const scoped_refptr<SequencedTaskRunner>& sequenced_task_runner); + ~SequenceCheckerImpl(); + + // Returns whether the we are being called on the underyling + // SequencedTaskRunner. If we're not bound to a + // |sequenced_task_runner|, returns whether we are being called on + // the underlying ThreadChecker's thread. + bool CalledOnValidSequence() const; + + // Changes the underyling SequencedTaskRunner. + // |sequenced_task_runner| can be NULL. In that case, this object + // behaves exactly like a ThreadChecker that has been detached from + // its thread, i.e. we will be bound to the thread on which we next + // call CalledOnValidSequence(). + void ChangeSequence( + const scoped_refptr<SequencedTaskRunner>& sequenced_task_runner); + + private: + // Guards all variables below. + mutable Lock lock_; + scoped_refptr<SequencedTaskRunner> sequenced_task_runner_; + // Used if |sequenced_task_runner_| is NULL. + ThreadCheckerImpl thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(SequenceCheckerImpl); +}; + +} // namespace base + +#endif // BASE_SEQUENCE_CHECKER_IMPL_H_ diff --git a/base/sequence_checker_impl_unittest.cc b/base/sequence_checker_impl_unittest.cc new file mode 100644 index 0000000..4e601b0 --- /dev/null +++ b/base/sequence_checker_impl_unittest.cc @@ -0,0 +1,218 @@ +// Copyright (c) 2012 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/sequence_checker_impl.h" + +#include <cstddef> + +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/location.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop.h" +#include "base/sequenced_task_runner.h" +#include "base/threading/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +namespace { + +// Implementation of SequencedTaskRunner that lets us control what +// RunsTasksOnCurrentThread() returns. +class FakeTaskRunner : public SequencedTaskRunner { + public: + FakeTaskRunner() : runs_tasks_on_current_thread_(false) {} + + void SetRunsTasksOnCurrentThread(bool runs_tasks_on_current_thread) { + runs_tasks_on_current_thread_ = runs_tasks_on_current_thread; + } + + // SequencedTaskRunner implementation. + virtual bool PostDelayedTask(const tracked_objects::Location& from_here, + const Closure& task, + TimeDelta delay) OVERRIDE { + ADD_FAILURE(); + return false; + } + + virtual bool PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, + const Closure& task, + TimeDelta delay) OVERRIDE { + ADD_FAILURE(); + return false; + } + + virtual bool RunsTasksOnCurrentThread() const OVERRIDE { + return runs_tasks_on_current_thread_; + } + + protected: + virtual ~FakeTaskRunner() {} + + private: + bool runs_tasks_on_current_thread_; +}; + +class SequenceCheckerImplTest : public ::testing::Test { +}; + +// Create a SequenceCheckerImpl with a SequencedTaskRunner and make +// sure that CalledOnValidSequence() returns what that SequencedTaskRunner +// returns for RunsTasksOnCurrentThread(). +TEST_F(SequenceCheckerImplTest, CalledOnValidSequenceNonNull) { + const scoped_refptr<FakeTaskRunner> fake_sequenced_task_runner( + new FakeTaskRunner()); + + const SequenceCheckerImpl sequence_checker_impl(fake_sequenced_task_runner); + EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence()); + + fake_sequenced_task_runner->SetRunsTasksOnCurrentThread(true); + EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence()); + + fake_sequenced_task_runner->SetRunsTasksOnCurrentThread(false); + EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence()); +} + +void ExpectCalledOnValidSequence( + const tracked_objects::Location& location, + const SequenceCheckerImpl* sequence_checker_impl, + bool expected_value) { + EXPECT_EQ(expected_value, sequence_checker_impl->CalledOnValidSequence()) + << location.ToString(); +} + +// Create a SequenceCheckerImpl with no SequencedTaskRunner and make +// sure that CalledOnValidSequence() behaves like +// ThreadChecker::CalledOnValidThread(). +TEST_F(SequenceCheckerImplTest, CalledOnValidSequenceNull) { + const SequenceCheckerImpl sequence_checker_impl(NULL); + EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence()); + + { + Thread thread("thread 1"); + ASSERT_TRUE(thread.Start()); + thread.message_loop()->PostTask( + FROM_HERE, Bind(&ExpectCalledOnValidSequence, + FROM_HERE, + Unretained(&sequence_checker_impl), + false)); + } + + EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence()); +} + +// Create a SequenceCheckerImpl with a SequencedTaskRunner and switch +// it to another one. CalledOnValidSequence() should return what its +// underlying SequencedTaskRunner returns for +// RunsTasksOnCurrentThread(). +TEST_F(SequenceCheckerImplTest, ChangeSequenceNonNull) { + const scoped_refptr<FakeTaskRunner> fake_sequenced_task_runner1( + new FakeTaskRunner()); + + const scoped_refptr<FakeTaskRunner> fake_sequenced_task_runner2( + new FakeTaskRunner()); + + SequenceCheckerImpl sequence_checker_impl(fake_sequenced_task_runner1); + EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence()); + + fake_sequenced_task_runner2->SetRunsTasksOnCurrentThread(true); + EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence()); + + sequence_checker_impl.ChangeSequence(fake_sequenced_task_runner2); + EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence()); + + sequence_checker_impl.ChangeSequence(fake_sequenced_task_runner1); + EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence()); +} + +// Create a SequenceCheckerImpl with a SequencedTaskRunner and switch +// it to a NULL one. CalledOnValidSequence() should then behave like +// ThreadChecker::CalledOnValidThread(). +TEST_F(SequenceCheckerImplTest, ChangeSequenceNull) { + const scoped_refptr<FakeTaskRunner> fake_sequenced_task_runner( + new FakeTaskRunner()); + + SequenceCheckerImpl sequence_checker_impl(fake_sequenced_task_runner); + EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence()); + + sequence_checker_impl.ChangeSequence(NULL); + // Binds to current thread. + EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence()); + { + Thread thread("thread 1"); + ASSERT_TRUE(thread.Start()); + thread.message_loop()->PostTask( + FROM_HERE, Bind(&ExpectCalledOnValidSequence, + FROM_HERE, + Unretained(&sequence_checker_impl), + false)); + } + + EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence()); + + sequence_checker_impl.ChangeSequence(NULL); + // Binds to worker thread. + { + Thread thread("thread 2"); + ASSERT_TRUE(thread.Start()); + thread.message_loop()->PostTask( + FROM_HERE, Bind(&ExpectCalledOnValidSequence, + FROM_HERE, + Unretained(&sequence_checker_impl), + true)); + } + EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence()); +} + +// Create a SequenceCheckerImpl with the current thread's task runner +// and switch it to other task runners. CalledOnValidSequence() should +// return true only when it's on the correct thread. +TEST_F(SequenceCheckerImplTest, MultipleThreads) { + MessageLoop loop; + + SequenceCheckerImpl sequence_checker_impl(loop.message_loop_proxy()); + EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence()); + + { + Thread thread("thread 1"); + ASSERT_TRUE(thread.Start()); + thread.message_loop()->PostTask( + FROM_HERE, Bind(&ExpectCalledOnValidSequence, + FROM_HERE, + Unretained(&sequence_checker_impl), + false)); + thread.message_loop()->PostTask( + FROM_HERE, Bind(&SequenceCheckerImpl::ChangeSequence, + Unretained(&sequence_checker_impl), + thread.message_loop_proxy())); + thread.message_loop()->PostTask( + FROM_HERE, Bind(&ExpectCalledOnValidSequence, + FROM_HERE, + Unretained(&sequence_checker_impl), + true)); + } + + EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence()); + + sequence_checker_impl.ChangeSequence(loop.message_loop_proxy()); + EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence()); + + { + Thread thread("thread 2"); + ASSERT_TRUE(thread.Start()); + thread.message_loop()->PostTask( + FROM_HERE, Bind(&ExpectCalledOnValidSequence, + FROM_HERE, + Unretained(&sequence_checker_impl), + false)); + } + + EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence()); +} + +} // namespace + +} // namespace base diff --git a/base/sequence_checker_unittest.cc b/base/sequence_checker_unittest.cc new file mode 100644 index 0000000..4fc3027 --- /dev/null +++ b/base/sequence_checker_unittest.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2012 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/sequence_checker.h" + +#include <cstddef> + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/test/null_task_runner.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +namespace { + +// Call various methods of SequenceChecker to make sure nothing blows +// up in either debug or release mode. +TEST(SequenceCheckerTest, Basic) { + SequenceChecker sequence_checker(new NullTaskRunner()); + sequence_checker.CalledOnValidSequence(); + sequence_checker.ChangeSequence(NULL); + sequence_checker.CalledOnValidSequence(); +} + +} // namespace + +} // namespace base diff --git a/base/test/null_task_runner.cc b/base/test/null_task_runner.cc new file mode 100644 index 0000000..bf43e6d --- /dev/null +++ b/base/test/null_task_runner.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2012 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/test/null_task_runner.h" + +namespace base { + +NullTaskRunner::NullTaskRunner() {} + +NullTaskRunner::~NullTaskRunner() {} + +bool NullTaskRunner::PostDelayedTask( + const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) { + return false; +} + +bool NullTaskRunner::PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) { + return false; +} + +bool NullTaskRunner::RunsTasksOnCurrentThread() const { + return true; +} + +} // namespace diff --git a/base/test/null_task_runner.h b/base/test/null_task_runner.h new file mode 100644 index 0000000..d6390e5 --- /dev/null +++ b/base/test/null_task_runner.h @@ -0,0 +1,34 @@ +// Copyright (c) 2012 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/basictypes.h" +#include "base/compiler_specific.h" +#include "base/single_thread_task_runner.h" + +namespace base { + +// Helper class for tests that need to provide an implementation of a +// *TaskRunner class but don't actually care about tasks being run. + +class NullTaskRunner : public base::SingleThreadTaskRunner { + public: + NullTaskRunner(); + + virtual bool PostDelayedTask(const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) OVERRIDE; + virtual bool PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) OVERRIDE; + // Always returns true to avoid triggering DCHECKs. + virtual bool RunsTasksOnCurrentThread() const OVERRIDE; + + protected: + virtual ~NullTaskRunner(); + + DISALLOW_COPY_AND_ASSIGN(NullTaskRunner); +}; + +} // namespace |