123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- // 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/threading/thread_collision_warner.h"
- #include <memory>
- #include "base/compiler_specific.h"
- #include "base/memory/raw_ptr.h"
- #include "base/synchronization/lock.h"
- #include "base/threading/platform_thread.h"
- #include "base/threading/simple_thread.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #if defined(NDEBUG)
- // Would cause a memory leak otherwise.
- #undef DFAKE_MUTEX
- #define DFAKE_MUTEX(obj) std::unique_ptr<base::AsserterBase> obj
- // In Release, we expect the AsserterBase::warn() to not happen.
- #define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_FALSE
- #else
- // In Debug, we expect the AsserterBase::warn() to happen.
- #define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_TRUE
- #endif
- 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) {}
- void warn() override { failed_ = true; }
- ~AssertReporter() override = default;
- 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_NDEBUG_FALSE_DEBUG_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) {
- }
- NonThreadSafeQueue(const NonThreadSafeQueue&) = delete;
- NonThreadSafeQueue& operator=(const NonThreadSafeQueue&) = delete;
- 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_);
- };
- class QueueUser : public base::DelegateSimpleThread::Delegate {
- public:
- explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {}
- void Run() override {
- queue_->push(0);
- queue_->pop();
- }
- private:
- raw_ptr<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_NDEBUG_FALSE_DEBUG_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) {
- }
- NonThreadSafeQueue(const NonThreadSafeQueue&) = delete;
- NonThreadSafeQueue& operator=(const NonThreadSafeQueue&) = delete;
- void push(int value) {
- DFAKE_SCOPED_LOCK(push_pop_);
- base::PlatformThread::Sleep(base::Seconds(5));
- }
- int pop() {
- DFAKE_SCOPED_LOCK(push_pop_);
- return 0;
- }
- private:
- DFAKE_MUTEX(push_pop_);
- };
- class QueueUser : public base::DelegateSimpleThread::Delegate {
- public:
- explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {}
- void Run() override {
- queue_->push(0);
- queue_->pop();
- }
- private:
- raw_ptr<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_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
- }
- TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) {
- // Queue with a 2 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) {
- }
- NonThreadSafeQueue(const NonThreadSafeQueue&) = delete;
- NonThreadSafeQueue& operator=(const NonThreadSafeQueue&) = delete;
- void push(int value) {
- DFAKE_SCOPED_LOCK(push_pop_);
- base::PlatformThread::Sleep(base::Seconds(2));
- }
- int pop() {
- DFAKE_SCOPED_LOCK(push_pop_);
- return 0;
- }
- private:
- DFAKE_MUTEX(push_pop_);
- };
- // This time the QueueUser class protects the non thread safe queue with
- // a lock.
- class QueueUser : public base::DelegateSimpleThread::Delegate {
- public:
- QueueUser(NonThreadSafeQueue* queue, base::Lock* lock)
- : queue_(queue), lock_(lock) {}
- void Run() override {
- {
- base::AutoLock auto_lock(*lock_);
- queue_->push(0);
- }
- {
- base::AutoLock auto_lock(*lock_);
- queue_->pop();
- }
- }
- private:
- raw_ptr<NonThreadSafeQueue> queue_;
- raw_ptr<base::Lock> lock_;
- };
- AssertReporter* local_reporter = new AssertReporter();
- NonThreadSafeQueue queue(local_reporter);
- base::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 2 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) {
- }
- NonThreadSafeQueue(const NonThreadSafeQueue&) = delete;
- NonThreadSafeQueue& operator=(const NonThreadSafeQueue&) = delete;
- void push(int) {
- DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
- bar();
- base::PlatformThread::Sleep(base::Seconds(2));
- }
- int pop() {
- DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
- return 0;
- }
- void bar() {
- DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
- }
- private:
- DFAKE_MUTEX(push_pop_);
- };
- // This time the QueueUser class protects the non thread safe queue with
- // a lock.
- class QueueUser : public base::DelegateSimpleThread::Delegate {
- public:
- QueueUser(NonThreadSafeQueue* queue, base::Lock* lock)
- : queue_(queue), lock_(lock) {}
- void Run() override {
- {
- base::AutoLock auto_lock(*lock_);
- queue_->push(0);
- }
- {
- base::AutoLock auto_lock(*lock_);
- queue_->bar();
- }
- {
- base::AutoLock auto_lock(*lock_);
- queue_->pop();
- }
- }
- private:
- raw_ptr<NonThreadSafeQueue> queue_;
- raw_ptr<base::Lock> lock_;
- };
- AssertReporter* local_reporter = new AssertReporter();
- NonThreadSafeQueue queue(local_reporter);
- base::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());
- }
|