#include "base/threading/thread_collision_warner.h"
#include <memory>
#include "base/compiler_specific.h"
#include "base/dcheck_is_on.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 "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#if !DCHECK_IS_ON()
#undef DFAKE_MUTEX
#define DFAKE_MUTEX(obj) std::unique_ptr<base::AsserterBase> obj
#define EXPECT_NDCHECK_FALSE_DCHECK_TRUE EXPECT_FALSE
#else
#define EXPECT_NDCHECK_FALSE_DCHECK_TRUE EXPECT_TRUE
#endif
namespace {
class AssertReporter : public base::AsserterBase {
public:
AssertReporter() = default;
void warn() override { failed_ = true; }
~AssertReporter() override = default;
bool fail_state() const { return failed_; }
void reset() { failed_ = false; }
private:
bool failed_ = false;
};
}
TEST(ThreadCollisionTest, BookCriticalSection) {
AssertReporter* local_reporter = new AssertReporter();
base::ThreadCollisionWarner warner(local_reporter);
EXPECT_FALSE(local_reporter->fail_state());
{
DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
EXPECT_FALSE(local_reporter->fail_state());
{
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());
{
DFAKE_SCOPED_RECURSIVE_LOCK(warner);
EXPECT_FALSE(local_reporter->fail_state());
{
DFAKE_SCOPED_RECURSIVE_LOCK(warner);
EXPECT_FALSE(local_reporter->fail_state());
}
}
{
DFAKE_SCOPED_LOCK(warner);
EXPECT_FALSE(local_reporter->fail_state());
}
}
TEST(ThreadCollisionTest, ScopedBookCriticalSection) {
AssertReporter* local_reporter = new AssertReporter();
base::ThreadCollisionWarner warner(local_reporter);
EXPECT_FALSE(local_reporter->fail_state());
{
DFAKE_SCOPED_LOCK(warner);
EXPECT_FALSE(local_reporter->fail_state());
}
{
DFAKE_SCOPED_LOCK(warner);
EXPECT_FALSE(local_reporter->fail_state());
{
DFAKE_SCOPED_LOCK(warner);
EXPECT_NDCHECK_FALSE_DCHECK_TRUE(local_reporter->fail_state());
local_reporter->reset();
}
}
{
DFAKE_SCOPED_LOCK(warner);
EXPECT_FALSE(local_reporter->fail_state());
}
}
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_NDCHECK_FALSE_DCHECK_TRUE(local_reporter->fail_state());
}
#ifndef THREAD_SANITIZER
TEST(ThreadCollisionTest, MTScopedBookCriticalSectionTest) {
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_NDCHECK_FALSE_DCHECK_TRUE(local_reporter->fail_state());
}
#endif
TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) {
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_);
};
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) {
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_);
};
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());
}