| //===-- 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 |