//===-- tsan_mutex.cpp ----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_atomic.h"
#include "tsan_interface.h"
#include "tsan_interface_ann.h"
#include "tsan_test_util.h"
#include "gtest/gtest.h"
#include <stdint.h>
namespace __tsan {
TEST_F(ThreadSanitizer, BasicMutex) {
ScopedThread t;
UserMutex m;
t.Create(m);
t.Lock(m);
t.Unlock(m);
CHECK(t.TryLock(m));
t.Unlock(m);
t.Lock(m);
CHECK(!t.TryLock(m));
t.Unlock(m);
t.Destroy(m);
}
TEST_F(ThreadSanitizer, BasicSpinMutex) {
ScopedThread t;
UserMutex m(UserMutex::Spin);
t.Create(m);
t.Lock(m);
t.Unlock(m);
CHECK(t.TryLock(m));
t.Unlock(m);
t.Lock(m);
CHECK(!t.TryLock(m));
t.Unlock(m);
t.Destroy(m);
}
TEST_F(ThreadSanitizer, BasicRwMutex) {
ScopedThread t;
UserMutex m(UserMutex::RW);
t.Create(m);
t.Lock(m);
t.Unlock(m);
CHECK(t.TryLock(m));
t.Unlock(m);
t.Lock(m);
CHECK(!t.TryLock(m));
t.Unlock(m);
t.ReadLock(m);
t.ReadUnlock(m);
CHECK(t.TryReadLock(m));
t.ReadUnlock(m);
t.Lock(m);
CHECK(!t.TryReadLock(m));
t.Unlock(m);
t.ReadLock(m);
CHECK(!t.TryLock(m));
t.ReadUnlock(m);
t.ReadLock(m);
CHECK(t.TryReadLock(m));
t.ReadUnlock(m);
t.ReadUnlock(m);
t.Destroy(m);
}
TEST_F(ThreadSanitizer, Mutex) {
UserMutex m;
MainThread t0;
t0.Create(m);
ScopedThread t1, t2;
MemLoc l;
t1.Lock(m);
t1.Write1(l);
t1.Unlock(m);
t2.Lock(m);
t2.Write1(l);
t2.Unlock(m);
t2.Destroy(m);
}
TEST_F(ThreadSanitizer, SpinMutex) {
UserMutex m(UserMutex::Spin);
MainThread t0;
t0.Create(m);
ScopedThread t1, t2;
MemLoc l;
t1.Lock(m);
t1.Write1(l);
t1.Unlock(m);
t2.Lock(m);
t2.Write1(l);
t2.Unlock(m);
t2.Destroy(m);
}
TEST_F(ThreadSanitizer, RwMutex) {
UserMutex m(UserMutex::RW);
MainThread t0;
t0.Create(m);
ScopedThread t1, t2, t3;
MemLoc l;
t1.Lock(m);
t1.Write1(l);
t1.Unlock(m);
t2.Lock(m);
t2.Write1(l);
t2.Unlock(m);
t1.ReadLock(m);
t3.ReadLock(m);
t1.Read1(l);
t3.Read1(l);
t1.ReadUnlock(m);
t3.ReadUnlock(m);
t2.Lock(m);
t2.Write1(l);
t2.Unlock(m);
t2.Destroy(m);
}
TEST_F(ThreadSanitizer, StaticMutex) {
// Emulates statically initialized mutex.
UserMutex m;
m.StaticInit();
{
ScopedThread t1, t2;
t1.Lock(m);
t1.Unlock(m);
t2.Lock(m);
t2.Unlock(m);
}
MainThread().Destroy(m);
}
static void *singleton_thread(void *param) {
atomic_uintptr_t *singleton = (atomic_uintptr_t *)param;
for (int i = 0; i < 4*1024*1024; i++) {
int *val = (int *)atomic_load(singleton, memory_order_acquire);
__tsan_acquire(singleton);
__tsan_read4(val);
CHECK_EQ(*val, 42);
}
return 0;
}
TEST(DISABLED_BENCH_ThreadSanitizer, Singleton) {
const int kClockSize = 100;
const int kThreadCount = 8;
// Puff off thread's clock.
for (int i = 0; i < kClockSize; i++) {
ScopedThread t1;
(void)t1;
}
// Create the singleton.
int val = 42;
__tsan_write4(&val);
atomic_uintptr_t singleton;
__tsan_release(&singleton);
atomic_store(&singleton, (uintptr_t)&val, memory_order_release);
// Create reader threads.
pthread_t threads[kThreadCount];
for (int t = 0; t < kThreadCount; t++)
pthread_create(&threads[t], 0, singleton_thread, &singleton);
for (int t = 0; t < kThreadCount; t++)
pthread_join(threads[t], 0);
}
TEST(DISABLED_BENCH_ThreadSanitizer, StopFlag) {
const int kClockSize = 100;
const int kIters = 16*1024*1024;
// Puff off thread's clock.
for (int i = 0; i < kClockSize; i++) {
ScopedThread t1;
(void)t1;
}
// Create the stop flag.
atomic_uintptr_t flag;
__tsan_release(&flag);
atomic_store(&flag, 0, memory_order_release);
// Read it a lot.
for (int i = 0; i < kIters; i++) {
uptr v = atomic_load(&flag, memory_order_acquire);
__tsan_acquire(&flag);
CHECK_EQ(v, 0);
}
}
} // namespace __tsan