#include "sanitizer_mutex.h"
#include "sanitizer_common.h"
namespace __sanitizer {
void StaticSpinMutex::LockSlow() { … }
void Semaphore::Wait() { … }
void Semaphore::Post(u32 count) { … }
#if SANITIZER_CHECK_DEADLOCKS
static constexpr int kMutexTypeMax = 20;
SANITIZER_WEAK_ATTRIBUTE MutexMeta mutex_meta[kMutexTypeMax] = {};
SANITIZER_WEAK_ATTRIBUTE void PrintMutexPC(uptr pc) {}
static StaticSpinMutex mutex_meta_mtx;
static int mutex_type_count = -1;
static bool mutex_can_lock[kMutexTypeMax][kMutexTypeMax];
static bool mutex_multi[kMutexTypeMax];
void DebugMutexInit() {
bool leaf[kMutexTypeMax];
internal_memset(&leaf, 0, sizeof(leaf));
int cnt[kMutexTypeMax];
internal_memset(&cnt, 0, sizeof(cnt));
for (int t = 0; t < kMutexTypeMax; t++) {
mutex_type_count = t;
if (!mutex_meta[t].name)
break;
CHECK_EQ(t, mutex_meta[t].type);
for (uptr j = 0; j < ARRAY_SIZE(mutex_meta[t].can_lock); j++) {
MutexType z = mutex_meta[t].can_lock[j];
if (z == MutexInvalid)
break;
if (z == MutexLeaf) {
CHECK(!leaf[t]);
leaf[t] = true;
continue;
}
if (z == MutexMulti) {
mutex_multi[t] = true;
continue;
}
CHECK_LT(z, kMutexTypeMax);
CHECK(!mutex_can_lock[t][z]);
mutex_can_lock[t][z] = true;
cnt[t]++;
}
}
CHECK_LT(mutex_type_count, kMutexTypeMax);
for (int t = 0; t < mutex_type_count; t++) {
if (!leaf[t])
continue;
CHECK_EQ(cnt[t], 0);
for (int z = 0; z < mutex_type_count; z++) {
if (z == MutexInvalid || t == z || leaf[z])
continue;
CHECK(!mutex_can_lock[z][t]);
mutex_can_lock[z][t] = true;
}
}
u32 trans[kMutexTypeMax];
static_assert(sizeof(trans[0]) * 8 >= kMutexTypeMax,
"kMutexTypeMax does not fit into u32, switch to u64");
internal_memset(&trans, 0, sizeof(trans));
for (int i = 0; i < mutex_type_count; i++) {
for (int j = 0; j < mutex_type_count; j++)
if (mutex_can_lock[i][j])
trans[i] |= 1 << j;
}
for (int k = 0; k < mutex_type_count; k++) {
for (int i = 0; i < mutex_type_count; i++) {
if (trans[i] & (1 << k))
trans[i] |= trans[k];
}
}
for (int i = 0; i < mutex_type_count; i++) {
if (trans[i] & (1 << i)) {
Printf("Mutex %s participates in a cycle\n", mutex_meta[i].name);
Die();
}
}
}
struct InternalDeadlockDetector {
struct LockDesc {
u64 seq;
uptr pc;
int recursion;
};
int initialized;
u64 sequence;
LockDesc locked[kMutexTypeMax];
void Lock(MutexType type, uptr pc) {
if (!Initialize(type))
return;
CHECK_LT(type, mutex_type_count);
u64 max_seq = 0;
MutexType max_idx = MutexInvalid;
for (int i = 0; i != mutex_type_count; i++) {
if (locked[i].seq == 0)
continue;
CHECK_NE(locked[i].seq, max_seq);
if (max_seq < locked[i].seq) {
max_seq = locked[i].seq;
max_idx = (MutexType)i;
}
}
if (max_idx == type && mutex_multi[type]) {
CHECK_EQ(locked[type].seq, max_seq);
CHECK(locked[type].pc);
locked[type].recursion++;
return;
}
if (max_idx != MutexInvalid && !mutex_can_lock[max_idx][type]) {
Printf("%s: internal deadlock: can't lock %s under %s mutex\n", SanitizerToolName,
mutex_meta[type].name, mutex_meta[max_idx].name);
PrintMutexPC(locked[max_idx].pc);
CHECK(0);
}
locked[type].seq = ++sequence;
locked[type].pc = pc;
locked[type].recursion = 1;
}
void Unlock(MutexType type) {
if (!Initialize(type))
return;
CHECK_LT(type, mutex_type_count);
CHECK(locked[type].seq);
CHECK_GT(locked[type].recursion, 0);
if (--locked[type].recursion)
return;
locked[type].seq = 0;
locked[type].pc = 0;
}
void CheckNoLocks() {
for (int i = 0; i < mutex_type_count; i++) CHECK_EQ(locked[i].recursion, 0);
}
bool Initialize(MutexType type) {
if (type == MutexUnchecked || type == MutexInvalid)
return false;
CHECK_GT(type, MutexInvalid);
if (initialized != 0)
return initialized > 0;
initialized = -1;
SpinMutexLock lock(&mutex_meta_mtx);
if (mutex_type_count < 0)
DebugMutexInit();
initialized = mutex_type_count ? 1 : -1;
return initialized > 0;
}
};
__attribute__((tls_model("initial-exec"))) static THREADLOCAL
InternalDeadlockDetector deadlock_detector;
void CheckedMutex::LockImpl(uptr pc) { deadlock_detector.Lock(type_, pc); }
void CheckedMutex::UnlockImpl() { deadlock_detector.Unlock(type_); }
void CheckedMutex::CheckNoLocksImpl() { deadlock_detector.CheckNoLocks(); }
#endif
}