tsan: switch to the new sanitizer_common mutex
Now that sanitizer_common mutex has feature-parity with tsan mutex, switch tsan to the sanitizer_common mutex and remove tsan's custom mutex. Reviewed By: vitalybuka, melver Differential Revision: https://reviews.llvm.org/D106379
This commit is contained in:
parent
8924d8e37e
commit
0118a64934
|
@ -149,9 +149,9 @@ class CheckedMutex {
|
|||
};
|
||||
|
||||
// Reader-writer mutex.
|
||||
class MUTEX Mutex2 {
|
||||
class MUTEX Mutex {
|
||||
public:
|
||||
constexpr Mutex2(MutexType type = MutexUnchecked) : checked_(type) {}
|
||||
constexpr Mutex(MutexType type = MutexUnchecked) : checked_(type) {}
|
||||
|
||||
void Lock() ACQUIRE() {
|
||||
checked_.Lock();
|
||||
|
@ -329,8 +329,8 @@ class MUTEX Mutex2 {
|
|||
static constexpr u64 kWriterLock = 1ull << (3 * kCounterWidth);
|
||||
static constexpr u64 kWriterSpinWait = 1ull << (3 * kCounterWidth + 1);
|
||||
|
||||
Mutex2(const Mutex2 &) = delete;
|
||||
void operator=(const Mutex2 &) = delete;
|
||||
Mutex(const Mutex &) = delete;
|
||||
void operator=(const Mutex &) = delete;
|
||||
};
|
||||
|
||||
void FutexWait(atomic_uint32_t *p, u32 cmp);
|
||||
|
@ -477,6 +477,8 @@ typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
|
|||
typedef GenericScopedLock<BlockingMutex> BlockingMutexLock;
|
||||
typedef GenericScopedLock<RWMutex> RWMutexLock;
|
||||
typedef GenericScopedReadLock<RWMutex> RWMutexReadLock;
|
||||
typedef GenericScopedLock<Mutex> Lock;
|
||||
typedef GenericScopedReadLock<Mutex> ReadLock;
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
|
|
|
@ -158,12 +158,12 @@ TEST(SanitizerCommon, BlockingMutex) {
|
|||
check_locked(mtx);
|
||||
}
|
||||
|
||||
TEST(SanitizerCommon, Mutex2) {
|
||||
Mutex2 mtx;
|
||||
TestData<Mutex2> data(&mtx);
|
||||
TEST(SanitizerCommon, Mutex) {
|
||||
Mutex mtx;
|
||||
TestData<Mutex> data(&mtx);
|
||||
pthread_t threads[kThreads];
|
||||
for (int i = 0; i < kThreads; i++)
|
||||
PTHREAD_CREATE(&threads[i], 0, read_write_thread<Mutex2>, &data);
|
||||
PTHREAD_CREATE(&threads[i], 0, read_write_thread<Mutex>, &data);
|
||||
for (int i = 0; i < kThreads; i++) PTHREAD_JOIN(threads[i], 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ set(TSAN_SOURCES
|
|||
rtl/tsan_malloc_mac.cpp
|
||||
rtl/tsan_md5.cpp
|
||||
rtl/tsan_mman.cpp
|
||||
rtl/tsan_mutex.cpp
|
||||
rtl/tsan_mutexset.cpp
|
||||
rtl/tsan_preinit.cpp
|
||||
rtl/tsan_report.cpp
|
||||
|
@ -94,7 +93,6 @@ set(TSAN_HEADERS
|
|||
rtl/tsan_interface_inl.h
|
||||
rtl/tsan_interface_java.h
|
||||
rtl/tsan_mman.h
|
||||
rtl/tsan_mutex.h
|
||||
rtl/tsan_mutexset.h
|
||||
rtl/tsan_platform.h
|
||||
rtl/tsan_ppc_regs.h
|
||||
|
|
|
@ -4,7 +4,6 @@ type ^
|
|||
..\rtl\tsan_clock.cpp ^
|
||||
..\rtl\tsan_flags.cpp ^
|
||||
..\rtl\tsan_md5.cpp ^
|
||||
..\rtl\tsan_mutex.cpp ^
|
||||
..\rtl\tsan_report.cpp ^
|
||||
..\rtl\tsan_rtl.cpp ^
|
||||
..\rtl\tsan_rtl_mutex.cpp ^
|
||||
|
|
|
@ -9,7 +9,6 @@ SRCS="
|
|||
../rtl/tsan_flags.cpp
|
||||
../rtl/tsan_interface_atomic.cpp
|
||||
../rtl/tsan_md5.cpp
|
||||
../rtl/tsan_mutex.cpp
|
||||
../rtl/tsan_report.cpp
|
||||
../rtl/tsan_rtl.cpp
|
||||
../rtl/tsan_rtl_mutex.cpp
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
#include "ubsan/ubsan_platform.h"
|
||||
|
||||
// Setup defaults for compile definitions.
|
||||
|
@ -172,6 +173,17 @@ enum ExternalTag : uptr {
|
|||
// as 16-bit values, see tsan_defs.h.
|
||||
};
|
||||
|
||||
enum MutexType {
|
||||
MutexTypeTrace = MutexLastCommon,
|
||||
MutexTypeReport,
|
||||
MutexTypeSyncVar,
|
||||
MutexTypeAnnotations,
|
||||
MutexTypeAtExit,
|
||||
MutexTypeFired,
|
||||
MutexTypeRacy,
|
||||
MutexTypeGlobalProc,
|
||||
};
|
||||
|
||||
} // namespace __tsan
|
||||
|
||||
#endif // TSAN_DEFS_H
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "tsan_defs.h"
|
||||
#include "tsan_mutex.h"
|
||||
|
||||
namespace __tsan {
|
||||
|
||||
|
|
|
@ -267,7 +267,7 @@ ScopedInterceptor::~ScopedInterceptor() {
|
|||
if (!thr_->ignore_interceptors) {
|
||||
ProcessPendingSignals(thr_);
|
||||
FuncExit(thr_);
|
||||
CheckNoLocks(thr_);
|
||||
CheckedMutex::CheckNoLocks();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||
#include "sanitizer_common/sanitizer_vector.h"
|
||||
#include "tsan_interface_ann.h"
|
||||
#include "tsan_mutex.h"
|
||||
#include "tsan_report.h"
|
||||
#include "tsan_rtl.h"
|
||||
#include "tsan_mman.h"
|
||||
|
@ -38,7 +37,7 @@ class ScopedAnnotation {
|
|||
|
||||
~ScopedAnnotation() {
|
||||
FuncExit(thr_);
|
||||
CheckNoLocks(thr_);
|
||||
CheckedMutex::CheckNoLocks();
|
||||
}
|
||||
private:
|
||||
ThreadState *const thr_;
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
#include "tsan_interface_java.h"
|
||||
#include "tsan_rtl.h"
|
||||
#include "tsan_mutex.h"
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||
|
|
|
@ -1,280 +0,0 @@
|
|||
//===-- 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_libc.h"
|
||||
#include "tsan_mutex.h"
|
||||
#include "tsan_platform.h"
|
||||
#include "tsan_rtl.h"
|
||||
|
||||
namespace __tsan {
|
||||
|
||||
// Simple reader-writer spin-mutex. Optimized for not-so-contended case.
|
||||
// Readers have preference, can possibly starvate writers.
|
||||
|
||||
// The table fixes what mutexes can be locked under what mutexes.
|
||||
// E.g. if the row for MutexTypeThreads contains MutexTypeReport,
|
||||
// then Report mutex can be locked while under Threads mutex.
|
||||
// The leaf mutexes can be locked under any other mutexes.
|
||||
// Recursive locking is not supported.
|
||||
#if SANITIZER_DEBUG && !SANITIZER_GO
|
||||
const MutexType MutexTypeLeaf = (MutexType)-1;
|
||||
static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
|
||||
/*0 MutexTypeInvalid*/ {},
|
||||
/*1 MutexTypeTrace*/ {MutexTypeLeaf},
|
||||
/*2 MutexTypeThreads*/ {MutexTypeReport},
|
||||
/*3 MutexTypeReport*/ {MutexTypeSyncVar,
|
||||
MutexTypeMBlock, MutexTypeJavaMBlock},
|
||||
/*4 MutexTypeSyncVar*/ {MutexTypeDDetector},
|
||||
/*5 MutexTypeSyncTab*/ {}, // unused
|
||||
/*6 MutexTypeSlab*/ {MutexTypeLeaf},
|
||||
/*7 MutexTypeAnnotations*/ {},
|
||||
/*8 MutexTypeAtExit*/ {MutexTypeSyncVar},
|
||||
/*9 MutexTypeMBlock*/ {MutexTypeSyncVar},
|
||||
/*10 MutexTypeJavaMBlock*/ {MutexTypeSyncVar},
|
||||
/*11 MutexTypeDDetector*/ {},
|
||||
/*12 MutexTypeFired*/ {MutexTypeLeaf},
|
||||
/*13 MutexTypeRacy*/ {MutexTypeLeaf},
|
||||
/*14 MutexTypeGlobalProc*/ {},
|
||||
};
|
||||
|
||||
static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
|
||||
#endif
|
||||
|
||||
void InitializeMutex() {
|
||||
#if SANITIZER_DEBUG && !SANITIZER_GO
|
||||
// Build the "can lock" adjacency matrix.
|
||||
// If [i][j]==true, then one can lock mutex j while under mutex i.
|
||||
const int N = MutexTypeCount;
|
||||
int cnt[N] = {};
|
||||
bool leaf[N] = {};
|
||||
for (int i = 1; i < N; i++) {
|
||||
for (int j = 0; j < N; j++) {
|
||||
MutexType z = CanLockTab[i][j];
|
||||
if (z == MutexTypeInvalid)
|
||||
continue;
|
||||
if (z == MutexTypeLeaf) {
|
||||
CHECK(!leaf[i]);
|
||||
leaf[i] = true;
|
||||
continue;
|
||||
}
|
||||
CHECK(!CanLockAdj[i][(int)z]);
|
||||
CanLockAdj[i][(int)z] = true;
|
||||
cnt[i]++;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < N; i++) {
|
||||
CHECK(!leaf[i] || cnt[i] == 0);
|
||||
}
|
||||
// Add leaf mutexes.
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (!leaf[i])
|
||||
continue;
|
||||
for (int j = 0; j < N; j++) {
|
||||
if (i == j || leaf[j] || j == MutexTypeInvalid)
|
||||
continue;
|
||||
CHECK(!CanLockAdj[j][i]);
|
||||
CanLockAdj[j][i] = true;
|
||||
}
|
||||
}
|
||||
// Build the transitive closure.
|
||||
bool CanLockAdj2[MutexTypeCount][MutexTypeCount];
|
||||
for (int i = 0; i < N; i++) {
|
||||
for (int j = 0; j < N; j++) {
|
||||
CanLockAdj2[i][j] = CanLockAdj[i][j];
|
||||
}
|
||||
}
|
||||
for (int k = 0; k < N; k++) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
for (int j = 0; j < N; j++) {
|
||||
if (CanLockAdj2[i][k] && CanLockAdj2[k][j]) {
|
||||
CanLockAdj2[i][j] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
Printf("Can lock graph:\n");
|
||||
for (int i = 0; i < N; i++) {
|
||||
for (int j = 0; j < N; j++) {
|
||||
Printf("%d ", CanLockAdj[i][j]);
|
||||
}
|
||||
Printf("\n");
|
||||
}
|
||||
Printf("Can lock graph closure:\n");
|
||||
for (int i = 0; i < N; i++) {
|
||||
for (int j = 0; j < N; j++) {
|
||||
Printf("%d ", CanLockAdj2[i][j]);
|
||||
}
|
||||
Printf("\n");
|
||||
}
|
||||
#endif
|
||||
// Verify that the graph is acyclic.
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (CanLockAdj2[i][i]) {
|
||||
Printf("Mutex %d participates in a cycle\n", i);
|
||||
Die();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
InternalDeadlockDetector::InternalDeadlockDetector() {
|
||||
// Rely on zero initialization because some mutexes can be locked before ctor.
|
||||
}
|
||||
|
||||
#if SANITIZER_DEBUG && !SANITIZER_GO
|
||||
void InternalDeadlockDetector::Lock(MutexType t) {
|
||||
// Printf("LOCK %d @%zu\n", t, seq_ + 1);
|
||||
CHECK_GT(t, MutexTypeInvalid);
|
||||
CHECK_LT(t, MutexTypeCount);
|
||||
u64 max_seq = 0;
|
||||
u64 max_idx = MutexTypeInvalid;
|
||||
for (int i = 0; i != MutexTypeCount; i++) {
|
||||
if (locked_[i] == 0)
|
||||
continue;
|
||||
CHECK_NE(locked_[i], max_seq);
|
||||
if (max_seq < locked_[i]) {
|
||||
max_seq = locked_[i];
|
||||
max_idx = i;
|
||||
}
|
||||
}
|
||||
locked_[t] = ++seq_;
|
||||
if (max_idx == MutexTypeInvalid)
|
||||
return;
|
||||
// Printf(" last %d @%zu\n", max_idx, max_seq);
|
||||
if (!CanLockAdj[max_idx][t]) {
|
||||
Printf("ThreadSanitizer: internal deadlock detected\n");
|
||||
Printf("ThreadSanitizer: can't lock %d while under %zu\n",
|
||||
t, (uptr)max_idx);
|
||||
CHECK(0);
|
||||
}
|
||||
}
|
||||
|
||||
void InternalDeadlockDetector::Unlock(MutexType t) {
|
||||
// Printf("UNLO %d @%zu #%zu\n", t, seq_, locked_[t]);
|
||||
CHECK(locked_[t]);
|
||||
locked_[t] = 0;
|
||||
}
|
||||
|
||||
void InternalDeadlockDetector::CheckNoLocks() {
|
||||
for (int i = 0; i != MutexTypeCount; i++) {
|
||||
CHECK_EQ(locked_[i], 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CheckNoLocks(ThreadState *thr) {
|
||||
#if SANITIZER_DEBUG && !SANITIZER_GO
|
||||
thr->internal_deadlock_detector.CheckNoLocks();
|
||||
#endif
|
||||
}
|
||||
|
||||
const uptr kUnlocked = 0;
|
||||
const uptr kWriteLock = 1;
|
||||
const uptr kReadLock = 2;
|
||||
|
||||
class Backoff {
|
||||
public:
|
||||
Backoff()
|
||||
: iter_() {
|
||||
}
|
||||
|
||||
bool Do() {
|
||||
if (iter_++ < kActiveSpinIters)
|
||||
proc_yield(kActiveSpinCnt);
|
||||
else
|
||||
internal_sched_yield();
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 Contention() const {
|
||||
u64 active = iter_ % kActiveSpinIters;
|
||||
u64 passive = iter_ - active;
|
||||
return active + 10 * passive;
|
||||
}
|
||||
|
||||
private:
|
||||
int iter_;
|
||||
static const int kActiveSpinIters = 10;
|
||||
static const int kActiveSpinCnt = 20;
|
||||
};
|
||||
|
||||
Mutex::Mutex(MutexType type) {
|
||||
CHECK_GT(type, MutexTypeInvalid);
|
||||
CHECK_LT(type, MutexTypeCount);
|
||||
#if SANITIZER_DEBUG
|
||||
type_ = type;
|
||||
#endif
|
||||
atomic_store(&state_, kUnlocked, memory_order_relaxed);
|
||||
}
|
||||
|
||||
Mutex::~Mutex() {
|
||||
CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);
|
||||
}
|
||||
|
||||
void Mutex::Lock() {
|
||||
#if SANITIZER_DEBUG && !SANITIZER_GO
|
||||
cur_thread()->internal_deadlock_detector.Lock(type_);
|
||||
#endif
|
||||
uptr cmp = kUnlocked;
|
||||
if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
|
||||
memory_order_acquire))
|
||||
return;
|
||||
for (Backoff backoff; backoff.Do();) {
|
||||
if (atomic_load(&state_, memory_order_relaxed) == kUnlocked) {
|
||||
cmp = kUnlocked;
|
||||
if (atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,
|
||||
memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mutex::Unlock() {
|
||||
uptr prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
|
||||
(void)prev;
|
||||
DCHECK_NE(prev & kWriteLock, 0);
|
||||
#if SANITIZER_DEBUG && !SANITIZER_GO
|
||||
cur_thread()->internal_deadlock_detector.Unlock(type_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::ReadLock() {
|
||||
#if SANITIZER_DEBUG && !SANITIZER_GO
|
||||
cur_thread()->internal_deadlock_detector.Lock(type_);
|
||||
#endif
|
||||
uptr prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
|
||||
if ((prev & kWriteLock) == 0)
|
||||
return;
|
||||
for (Backoff backoff; backoff.Do();) {
|
||||
prev = atomic_load(&state_, memory_order_acquire);
|
||||
if ((prev & kWriteLock) == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mutex::ReadUnlock() {
|
||||
uptr prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);
|
||||
(void)prev;
|
||||
DCHECK_EQ(prev & kWriteLock, 0);
|
||||
DCHECK_GT(prev & ~kWriteLock, 0);
|
||||
#if SANITIZER_DEBUG && !SANITIZER_GO
|
||||
cur_thread()->internal_deadlock_detector.Unlock(type_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::CheckLocked() {
|
||||
CHECK_NE(atomic_load(&state_, memory_order_relaxed), 0);
|
||||
}
|
||||
|
||||
} // namespace __tsan
|
|
@ -1,87 +0,0 @@
|
|||
//===-- tsan_mutex.h --------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef TSAN_MUTEX_H
|
||||
#define TSAN_MUTEX_H
|
||||
|
||||
#include "sanitizer_common/sanitizer_atomic.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
#include "tsan_defs.h"
|
||||
|
||||
namespace __tsan {
|
||||
|
||||
enum MutexType {
|
||||
MutexTypeInvalid,
|
||||
MutexTypeTrace,
|
||||
MutexTypeThreads,
|
||||
MutexTypeReport,
|
||||
MutexTypeSyncVar,
|
||||
MutexTypeSyncTab,
|
||||
MutexTypeSlab,
|
||||
MutexTypeAnnotations,
|
||||
MutexTypeAtExit,
|
||||
MutexTypeMBlock,
|
||||
MutexTypeJavaMBlock,
|
||||
MutexTypeDDetector,
|
||||
MutexTypeFired,
|
||||
MutexTypeRacy,
|
||||
MutexTypeGlobalProc,
|
||||
|
||||
// This must be the last.
|
||||
MutexTypeCount
|
||||
};
|
||||
|
||||
class Mutex {
|
||||
public:
|
||||
explicit Mutex(MutexType type);
|
||||
~Mutex();
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
void ReadLock();
|
||||
void ReadUnlock();
|
||||
|
||||
void CheckLocked();
|
||||
|
||||
private:
|
||||
atomic_uintptr_t state_;
|
||||
#if SANITIZER_DEBUG
|
||||
MutexType type_;
|
||||
#endif
|
||||
|
||||
Mutex(const Mutex&);
|
||||
void operator = (const Mutex&);
|
||||
};
|
||||
|
||||
typedef GenericScopedLock<Mutex> Lock;
|
||||
typedef GenericScopedReadLock<Mutex> ReadLock;
|
||||
|
||||
class InternalDeadlockDetector {
|
||||
public:
|
||||
InternalDeadlockDetector();
|
||||
void Lock(MutexType t);
|
||||
void Unlock(MutexType t);
|
||||
void CheckNoLocks();
|
||||
private:
|
||||
u64 seq_;
|
||||
u64 locked_[MutexTypeCount];
|
||||
};
|
||||
|
||||
void InitializeMutex();
|
||||
|
||||
// Checks that the current thread does not hold any runtime locks
|
||||
// (e.g. when returning from an interceptor).
|
||||
void CheckNoLocks(ThreadState *thr);
|
||||
|
||||
} // namespace __tsan
|
||||
|
||||
#endif // TSAN_MUTEX_H
|
|
@ -422,7 +422,6 @@ void Initialize(ThreadState *thr) {
|
|||
InitializeInterceptors();
|
||||
CheckShadowMapping();
|
||||
InitializePlatform();
|
||||
InitializeMutex();
|
||||
InitializeDynamicAnnotations();
|
||||
#if !SANITIZER_GO
|
||||
InitializeShadowMemory();
|
||||
|
@ -1133,7 +1132,28 @@ void build_consistency_release() {}
|
|||
|
||||
} // namespace __tsan
|
||||
|
||||
#if SANITIZER_CHECK_DEADLOCKS
|
||||
namespace __sanitizer {
|
||||
using namespace __tsan;
|
||||
MutexMeta mutex_meta[] = {
|
||||
{MutexInvalid, "Invalid", {}},
|
||||
{MutexThreadRegistry, "ThreadRegistry", {}},
|
||||
{MutexTypeTrace, "Trace", {MutexLeaf}},
|
||||
{MutexTypeReport, "Report", {MutexTypeSyncVar}},
|
||||
{MutexTypeSyncVar, "SyncVar", {}},
|
||||
{MutexTypeAnnotations, "Annotations", {}},
|
||||
{MutexTypeAtExit, "AtExit", {MutexTypeSyncVar}},
|
||||
{MutexTypeFired, "Fired", {MutexLeaf}},
|
||||
{MutexTypeRacy, "Racy", {MutexLeaf}},
|
||||
{MutexTypeGlobalProc, "GlobalProc", {}},
|
||||
{},
|
||||
};
|
||||
|
||||
void PrintMutexPC(uptr pc) { StackTrace(&pc, 1).Print(); }
|
||||
} // namespace __sanitizer
|
||||
#endif
|
||||
|
||||
#if !SANITIZER_GO
|
||||
// Must be included in this file to make sure everything is inlined.
|
||||
#include "tsan_interface_inl.h"
|
||||
# include "tsan_interface_inl.h"
|
||||
#endif
|
||||
|
|
|
@ -129,7 +129,7 @@ bool ShouldReport(ThreadState *thr, ReportType typ) {
|
|||
// We set thr->suppress_reports in the fork context.
|
||||
// Taking any locking in the fork context can lead to deadlocks.
|
||||
// If any locks are already taken, it's too late to do this check.
|
||||
CheckNoLocks(thr);
|
||||
CheckedMutex::CheckNoLocks();
|
||||
// For the same reason check we didn't lock thread_registry yet.
|
||||
if (SANITIZER_DEBUG)
|
||||
ThreadRegistryLock l(ctx->thread_registry);
|
||||
|
@ -596,7 +596,7 @@ static bool RaceBetweenAtomicAndFree(ThreadState *thr) {
|
|||
}
|
||||
|
||||
void ReportRace(ThreadState *thr) {
|
||||
CheckNoLocks(thr);
|
||||
CheckedMutex::CheckNoLocks();
|
||||
|
||||
// Symbolizer makes lots of intercepted calls. If we try to process them,
|
||||
// at best it will cause deadlocks on internal mutexes.
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "sanitizer_common/sanitizer_deadlock_detector_interface.h"
|
||||
#include "tsan_defs.h"
|
||||
#include "tsan_clock.h"
|
||||
#include "tsan_mutex.h"
|
||||
#include "tsan_dense_alloc.h"
|
||||
|
||||
namespace __tsan {
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define TSAN_TRACE_H
|
||||
|
||||
#include "tsan_defs.h"
|
||||
#include "tsan_mutex.h"
|
||||
#include "tsan_stack_trace.h"
|
||||
#include "tsan_mutexset.h"
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ set(TSAN_UNIT_TEST_SOURCES
|
|||
tsan_dense_alloc_test.cpp
|
||||
tsan_flags_test.cpp
|
||||
tsan_mman_test.cpp
|
||||
tsan_mutex_test.cpp
|
||||
tsan_shadow_test.cpp
|
||||
tsan_stack_test.cpp
|
||||
tsan_sync_test.cpp
|
||||
|
|
|
@ -1,125 +0,0 @@
|
|||
//===-- tsan_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 (TSan), a race detector.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common/sanitizer_atomic.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
#include "tsan_mutex.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace __tsan {
|
||||
|
||||
template<typename MutexType>
|
||||
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++) {
|
||||
CHECK_EQ(data_[i], v0);
|
||||
data_[i]++;
|
||||
}
|
||||
}
|
||||
|
||||
void Read() {
|
||||
ReadLock l(mtx_);
|
||||
T v0 = data_[0];
|
||||
for (int i = 0; i < kSize; i++) {
|
||||
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<MutexType> Lock;
|
||||
static const int kSize = 64;
|
||||
typedef u64 T;
|
||||
MutexType *mtx_;
|
||||
char pad_[kCacheLineSize];
|
||||
T data_[kSize];
|
||||
};
|
||||
|
||||
const int kThreads = 8;
|
||||
const int kWriteRate = 1024;
|
||||
#if SANITIZER_DEBUG
|
||||
const int kIters = 16*1024;
|
||||
#else
|
||||
const int kIters = 64*1024;
|
||||
#endif
|
||||
|
||||
template<typename MutexType>
|
||||
static void *write_mutex_thread(void *param) {
|
||||
TestData<MutexType> *data = (TestData<MutexType>*)param;
|
||||
for (int i = 0; i < kIters; i++) {
|
||||
data->Write();
|
||||
data->Backoff();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename MutexType>
|
||||
static void *read_mutex_thread(void *param) {
|
||||
TestData<MutexType> *data = (TestData<MutexType>*)param;
|
||||
for (int i = 0; i < kIters; i++) {
|
||||
if ((i % kWriteRate) == 0)
|
||||
data->Write();
|
||||
else
|
||||
data->Read();
|
||||
data->Backoff();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(Mutex, Write) {
|
||||
Mutex mtx(MutexTypeAnnotations);
|
||||
TestData<Mutex> data(&mtx);
|
||||
pthread_t threads[kThreads];
|
||||
for (int i = 0; i < kThreads; i++)
|
||||
pthread_create(&threads[i], 0, write_mutex_thread<Mutex>, &data);
|
||||
for (int i = 0; i < kThreads; i++)
|
||||
pthread_join(threads[i], 0);
|
||||
}
|
||||
|
||||
TEST(Mutex, ReadWrite) {
|
||||
Mutex mtx(MutexTypeAnnotations);
|
||||
TestData<Mutex> data(&mtx);
|
||||
pthread_t threads[kThreads];
|
||||
for (int i = 0; i < kThreads; i++)
|
||||
pthread_create(&threads[i], 0, read_mutex_thread<Mutex>, &data);
|
||||
for (int i = 0; i < kThreads; i++)
|
||||
pthread_join(threads[i], 0);
|
||||
}
|
||||
|
||||
TEST(Mutex, SpinWrite) {
|
||||
SpinMutex mtx;
|
||||
TestData<SpinMutex> data(&mtx);
|
||||
pthread_t threads[kThreads];
|
||||
for (int i = 0; i < kThreads; i++)
|
||||
pthread_create(&threads[i], 0, write_mutex_thread<SpinMutex>, &data);
|
||||
for (int i = 0; i < kThreads; i++)
|
||||
pthread_join(threads[i], 0);
|
||||
}
|
||||
|
||||
} // namespace __tsan
|
Loading…
Reference in New Issue