//===-- sanitizer_mutex_test.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/AddressSanitizer runtime. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_pthread_wrappers.h" #include "gtest/gtest.h" #include namespace __sanitizer { template class TestData { public: explicit TestData(MutexType *mtx) : mtx_(mtx) { for (int i = 0; i < kSize; i++) data_[i] = 0; } void Write() { Lock l(mtx_); T v0 = data_[0]; for (int i = 0; i < kSize; i++) { mtx_->CheckLocked(); CHECK_EQ(data_[i], v0); data_[i]++; } } void TryWrite() { if (!mtx_->TryLock()) return; T v0 = data_[0]; for (int i = 0; i < kSize; i++) { mtx_->CheckLocked(); CHECK_EQ(data_[i], v0); data_[i]++; } mtx_->Unlock(); } void Read() { ReadLock l(mtx_); T v0 = data_[0]; for (int i = 0; i < kSize; i++) { mtx_->CheckReadLocked(); CHECK_EQ(data_[i], v0); } } void Backoff() { volatile T data[kSize] = {}; for (int i = 0; i < kSize; i++) { data[i]++; CHECK_EQ(data[i], 1); } } private: typedef GenericScopedLock Lock; typedef GenericScopedReadLock ReadLock; static const int kSize = 64; typedef u64 T; MutexType *mtx_; char pad_[kCacheLineSize]; T data_[kSize]; }; const int kThreads = 8; #if SANITIZER_DEBUG const int kIters = 16*1024; #else const int kIters = 64*1024; #endif template static void *lock_thread(void *param) { TestData *data = (TestData*)param; for (int i = 0; i < kIters; i++) { data->Write(); data->Backoff(); } return 0; } template static void *try_thread(void *param) { TestData *data = (TestData*)param; for (int i = 0; i < kIters; i++) { data->TryWrite(); data->Backoff(); } return 0; } template static void *read_write_thread(void *param) { TestData *data = (TestData *)param; for (int i = 0; i < kIters; i++) { if ((i % 10) == 0) data->Write(); else data->Read(); data->Backoff(); } return 0; } template static void check_locked(MutexType *mtx) { GenericScopedLock l(mtx); mtx->CheckLocked(); } TEST(SanitizerCommon, SpinMutex) { SpinMutex mtx; mtx.Init(); TestData data(&mtx); pthread_t threads[kThreads]; for (int i = 0; i < kThreads; i++) PTHREAD_CREATE(&threads[i], 0, lock_thread, &data); for (int i = 0; i < kThreads; i++) PTHREAD_JOIN(threads[i], 0); } TEST(SanitizerCommon, SpinMutexTry) { SpinMutex mtx; mtx.Init(); TestData data(&mtx); pthread_t threads[kThreads]; for (int i = 0; i < kThreads; i++) PTHREAD_CREATE(&threads[i], 0, try_thread, &data); for (int i = 0; i < kThreads; i++) PTHREAD_JOIN(threads[i], 0); } TEST(SanitizerCommon, Mutex) { Mutex mtx; TestData data(&mtx); pthread_t threads[kThreads]; for (int i = 0; i < kThreads; i++) PTHREAD_CREATE(&threads[i], 0, read_write_thread, &data); for (int i = 0; i < kThreads; i++) PTHREAD_JOIN(threads[i], 0); } TEST(SanitizerCommon, MutexTry) { Mutex mtx; TestData data(&mtx); pthread_t threads[kThreads]; for (int i = 0; i < kThreads; i++) PTHREAD_CREATE(&threads[i], 0, try_thread, &data); for (int i = 0; i < kThreads; i++) PTHREAD_JOIN(threads[i], 0); } struct SemaphoreData { Semaphore *sem; bool done; }; void *SemaphoreThread(void *arg) { auto data = static_cast(arg); data->sem->Wait(); data->done = true; return nullptr; } TEST(SanitizerCommon, Semaphore) { Semaphore sem; sem.Post(1); sem.Wait(); sem.Post(3); sem.Wait(); sem.Wait(); sem.Wait(); SemaphoreData data = {&sem, false}; pthread_t thread; PTHREAD_CREATE(&thread, nullptr, SemaphoreThread, &data); internal_sleep(1); CHECK(!data.done); sem.Post(1); PTHREAD_JOIN(thread, nullptr); } } // namespace __sanitizer