summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authortommycli@chromium.org <tommycli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-30 19:26:40 +0000
committertommycli@chromium.org <tommycli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-30 19:26:40 +0000
commitd52426cf3429ee05807a97c7c22ed16494abb774 (patch)
treea7c9aa2c1b3974c8e985e577afbce7cc1803276a /base
parent71316604afb68ed5926ff25dc952c4d14000d2c1 (diff)
downloadchromium_src-d52426cf3429ee05807a97c7c22ed16494abb774.zip
chromium_src-d52426cf3429ee05807a97c7c22ed16494abb774.tar.gz
chromium_src-d52426cf3429ee05807a97c7c22ed16494abb774.tar.bz2
base: Re-re-apply WeakPtr support for SequencedWorkerPools
This is a continuation of https://codereview.chromium.org/20163004/#ps55001 and identical to Patchset 4. I'm adding back some death test threadsafe flags that got dropped from the re-apply. In other words, this CL is: https://codereview.chromium.org/20163004/#ps55001 + https://codereview.chromium.org/19617002/ Feel free to revert if it causes issues again. TBR=akalin,darin,dewittj,rnk BUG=165590 Review URL: https://codereview.chromium.org/20754006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@214411 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r--base/base.gyp1
-rw-r--r--base/memory/weak_ptr.cc12
-rw-r--r--base/memory/weak_ptr.h6
-rw-r--r--base/message_loop/message_pump_io_ios.h1
-rw-r--r--base/sequence_checker.h16
-rw-r--r--base/sequence_checker_impl.cc39
-rw-r--r--base/sequence_checker_impl.h42
-rw-r--r--base/sequence_checker_impl_unittest.cc218
-rw-r--r--base/sequence_checker_unittest.cc338
-rw-r--r--base/threading/sequenced_worker_pool.cc4
10 files changed, 385 insertions, 292 deletions
diff --git a/base/base.gyp b/base/base.gyp
index 6f22d5a..15776f3 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -571,7 +571,6 @@
'scoped_observer.h',
'security_unittest.cc',
'sequence_checker_unittest.cc',
- 'sequence_checker_impl_unittest.cc',
'sha1_unittest.cc',
'stl_util_unittest.cc',
'strings/nullable_string16_unittest.cc',
diff --git a/base/memory/weak_ptr.cc b/base/memory/weak_ptr.cc
index a22f61a..d9ce86a 100644
--- a/base/memory/weak_ptr.cc
+++ b/base/memory/weak_ptr.cc
@@ -10,21 +10,21 @@ namespace internal {
WeakReference::Flag::Flag() : is_valid_(true) {
// Flags only become bound when checked for validity, or invalidated,
// so that we can check that later validity/invalidation operations on
- // the same Flag take place on the same thread.
- thread_checker_.DetachFromThread();
+ // the same Flag take place on the same sequenced thread.
+ sequence_checker_.DetachFromSequence();
}
void WeakReference::Flag::Invalidate() {
// The flag being invalidated with a single ref implies that there are no
// weak pointers in existence. Allow deletion on other thread in this case.
- DCHECK(thread_checker_.CalledOnValidThread() || HasOneRef())
- << "WeakPtrs must be checked and invalidated on the same thread.";
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread() || HasOneRef())
+ << "WeakPtrs must be invalidated on the same sequenced thread.";
is_valid_ = false;
}
bool WeakReference::Flag::IsValid() const {
- DCHECK(thread_checker_.CalledOnValidThread())
- << "WeakPtrs must be checked and invalidated on the same thread.";
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread())
+ << "WeakPtrs must be checked on the same sequenced thread.";
return is_valid_;
}
diff --git a/base/memory/weak_ptr.h b/base/memory/weak_ptr.h
index 621c0fa..b4120a1 100644
--- a/base/memory/weak_ptr.h
+++ b/base/memory/weak_ptr.h
@@ -67,8 +67,8 @@
#include "base/base_export.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
#include "base/template_util.h"
-#include "base/threading/thread_checker.h"
namespace base {
@@ -91,14 +91,14 @@ class BASE_EXPORT WeakReference {
bool IsValid() const;
// Remove this when crbug.com/234964 is addressed.
- void DetachFromThreadHack() { thread_checker_.DetachFromThread(); }
+ void DetachFromThreadHack() { sequence_checker_.DetachFromSequence(); }
private:
friend class base::RefCountedThreadSafe<Flag>;
~Flag();
- ThreadChecker thread_checker_;
+ SequenceChecker sequence_checker_;
bool is_valid_;
};
diff --git a/base/message_loop/message_pump_io_ios.h b/base/message_loop/message_pump_io_ios.h
index 52de9fd..18af4a8 100644
--- a/base/message_loop/message_pump_io_ios.h
+++ b/base/message_loop/message_pump_io_ios.h
@@ -12,6 +12,7 @@
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_pump_mac.h"
#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
namespace base {
diff --git a/base/sequence_checker.h b/base/sequence_checker.h
index fbf146a..89bbd7e 100644
--- a/base/sequence_checker.h
+++ b/base/sequence_checker.h
@@ -28,12 +28,11 @@ class SequencedTaskRunner;
// the right version for your build configuration.
class SequenceCheckerDoNothing {
public:
- bool CalledOnValidSequence() const {
+ bool CalledOnValidSequencedThread() const {
return true;
}
- void ChangeSequence(
- const scoped_refptr<SequencedTaskRunner>& sequenced_task_runner) {}
+ void DetachFromSequence() {}
};
// SequenceChecker is a helper class used to help verify that some
@@ -44,10 +43,6 @@ class SequenceCheckerDoNothing {
// 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) ...
@@ -60,16 +55,9 @@ class SequenceCheckerDoNothing {
// 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
diff --git a/base/sequence_checker_impl.cc b/base/sequence_checker_impl.cc
index 24d9ed9..e95b8ee 100644
--- a/base/sequence_checker_impl.cc
+++ b/base/sequence_checker_impl.cc
@@ -4,28 +4,43 @@
#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()
+ : sequence_token_assigned_(false) {
+ AutoLock auto_lock(lock_);
+ EnsureSequenceTokenAssigned();
+}
SequenceCheckerImpl::~SequenceCheckerImpl() {}
-bool SequenceCheckerImpl::CalledOnValidSequence() const {
+bool SequenceCheckerImpl::CalledOnValidSequencedThread() const {
AutoLock auto_lock(lock_);
- return sequenced_task_runner_.get() ?
- sequenced_task_runner_->RunsTasksOnCurrentThread() :
- thread_checker_.CalledOnValidThread();
+ EnsureSequenceTokenAssigned();
+
+ // If this thread is not associated with a SequencedWorkerPool,
+ // SequenceChecker behaves as a ThreadChecker. See header for details.
+ if (!sequence_token_.IsValid())
+ return thread_checker_.CalledOnValidThread();
+
+ return sequence_token_.Equals(
+ SequencedWorkerPool::GetSequenceTokenForCurrentThread());
}
-void SequenceCheckerImpl::ChangeSequence(
- const scoped_refptr<SequencedTaskRunner>& sequenced_task_runner) {
+void SequenceCheckerImpl::DetachFromSequence() {
AutoLock auto_lock(lock_);
- sequenced_task_runner_ = sequenced_task_runner;
thread_checker_.DetachFromThread();
+ sequence_token_assigned_ = false;
+ sequence_token_ = SequencedWorkerPool::SequenceToken();
+}
+
+void SequenceCheckerImpl::EnsureSequenceTokenAssigned() const {
+ lock_.AssertAcquired();
+ if (sequence_token_assigned_)
+ return;
+
+ sequence_token_assigned_ = true;
+ sequence_token_ = SequencedWorkerPool::GetSequenceTokenForCurrentThread();
}
} // namespace base
diff --git a/base/sequence_checker_impl.h b/base/sequence_checker_impl.h
index ccd1198..741aafe 100644
--- a/base/sequence_checker_impl.h
+++ b/base/sequence_checker_impl.h
@@ -7,48 +7,42 @@
#include "base/base_export.h"
#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
+#include "base/threading/sequenced_worker_pool.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.
+// particular, it behaves exactly like ThreadChecker if constructed
+// on a thread that is not part of a SequencedWorkerPool.
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();
~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;
+ // Returns whether the we are being called on the same sequence token
+ // as previous calls. If there is no associated sequence, then returns
+ // whether we are being called on the underlying ThreadChecker's thread.
+ bool CalledOnValidSequencedThread() 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);
+ // Unbinds the checker from the currently associated sequence. The
+ // checker will be re-bound on the next call to CalledOnValidSequence().
+ void DetachFromSequence();
private:
+ void EnsureSequenceTokenAssigned() const;
+
// Guards all variables below.
mutable Lock lock_;
- scoped_refptr<SequencedTaskRunner> sequenced_task_runner_;
- // Used if |sequenced_task_runner_| is NULL.
+
+ // Used if |sequence_token_| is not valid.
ThreadCheckerImpl thread_checker_;
+ mutable bool sequence_token_assigned_;
+
+ mutable SequencedWorkerPool::SequenceToken sequence_token_;
DISALLOW_COPY_AND_ASSIGN(SequenceCheckerImpl);
};
diff --git a/base/sequence_checker_impl_unittest.cc b/base/sequence_checker_impl_unittest.cc
deleted file mode 100644
index b05da11..0000000
--- a/base/sequence_checker_impl_unittest.cc
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright 2013 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/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
index 4fc3027..7df7614 100644
--- a/base/sequence_checker_unittest.cc
+++ b/base/sequence_checker_unittest.cc
@@ -1,29 +1,339 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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/basictypes.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
#include "base/memory/ref_counted.h"
-#include "base/test/null_task_runner.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/sequence_checker.h"
+#include "base/test/sequenced_worker_pool_owner.h"
+#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
+// Duplicated from base/sequence_checker.h so that we can be good citizens
+// there and undef the macro.
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+#define ENABLE_SEQUENCE_CHECKER 1
+#else
+#define ENABLE_SEQUENCE_CHECKER 0
+#endif
+
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();
+const size_t kNumWorkerThreads = 3;
+
+// Simple class to exercise the basics of SequenceChecker.
+// DoStuff should verify that it's called on a valid sequenced thread.
+// SequenceCheckedObject can be destroyed on any thread (like WeakPtr).
+class SequenceCheckedObject {
+ public:
+ SequenceCheckedObject() {}
+ ~SequenceCheckedObject() {}
+
+ // Verifies that it was called on the same thread as the constructor.
+ void DoStuff() {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ }
+
+ void DetachFromSequence() {
+ sequence_checker_.DetachFromSequence();
+ }
+
+ private:
+ SequenceChecker sequence_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(SequenceCheckedObject);
+};
+
+class SequenceCheckerTest : public testing::Test {
+ public:
+ SequenceCheckerTest() : other_thread_("sequence_checker_test_other_thread") {}
+
+ virtual ~SequenceCheckerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ other_thread_.Start();
+ ResetPool();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ other_thread_.Stop();
+ pool()->Shutdown();
+ }
+
+ protected:
+ base::Thread* other_thread() { return &other_thread_; }
+
+ const scoped_refptr<SequencedWorkerPool>& pool() {
+ return pool_owner_->pool();
+ }
+
+ void PostDoStuffToWorkerPool(SequenceCheckedObject* sequence_checked_object,
+ const std::string& token_name) {
+ pool()->PostNamedSequencedWorkerTask(
+ token_name,
+ FROM_HERE,
+ base::Bind(&SequenceCheckedObject::DoStuff,
+ base::Unretained(sequence_checked_object)));
+ }
+
+ void PostDoStuffToOtherThread(
+ SequenceCheckedObject* sequence_checked_object) {
+ other_thread()->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&SequenceCheckedObject::DoStuff,
+ base::Unretained(sequence_checked_object)));
+ }
+
+ void PostDeleteToOtherThread(
+ scoped_ptr<SequenceCheckedObject> sequence_checked_object) {
+ other_thread()->message_loop()->DeleteSoon(
+ FROM_HERE,
+ sequence_checked_object.release());
+ }
+
+ // Destroys the SequencedWorkerPool instance, blocking until it is fully shut
+ // down, and creates a new instance.
+ void ResetPool() {
+ pool_owner_.reset(new SequencedWorkerPoolOwner(kNumWorkerThreads, "test"));
+ }
+
+ void MethodOnDifferentThreadDeathTest();
+ void DetachThenCallFromDifferentThreadDeathTest();
+ void DifferentSequenceTokensDeathTest();
+ void WorkerPoolAndSimpleThreadDeathTest();
+ void TwoDifferentWorkerPoolsDeathTest();
+
+ private:
+ MessageLoop message_loop_; // Needed by SequencedWorkerPool to function.
+ base::Thread other_thread_;
+ scoped_ptr<SequencedWorkerPoolOwner> pool_owner_;
+};
+
+TEST_F(SequenceCheckerTest, CallsAllowedOnSameThread) {
+ scoped_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
+
+ // Verify that DoStuff doesn't assert.
+ sequence_checked_object->DoStuff();
+
+ // Verify that the destructor doesn't assert.
+ sequence_checked_object.reset();
+}
+
+TEST_F(SequenceCheckerTest, DestructorAllowedOnDifferentThread) {
+ scoped_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
+
+ // Verify the destructor doesn't assert when called on a different thread.
+ PostDeleteToOtherThread(sequence_checked_object.Pass());
+ other_thread()->Stop();
+}
+
+TEST_F(SequenceCheckerTest, DetachFromSequence) {
+ scoped_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
+
+ // Verify that DoStuff doesn't assert when called on a different thread after
+ // a call to DetachFromSequence.
+ sequence_checked_object->DetachFromSequence();
+
+ PostDoStuffToOtherThread(sequence_checked_object.get());
+ other_thread()->Stop();
+}
+
+TEST_F(SequenceCheckerTest, SameSequenceTokenValid) {
+ scoped_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
+
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ pool()->FlushForTesting();
+
+ PostDeleteToOtherThread(sequence_checked_object.Pass());
+ other_thread()->Stop();
+}
+
+TEST_F(SequenceCheckerTest, DetachSequenceTokenValid) {
+ scoped_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
+
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ pool()->FlushForTesting();
+
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
+ pool()->FlushForTesting();
+
+ PostDeleteToOtherThread(sequence_checked_object.Pass());
+ other_thread()->Stop();
+}
+
+#if GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER
+
+void SequenceCheckerTest::MethodOnDifferentThreadDeathTest() {
+ scoped_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
+
+ // DoStuff should assert in debug builds only when called on a
+ // different thread.
+ PostDoStuffToOtherThread(sequence_checked_object.get());
+ other_thread()->Stop();
+}
+
+#if ENABLE_SEQUENCE_CHECKER
+TEST_F(SequenceCheckerTest, MethodNotAllowedOnDifferentThreadDeathTestInDebug) {
+ // The default style "fast" does not support multi-threaded tests.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ ASSERT_DEATH({
+ MethodOnDifferentThreadDeathTest();
+ }, "");
+}
+#else
+TEST_F(SequenceCheckerTest, MethodAllowedOnDifferentThreadDeathTestInRelease) {
+ MethodOnDifferentThreadDeathTest();
+}
+#endif // ENABLE_SEQUENCE_CHECKER
+
+void SequenceCheckerTest::DetachThenCallFromDifferentThreadDeathTest() {
+ scoped_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
+
+ // DoStuff doesn't assert when called on a different thread
+ // after a call to DetachFromSequence.
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToOtherThread(sequence_checked_object.get());
+ other_thread()->Stop();
+
+ // DoStuff should assert in debug builds only after moving to
+ // another thread.
+ sequence_checked_object->DoStuff();
+}
+
+#if ENABLE_SEQUENCE_CHECKER
+TEST_F(SequenceCheckerTest, DetachFromSequenceDeathTestInDebug) {
+ // The default style "fast" does not support multi-threaded tests.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ ASSERT_DEATH({
+ DetachThenCallFromDifferentThreadDeathTest();
+ }, "");
+}
+#else
+TEST_F(SequenceCheckerTest, DetachFromThreadDeathTestInRelease) {
+ DetachThenCallFromDifferentThreadDeathTest();
+}
+#endif // ENABLE_SEQUENCE_CHECKER
+
+void SequenceCheckerTest::DifferentSequenceTokensDeathTest() {
+ scoped_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
+
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
+ pool()->FlushForTesting();
+
+ PostDeleteToOtherThread(sequence_checked_object.Pass());
+ other_thread()->Stop();
}
+#if ENABLE_SEQUENCE_CHECKER
+TEST_F(SequenceCheckerTest, DifferentSequenceTokensDeathTestInDebug) {
+ // The default style "fast" does not support multi-threaded tests.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ ASSERT_DEATH({
+ DifferentSequenceTokensDeathTest();
+ }, "");
+}
+#else
+TEST_F(SequenceCheckerTest,
+ DifferentSequenceTokensDeathTestInRelease) {
+ DifferentSequenceTokensDeathTest();
+}
+#endif // ENABLE_SEQUENCE_CHECKER
+
+void SequenceCheckerTest::WorkerPoolAndSimpleThreadDeathTest() {
+ scoped_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
+
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ pool()->FlushForTesting();
+
+ PostDoStuffToOtherThread(sequence_checked_object.get());
+ other_thread()->Stop();
+}
+
+#if ENABLE_SEQUENCE_CHECKER
+TEST_F(SequenceCheckerTest, WorkerPoolAndSimpleThreadDeathTestInDebug) {
+ // The default style "fast" does not support multi-threaded tests.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ ASSERT_DEATH({
+ WorkerPoolAndSimpleThreadDeathTest();
+ }, "");
+}
+#else
+TEST_F(SequenceCheckerTest,
+ WorkerPoolAndSimpleThreadDeathTestInRelease) {
+ WorkerPoolAndSimpleThreadDeathTest();
+}
+#endif // ENABLE_SEQUENCE_CHECKER
+
+void SequenceCheckerTest::TwoDifferentWorkerPoolsDeathTest() {
+ scoped_ptr<SequenceCheckedObject> sequence_checked_object(
+ new SequenceCheckedObject);
+
+ sequence_checked_object->DetachFromSequence();
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
+ pool()->FlushForTesting();
+
+ SequencedWorkerPoolOwner second_pool_owner(kNumWorkerThreads, "test2");
+ second_pool_owner.pool()->PostNamedSequencedWorkerTask(
+ "A",
+ FROM_HERE,
+ base::Bind(&SequenceCheckedObject::DoStuff,
+ base::Unretained(sequence_checked_object.get())));
+ second_pool_owner.pool()->FlushForTesting();
+ second_pool_owner.pool()->Shutdown();
+}
+
+#if ENABLE_SEQUENCE_CHECKER
+TEST_F(SequenceCheckerTest, TwoDifferentWorkerPoolsDeathTestInDebug) {
+ // The default style "fast" does not support multi-threaded tests.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ ASSERT_DEATH({
+ TwoDifferentWorkerPoolsDeathTest();
+ }, "");
+}
+#else
+TEST_F(SequenceCheckerTest,
+ TwoDifferentWorkerPoolsDeathTestInRelease) {
+ TwoDifferentWorkerPoolsDeathTest();
+}
+#endif // ENABLE_SEQUENCE_CHECKER
+
+#endif // GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER
+
} // namespace
} // namespace base
+
+// Just in case we ever get lumped together with other compilation units.
+#undef ENABLE_SEQUENCE_CHECKER
diff --git a/base/threading/sequenced_worker_pool.cc b/base/threading/sequenced_worker_pool.cc
index 035ef2b..d992168 100644
--- a/base/threading/sequenced_worker_pool.cc
+++ b/base/threading/sequenced_worker_pool.cc
@@ -1124,6 +1124,10 @@ SequencedWorkerPool::Inner::g_last_sequence_number_;
// static
SequencedWorkerPool::SequenceToken
SequencedWorkerPool::GetSequenceTokenForCurrentThread() {
+ // Don't construct lazy instance on check.
+ if (g_lazy_tls_ptr == NULL)
+ return SequenceToken();
+
SequencedWorkerPool::SequenceToken* token = g_lazy_tls_ptr.Get().Get();
if (!token)
return SequenceToken();