diff options
author | maruel@chromium.org <maruel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-17 02:25:44 +0000 |
---|---|---|
committer | maruel@chromium.org <maruel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-17 02:25:44 +0000 |
commit | 2d4537d50684e97458ca6d27e98e742533bb1141 (patch) | |
tree | 90141a594a60c4f55e512cacbe5ca736f7ee2354 /base/thread_collision_warner_unittest.cc | |
parent | 3b680a8b28f63a13f82fbb5e7324fba69c28d75d (diff) | |
download | chromium_src-2d4537d50684e97458ca6d27e98e742533bb1141.zip chromium_src-2d4537d50684e97458ca6d27e98e742533bb1141.tar.gz chromium_src-2d4537d50684e97458ca6d27e98e742533bb1141.tar.bz2 |
Adds a helper class used to mark/define critical section in a class and then install controls to check that those critical sections are not violated.
This CL is due the thread posted on chromium-dev:
http://groups.google.com/group/chromium-dev/browse_frm/thread/30af0b63b6adb245.
From Gaetano Mendola <mendola bigfoot com>
Review URL: http://codereview.chromium.org/8621
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@7127 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/thread_collision_warner_unittest.cc')
-rw-r--r-- | base/thread_collision_warner_unittest.cc | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/base/thread_collision_warner_unittest.cc b/base/thread_collision_warner_unittest.cc new file mode 100644 index 0000000..7994d13 --- /dev/null +++ b/base/thread_collision_warner_unittest.cc @@ -0,0 +1,360 @@ +// Copyright (c) 2008 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/compiler_specific.h" +#include "base/lock.h" +#include "base/platform_thread.h" +#include "base/simple_thread.h" +#include "base/thread_collision_warner.h" +#include "testing/gtest/include/gtest/gtest.h" + +// '' : local class member function does not have a body +MSVC_PUSH_DISABLE_WARNING(4822) + +namespace { + +// This is the asserter used with ThreadCollisionWarner instead of the default +// DCheckAsserter. The method fail_state is used to know if a collision took +// place. +class AssertReporter : public base::AsserterBase { + public: + AssertReporter() + : failed_(false) {} + + virtual void warn() { + failed_ = true; + } + + virtual ~AssertReporter() {} + + bool fail_state() const { return failed_; } + void reset() { failed_ = false; } + + private: + bool failed_; +}; + +} // namespace + +TEST(ThreadCollisionTest, BookCriticalSection) { + AssertReporter* local_reporter = new AssertReporter(); + + base::ThreadCollisionWarner warner(local_reporter); + EXPECT_FALSE(local_reporter->fail_state()); + + { // Pin section. + DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner); + EXPECT_FALSE(local_reporter->fail_state()); + { // Pin section. + DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner); + EXPECT_FALSE(local_reporter->fail_state()); + } + } +} + +TEST(ThreadCollisionTest, ScopedRecursiveBookCriticalSection) { + AssertReporter* local_reporter = new AssertReporter(); + + base::ThreadCollisionWarner warner(local_reporter); + EXPECT_FALSE(local_reporter->fail_state()); + + { // Pin section. + DFAKE_SCOPED_RECURSIVE_LOCK(warner); + EXPECT_FALSE(local_reporter->fail_state()); + { // Pin section again (allowed by DFAKE_SCOPED_RECURSIVE_LOCK) + DFAKE_SCOPED_RECURSIVE_LOCK(warner); + EXPECT_FALSE(local_reporter->fail_state()); + } // Unpin section. + } // Unpin section. + + // Check that section is not pinned + { // Pin section. + DFAKE_SCOPED_LOCK(warner); + EXPECT_FALSE(local_reporter->fail_state()); + } // Unpin section. +} + +TEST(ThreadCollisionTest, ScopedBookCriticalSection) { + AssertReporter* local_reporter = new AssertReporter(); + + base::ThreadCollisionWarner warner(local_reporter); + EXPECT_FALSE(local_reporter->fail_state()); + + { // Pin section. + DFAKE_SCOPED_LOCK(warner); + EXPECT_FALSE(local_reporter->fail_state()); + } // Unpin section. + + { // Pin section. + DFAKE_SCOPED_LOCK(warner); + EXPECT_FALSE(local_reporter->fail_state()); + { // Pin section again (not allowed by DFAKE_SCOPED_LOCK) + DFAKE_SCOPED_LOCK(warner); + EXPECT_TRUE(local_reporter->fail_state()); + // Reset the status of warner for further tests. + local_reporter->reset(); + } // Unpin section. + } // Unpin section. + + { // Pin section. + DFAKE_SCOPED_LOCK(warner); + EXPECT_FALSE(local_reporter->fail_state()); + } // Unpin section. +} + +TEST(ThreadCollisionTest, MTBookCriticalSectionTest) { + class NonThreadSafeQueue { + public: + explicit NonThreadSafeQueue(base::AsserterBase* asserter) + : push_pop_(asserter) { } + + void push(int value) { + DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); + } + + int pop() { + DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); + return 0; + } + + private: + DFAKE_MUTEX(push_pop_); + + DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue); + }; + + class QueueUser : public base::DelegateSimpleThread::Delegate { + public: + explicit QueueUser(NonThreadSafeQueue& queue) + : queue_(queue) {} + + virtual void Run() { + queue_.push(0); + queue_.pop(); + } + + private: + NonThreadSafeQueue& queue_; + }; + + AssertReporter* local_reporter = new AssertReporter(); + + NonThreadSafeQueue queue(local_reporter); + + QueueUser queue_user_a(queue); + QueueUser queue_user_b(queue); + + base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); + base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); + + thread_a.Start(); + thread_b.Start(); + + thread_a.Join(); + thread_b.Join(); + + EXPECT_TRUE(local_reporter->fail_state()); +} + +TEST(ThreadCollisionTest, MTScopedBookCriticalSectionTest) { + // Queue with a 5 seconds push execution time, hopefuly the two used threads + // in the test will enter the push at same time. + class NonThreadSafeQueue { + public: + explicit NonThreadSafeQueue(base::AsserterBase* asserter) + : push_pop_(asserter) { } + + void push(int value) { + DFAKE_SCOPED_LOCK(push_pop_); + PlatformThread::Sleep(5000); + } + + int pop() { + DFAKE_SCOPED_LOCK(push_pop_); + return 0; + } + + private: + DFAKE_MUTEX(push_pop_); + + DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue); + }; + + class QueueUser : public base::DelegateSimpleThread::Delegate { + public: + explicit QueueUser(NonThreadSafeQueue& queue) + : queue_(queue) {} + + virtual void Run() { + queue_.push(0); + queue_.pop(); + } + + private: + NonThreadSafeQueue& queue_; + }; + + AssertReporter* local_reporter = new AssertReporter(); + + NonThreadSafeQueue queue(local_reporter); + + QueueUser queue_user_a(queue); + QueueUser queue_user_b(queue); + + base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); + base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); + + thread_a.Start(); + thread_b.Start(); + + thread_a.Join(); + thread_b.Join(); + + EXPECT_TRUE(local_reporter->fail_state()); +} + +TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) { + // Queue with a 5 seconds push execution time, hopefuly the two used threads + // in the test will enter the push at same time. + class NonThreadSafeQueue { + public: + explicit NonThreadSafeQueue(base::AsserterBase* asserter) + : push_pop_(asserter) { } + + void push(int value) { + DFAKE_SCOPED_LOCK(push_pop_); + PlatformThread::Sleep(5000); + } + + int pop() { + DFAKE_SCOPED_LOCK(push_pop_); + return 0; + } + + private: + DFAKE_MUTEX(push_pop_); + + DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue); + }; + + // This time the QueueUser class protects the non thread safe queue with + // a lock. + class QueueUser : public base::DelegateSimpleThread::Delegate { + public: + QueueUser(NonThreadSafeQueue& queue, Lock& lock) + : queue_(queue), + lock_(lock) {} + + virtual void Run() { + { + AutoLock auto_lock(lock_); + queue_.push(0); + } + { + AutoLock auto_lock(lock_); + queue_.pop(); + } + } + private: + NonThreadSafeQueue& queue_; + Lock& lock_; + }; + + AssertReporter* local_reporter = new AssertReporter(); + + NonThreadSafeQueue queue(local_reporter); + + Lock lock; + + QueueUser queue_user_a(queue, lock); + QueueUser queue_user_b(queue, lock); + + base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); + base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); + + thread_a.Start(); + thread_b.Start(); + + thread_a.Join(); + thread_b.Join(); + + EXPECT_FALSE(local_reporter->fail_state()); +} + +TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) { + // Queue with a 5 seconds push execution time, hopefuly the two used threads + // in the test will enter the push at same time. + class NonThreadSafeQueue { + public: + explicit NonThreadSafeQueue(base::AsserterBase* asserter) + : push_pop_(asserter) { } + + void push(int) { + DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); + bar(); + PlatformThread::Sleep(5000); + } + + int pop() { + DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); + return 0; + } + + void bar() { + DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); + } + + private: + DFAKE_MUTEX(push_pop_); + + DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue); + }; + + // This time the QueueUser class protects the non thread safe queue with + // a lock. + class QueueUser : public base::DelegateSimpleThread::Delegate { + public: + QueueUser(NonThreadSafeQueue& queue, Lock& lock) + : queue_(queue), + lock_(lock) {} + + virtual void Run() { + { + AutoLock auto_lock(lock_); + queue_.push(0); + } + { + AutoLock auto_lock(lock_); + queue_.bar(); + } + { + AutoLock auto_lock(lock_); + queue_.pop(); + } + } + private: + NonThreadSafeQueue& queue_; + Lock& lock_; + }; + + AssertReporter* local_reporter = new AssertReporter(); + + NonThreadSafeQueue queue(local_reporter); + + Lock lock; + + QueueUser queue_user_a(queue, lock); + QueueUser queue_user_b(queue, lock); + + base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); + base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); + + thread_a.Start(); + thread_b.Start(); + + thread_a.Join(); + thread_b.Join(); + + EXPECT_FALSE(local_reporter->fail_state()); +} |