// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=0 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_CAPABILITY=1 %s
// FIXME: should also run %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++11 -Wc++98-compat %s
// FIXME: should also run %clang_cc1 -fsyntax-only -verify -Wthread-safety %s
#include "thread-safety-annotations.h"
class LOCKABLE Mutex {
public:
void Lock() EXCLUSIVE_LOCK_FUNCTION();
void ReaderLock() SHARED_LOCK_FUNCTION();
void Unlock() UNLOCK_FUNCTION();
void ExclusiveUnlock() EXCLUSIVE_UNLOCK_FUNCTION();
void ReaderUnlock() SHARED_UNLOCK_FUNCTION();
bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true);
bool ReaderTryLock() SHARED_TRYLOCK_FUNCTION(true);
void LockWhen(const int &cond) EXCLUSIVE_LOCK_FUNCTION();
void PromoteShared() SHARED_UNLOCK_FUNCTION() EXCLUSIVE_LOCK_FUNCTION();
void DemoteExclusive() EXCLUSIVE_UNLOCK_FUNCTION() SHARED_LOCK_FUNCTION();
// for negative capabilities
const Mutex& operator!() const { return *this; }
void AssertHeld() ASSERT_EXCLUSIVE_LOCK();
void AssertReaderHeld() ASSERT_SHARED_LOCK();
};
class SCOPED_LOCKABLE MutexLock {
public:
MutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu);
MutexLock(Mutex *mu, bool adopt) EXCLUSIVE_LOCKS_REQUIRED(mu);
~MutexLock() UNLOCK_FUNCTION();
};
class SCOPED_LOCKABLE ReaderMutexLock {
public:
ReaderMutexLock(Mutex *mu) SHARED_LOCK_FUNCTION(mu);
ReaderMutexLock(Mutex *mu, bool adopt) SHARED_LOCKS_REQUIRED(mu);
~ReaderMutexLock() UNLOCK_FUNCTION();
};
class SCOPED_LOCKABLE ReleasableMutexLock {
public:
ReleasableMutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu);
~ReleasableMutexLock() UNLOCK_FUNCTION();
void Release() UNLOCK_FUNCTION();
};
class SCOPED_LOCKABLE DoubleMutexLock {
public:
DoubleMutexLock(Mutex *mu1, Mutex *mu2) EXCLUSIVE_LOCK_FUNCTION(mu1, mu2);
~DoubleMutexLock() UNLOCK_FUNCTION();
};
// The universal lock, written "*", allows checking to be selectively turned
// off for a particular piece of code.
void beginNoWarnOnReads() SHARED_LOCK_FUNCTION("*");
void endNoWarnOnReads() UNLOCK_FUNCTION("*");
void beginNoWarnOnWrites() EXCLUSIVE_LOCK_FUNCTION("*");
void endNoWarnOnWrites() UNLOCK_FUNCTION("*");
// For testing handling of smart pointers.
template<class T>
class SmartPtr {
public:
SmartPtr(T* p) : ptr_(p) { }
SmartPtr(const SmartPtr<T>& p) : ptr_(p.ptr_) { }
~SmartPtr();
T* get() const { return ptr_; }
T* operator->() const { return ptr_; }
T& operator*() const { return *ptr_; }
T& operator[](int i) const { return ptr_[i]; }
private:
T* ptr_;
};
template<typename T, typename U>
U& operator->*(const SmartPtr<T>& ptr, U T::*p) { return ptr->*p; }
// For testing destructor calls and cleanup.
class MyString {
public:
MyString(const char* s);
~MyString();
};
// For testing operator overloading
template <class K, class T>
class MyMap {
public:
T& operator[](const K& k);
};
// For testing handling of containers.
template <class T>
class MyContainer {
public:
MyContainer();
typedef T* iterator;
typedef const T* const_iterator;
T* begin();
T* end();
const T* cbegin();
const T* cend();
T& operator[](int i);
const T& operator[](int i) const;
private:
T* ptr_;
};
Mutex sls_mu;
Mutex sls_mu2 __attribute__((acquired_after(sls_mu)));
int sls_guard_var __attribute__((guarded_var)) = 0;
int sls_guardby_var __attribute__((guarded_by(sls_mu))) = 0;
bool getBool();
class MutexWrapper {
public:
Mutex mu;
int x __attribute__((guarded_by(mu)));
void MyLock() EXCLUSIVE_LOCK_FUNCTION(mu);
};
struct TestingMoreComplexAttributes {
Mutex lock;
struct { Mutex lock; } strct;
union {
bool a __attribute__((guarded_by(lock)));
bool b __attribute__((guarded_by(strct.lock)));
bool *ptr_a __attribute__((pt_guarded_by(lock)));
bool *ptr_b __attribute__((pt_guarded_by(strct.lock)));
Mutex lock1 __attribute__((acquired_before(lock))) __attribute__((acquired_before(strct.lock)));
Mutex lock2 __attribute__((acquired_after(lock))) __attribute__((acquired_after(strct.lock)));
};
} more_complex_atttributes;
void more_complex_attributes() {
more_complex_atttributes.a = true; // expected-warning{{writing variable 'a' requires holding mutex 'lock' exclusively}}
more_complex_atttributes.b = true; // expected-warning{{writing variable 'b' requires holding mutex 'strct.lock' exclusively}}
*more_complex_atttributes.ptr_a = true; // expected-warning{{writing the value pointed to by 'ptr_a' requires holding mutex 'lock' exclusively}}
*more_complex_atttributes.ptr_b = true; // expected-warning{{writing the value pointed to by 'ptr_b' requires holding mutex 'strct.lock' exclusively}}
more_complex_atttributes.lock.Lock();
more_complex_atttributes.lock1.Lock(); // expected-warning{{mutex 'lock1' must be acquired before 'lock'}}
more_complex_atttributes.lock1.Unlock();
more_complex_atttributes.lock.Unlock();
more_complex_atttributes.lock2.Lock();
more_complex_atttributes.lock.Lock(); // expected-warning{{mutex 'lock' must be acquired before 'lock2'}}
more_complex_atttributes.lock.Unlock();
more_complex_atttributes.lock2.Unlock();
}
MutexWrapper sls_mw;
void sls_fun_0() {
sls_mw.mu.Lock();
sls_mw.x = 5;
sls_mw.mu.Unlock();
}
void sls_fun_2() {
sls_mu.Lock();
int x = sls_guard_var;
sls_mu.Unlock();
}
void sls_fun_3() {
sls_mu.Lock();
sls_guard_var = 2;
sls_mu.Unlock();
}
void sls_fun_4() {
sls_mu2.Lock();
sls_guard_var = 2;
sls_mu2.Unlock();
}
void sls_fun_5() {
sls_mu.Lock();
int x = sls_guardby_var;
sls_mu.Unlock();
}
void sls_fun_6() {
sls_mu.Lock();
sls_guardby_var = 2;
sls_mu.Unlock();
}
void sls_fun_7() {
sls_mu.Lock();
sls_mu2.Lock();
sls_mu2.Unlock();
sls_mu.Unlock();
}
void sls_fun_8() {
sls_mu.Lock();
if (getBool())
sls_mu.Unlock();
else
sls_mu.Unlock();
}
void sls_fun_9() {
if (getBool())
sls_mu.Lock();
else
sls_mu.Lock();
sls_mu.Unlock();
}
void sls_fun_good_6() {
if (getBool()) {
sls_mu.Lock();
} else {
if (getBool()) {
getBool(); // EMPTY
} else {
getBool(); // EMPTY
}
sls_mu.Lock();
}
sls_mu.Unlock();
}
void sls_fun_good_7() {
sls_mu.Lock();
while (getBool()) {
sls_mu.Unlock();
if (getBool()) {
if (getBool()) {
sls_mu.Lock();
continue;
}
}
sls_mu.Lock();
}
sls_mu.Unlock();
}
void sls_fun_good_8() {
sls_mw.MyLock();
sls_mw.mu.Unlock();
}
void sls_fun_bad_1() {
sls_mu.Unlock(); // \
// expected-warning{{releasing mutex 'sls_mu' that was not held}}
}
void sls_fun_bad_2() {
sls_mu.Lock(); // expected-note{{mutex acquired here}}
sls_mu.Lock(); // \
// expected-warning{{acquiring mutex 'sls_mu' that is already held}}
sls_mu.Unlock();
}
void sls_fun_bad_3() {
sls_mu.Lock(); // expected-note {{mutex acquired here}}
} // expected-warning{{mutex 'sls_mu' is still held at the end of function}}
void sls_fun_bad_4() {
if (getBool())
sls_mu.Lock(); // expected-note{{mutex acquired here}}
else
sls_mu2.Lock(); // expected-note{{mutex acquired here}}
} // expected-warning{{mutex 'sls_mu' is not held on every path through here}} \
// expected-warning{{mutex 'sls_mu2' is not held on every path through here}}
void sls_fun_bad_5() {
sls_mu.Lock(); // expected-note {{mutex acquired here}}
if (getBool())
sls_mu.Unlock();
} // expected-warning{{mutex 'sls_mu' is not held on every path through here}}
void sls_fun_bad_6() {
if (getBool()) {
sls_mu.Lock(); // expected-note {{mutex acquired here}}
} else {
if (getBool()) {
getBool(); // EMPTY
} else {
getBool(); // EMPTY
}
}
sls_mu.Unlock(); // \
expected-warning{{mutex 'sls_mu' is not held on every path through here}}\
expected-warning{{releasing mutex 'sls_mu' that was not held}}
}
void sls_fun_bad_7() {
sls_mu.Lock();
while (getBool()) { // \
expected-warning{{expecting mutex 'sls_mu' to be held at start of each loop}}
sls_mu.Unlock();
if (getBool()) {
if (getBool()) {
continue;
}
}
sls_mu.Lock(); // expected-note {{mutex acquired here}}
}
sls_mu.Unlock();
}
void sls_fun_bad_8() {
sls_mu.Lock(); // expected-note{{mutex acquired here}}
do {
sls_mu.Unlock(); // expected-warning{{expecting mutex 'sls_mu' to be held at start of each loop}}
} while (getBool());
}
void sls_fun_bad_9() {
do {
sls_mu.Lock(); // \
// expected-warning{{expecting mutex 'sls_mu' to be held at start of each loop}} \
// expected-note{{mutex acquired here}}
} while (getBool());
sls_mu.Unlock();
}
void sls_fun_bad_10() {
sls_mu.Lock(); // expected-note 2{{mutex acquired here}}
while(getBool()) { // expected-warning{{expecting mutex 'sls_mu' to be held at start of each loop}}
sls_mu.Unlock();
}
} // expected-warning{{mutex 'sls_mu' is still held at the end of function}}
void sls_fun_bad_11() {
while (getBool()) { // \
expected-warning{{expecting mutex 'sls_mu' to be held at start of each loop}}
sls_mu.Lock(); // expected-note {{mutex acquired here}}
}
sls_mu.Unlock(); // \
// expected-warning{{releasing mutex 'sls_mu' that was not held}}
}
void sls_fun_bad_12() {
sls_mu.Lock(); // expected-note {{mutex acquired here}}
while (getBool()) {
sls_mu.Unlock();
if (getBool()) {
if (getBool()) {
break;
}
}
sls_mu.Lock();
}
sls_mu.Unlock(); // \
expected-warning{{mutex 'sls_mu' is not held on every path through here}} \
expected-warning{{releasing mutex 'sls_mu' that was not held}}
}
//-----------------------------------------//
// Handling lock expressions in attribute args
// -------------------------------------------//
Mutex aa_mu;
class GlobalLocker {
public:
void globalLock() EXCLUSIVE_LOCK_FUNCTION(aa_mu);
void globalUnlock() UNLOCK_FUNCTION(aa_mu);
};
GlobalLocker glock;
void aa_fun_1() {
glock.globalLock();
glock.globalUnlock();
}
void aa_fun_bad_1() {
glock.globalUnlock(); // \
// expected-warning{{releasing mutex 'aa_mu' that was not held}}
}
void aa_fun_bad_2() {
glock.globalLock(); // expected-note{{mutex acquired here}}
glock.globalLock(); // \
// expected-warning{{acquiring mutex 'aa_mu' that is already held}}
glock.globalUnlock();
}
void aa_fun_bad_3() {
glock.globalLock(); // expected-note{{mutex acquired here}}
} // expected-warning{{mutex 'aa_mu' is still held at the end of function}}
//--------------------------------------------------//
// Regression tests for unusual method names
//--------------------------------------------------//
Mutex wmu;
// Test diagnostics for other method names.
class WeirdMethods {
// FIXME: can't currently check inside constructors and destructors.
WeirdMethods() {
wmu.Lock(); // EXPECTED-NOTE {{mutex acquired here}}
} // EXPECTED-WARNING {{mutex 'wmu' is still held at the end of function}}
~WeirdMethods() {
wmu.Lock(); // EXPECTED-NOTE {{mutex acquired here}}
} // EXPECTED-WARNING {{mutex 'wmu' is still held at the end of function}}
void operator++() {
wmu.Lock(); // expected-note {{mutex acquired here}}
} // expected-warning {{mutex 'wmu' is still held at the end of function}}
operator int*() {
wmu.Lock(); // expected-note {{mutex acquired here}}
return 0;
} // expected-warning {{mutex 'wmu' is still held at the end of function}}
};
//-----------------------------------------------//
// Errors for guarded by or guarded var variables
// ----------------------------------------------//
int *pgb_gvar __attribute__((pt_guarded_var));
int *pgb_var __attribute__((pt_guarded_by(sls_mu)));
class PGBFoo {
public:
int x;
int *pgb_field __attribute__((guarded_by(sls_mu2)))
__attribute__((pt_guarded_by(sls_mu)));
void testFoo() {
pgb_field = &x; // \
// expected-warning {{writing variable 'pgb_field' requires holding mutex 'sls_mu2' exclusively}}
*pgb_field = x; // expected-warning {{reading variable 'pgb_field' requires holding mutex 'sls_mu2'}} \
// expected-warning {{writing the value pointed to by 'pgb_field' requires holding mutex 'sls_mu' exclusively}}
x = *pgb_field; // expected-warning {{reading variable 'pgb_field' requires holding mutex 'sls_mu2'}} \
// expected-warning {{reading the value pointed to by 'pgb_field' requires holding mutex 'sls_mu'}}
(*pgb_field)++; // expected-warning {{reading variable 'pgb_field' requires holding mutex 'sls_mu2'}} \
// expected-warning {{writing the value pointed to by 'pgb_field' requires holding mutex 'sls_mu' exclusively}}
}
};
class GBFoo {
public:
int gb_field __attribute__((guarded_by(sls_mu)));
void testFoo() {
gb_field = 0; // \
// expected-warning {{writing variable 'gb_field' requires holding mutex 'sls_mu' exclusively}}
}
void testNoAnal() NO_THREAD_SAFETY_ANALYSIS {
gb_field = 0;
}
};
GBFoo GlobalGBFoo __attribute__((guarded_by(sls_mu)));
void gb_fun_0() {
sls_mu.Lock();
int x = *pgb_var;
sls_mu.Unlock();
}
void gb_fun_1() {
sls_mu.Lock();
*pgb_var = 2;
sls_mu.Unlock();
}
void gb_fun_2() {
int x;
pgb_var = &x;
}
void gb_fun_3() {
int *x = pgb_var;
}
void gb_bad_0() {
sls_guard_var = 1; // \
// expected-warning{{writing variable 'sls_guard_var' requires holding any mutex exclusively}}
}
void gb_bad_1() {
int x = sls_guard_var; // \
// expected-warning{{reading variable 'sls_guard_var' requires holding any mutex}}
}
void gb_bad_2() {
sls_guardby_var = 1; // \
// expected-warning {{writing variable 'sls_guardby_var' requires holding mutex 'sls_mu' exclusively}}
}
void gb_bad_3() {
int x = sls_guardby_var; // \
// expected-warning {{reading variable 'sls_guardby_var' requires holding mutex 'sls_mu'}}
}
void gb_bad_4() {
*pgb_gvar = 1; // \
// expected-warning {{writing the value pointed to by 'pgb_gvar' requires holding any mutex exclusively}}
}
void gb_bad_5() {
int x = *pgb_gvar; // \
// expected-warning {{reading the value pointed to by 'pgb_gvar' requires holding any mutex}}
}
void gb_bad_6() {
*pgb_var = 1; // \
// expected-warning {{writing the value pointed to by 'pgb_var' requires holding mutex 'sls_mu' exclusively}}
}
void gb_bad_7() {
int x = *pgb_var; // \
// expected-warning {{reading the value pointed to by 'pgb_var' requires holding mutex 'sls_mu'}}
}
void gb_bad_8() {
GBFoo G;
G.gb_field = 0; // \
// expected-warning {{writing variable 'gb_field' requires holding mutex 'sls_mu'}}
}
void gb_bad_9() {
sls_guard_var++; // \
// expected-warning{{writing variable 'sls_guard_var' requires holding any mutex exclusively}}
sls_guard_var--; // \
// expected-warning{{writing variable 'sls_guard_var' requires holding any mutex exclusively}}
++sls_guard_var; // \
// expected-warning{{writing variable 'sls_guard_var' requires holding any mutex exclusively}}
--sls_guard_var;// \
// expected-warning{{writing variable 'sls_guard_var' requires holding any mutex exclusively}}
}
//-----------------------------------------------//
// Warnings on variables with late parsed attributes
// ----------------------------------------------//
class LateFoo {
public:
int a __attribute__((guarded_by(mu)));
int b;
void foo() EXCLUSIVE_LOCKS_REQUIRED(mu) { }
void test() {
a = 0; // \
// expected-warning{{writing variable 'a' requires holding mutex 'mu' exclusively}}
b = a; // \
// expected-warning {{reading variable 'a' requires holding mutex 'mu'}}
c = 0; // \
// expected-warning {{writing variable 'c' requires holding mutex 'mu' exclusively}}
}
int c __attribute__((guarded_by(mu)));
Mutex mu;
};
class LateBar {
public:
int a_ __attribute__((guarded_by(mu1_)));
int b_;
int *q __attribute__((pt_guarded_by(mu)));
Mutex mu1_;
Mutex mu;
LateFoo Foo;
LateFoo Foo2;
LateFoo *FooPointer;
};
LateBar b1, *b3;
void late_0() {
LateFoo FooA;
LateFoo FooB;
FooA.mu.Lock();
FooA.a = 5;
FooA.mu.Unlock();
}
void late_1() {
LateBar BarA;
BarA.FooPointer->mu.Lock();
BarA.FooPointer->a = 2;
BarA.FooPointer->mu.Unlock();
}
void late_bad_0() {
LateFoo fooA;
LateFoo fooB;
fooA.mu.Lock();
fooB.a = 5; // \
// expected-warning{{writing variable 'a' requires holding mutex 'fooB.mu' exclusively}} \
// expected-note{{found near match 'fooA.mu'}}
fooA.mu.Unlock();
}
void late_bad_1() {
Mutex mu;
mu.Lock();
b1.mu1_.Lock();
int res = b1.a_ + b3->b_;
b3->b_ = *b1.q; // \
// expected-warning{{reading the value pointed to by 'q' requires holding mutex 'b1.mu'}}
b1.mu1_.Unlock();
b1.b_ = res;
mu.Unlock();
}
void late_bad_2() {
LateBar BarA;
BarA.FooPointer->mu.Lock();
BarA.Foo.a = 2; // \
// expected-warning{{writing variable 'a' requires holding mutex 'BarA.Foo.mu' exclusively}} \
// expected-note{{found near match 'BarA.FooPointer->mu'}}
BarA.FooPointer->mu.Unlock();
}
void late_bad_3() {
LateBar BarA;
BarA.Foo.mu.Lock();
BarA.FooPointer->a = 2; // \
// expected-warning{{writing variable 'a' requires holding mutex 'BarA.FooPointer->mu' exclusively}} \
// expected-note{{found near match 'BarA.Foo.mu'}}
BarA.Foo.mu.Unlock();
}
void late_bad_4() {
LateBar BarA;
BarA.Foo.mu.Lock();
BarA.Foo2.a = 2; // \
// expected-warning{{writing variable 'a' requires holding mutex 'BarA.Foo2.mu' exclusively}} \
// expected-note{{found near match 'BarA.Foo.mu'}}
BarA.Foo.mu.Unlock();
}
//-----------------------------------------------//
// Extra warnings for shared vs. exclusive locks
// ----------------------------------------------//
void shared_fun_0() {
sls_mu.Lock();
do {
sls_mu.Unlock();
sls_mu.Lock();
} while (getBool());
sls_mu.Unlock();
}
void shared_fun_1() {
sls_mu.ReaderLock(); // \
// expected-note {{the other acquisition of mutex 'sls_mu' is here}}
do {
sls_mu.Unlock();
sls_mu.Lock(); // \
// expected-warning {{mutex 'sls_mu' is acquired exclusively and shared in the same scope}}
} while (getBool());
sls_mu.Unlock();
}
void shared_fun_3() {
if (getBool())
sls_mu.Lock();
else
sls_mu.Lock();
*pgb_var = 1;
sls_mu.Unlock();
}
void shared_fun_4() {
if (getBool())
sls_mu.ReaderLock();
else
sls_mu.ReaderLock();
int x = sls_guardby_var;
sls_mu.Unlock();
}
void shared_fun_8() {
if (getBool())
sls_mu.Lock(); // \
// expected-warning {{mutex 'sls_mu' is acquired exclusively and shared in the same scope}}
else
sls_mu.ReaderLock(); // \
// expected-note {{the other acquisition of mutex 'sls_mu' is here}}
sls_mu.Unlock();
}
void shared_fun_9() {
sls_mu.Lock();
sls_mu.ExclusiveUnlock();
sls_mu.ReaderLock();
sls_mu.ReaderUnlock();
}
void shared_fun_10() {
sls_mu.Lock();
sls_mu.DemoteExclusive();
sls_mu.ReaderUnlock();
}
void shared_fun_11() {
sls_mu.ReaderLock();
sls_mu.PromoteShared();
sls_mu.Unlock();
}
void shared_bad_0() {
sls_mu.Lock(); // \
// expected-note {{the other acquisition of mutex 'sls_mu' is here}}
do {
sls_mu.Unlock();
sls_mu.ReaderLock(); // \
// expected-warning {{mutex 'sls_mu' is acquired exclusively and shared in the same scope}}
} while (getBool());
sls_mu.Unlock();
}
void shared_bad_1() {
if (getBool())
sls_mu.Lock(); // \
// expected-warning {{mutex 'sls_mu' is acquired exclusively and shared in the same scope}}
else
sls_mu.ReaderLock(); // \
// expected-note {{the other acquisition of mutex 'sls_mu' is here}}
*pgb_var = 1;
sls_mu.Unlock();
}
void shared_bad_2() {
if (getBool())
sls_mu.ReaderLock(); // \
// expected-warning {{mutex 'sls_mu' is acquired exclusively and shared in the same scope}}
else
sls_mu.Lock(); // \
// expected-note {{the other acquisition of mutex 'sls_mu' is here}}
*pgb_var = 1;
sls_mu.Unlock();
}
void shared_bad_3() {
sls_mu.Lock(); // expected-note {{mutex acquired here}}
sls_mu.ReaderUnlock(); // \
// expected-warning {{releasing mutex 'sls_mu' using shared access, expected exclusive access}}
}
void shared_bad_4() {
sls_mu.ReaderLock(); // expected-note {{mutex acquired here}}
sls_mu.ExclusiveUnlock(); // \
// expected-warning {{releasing mutex 'sls_mu' using exclusive access, expected shared access}}
}
void shared_bad_5() {
sls_mu.Lock(); // expected-note {{mutex acquired here}}
sls_mu.PromoteShared(); // \
// expected-warning {{releasing mutex 'sls_mu' using shared access, expected exclusive access}}
sls_mu.ExclusiveUnlock();
}
void shared_bad_6() {
sls_mu.ReaderLock(); // expected-note {{mutex acquired here}}
sls_mu.DemoteExclusive(); // \
// expected-warning {{releasing mutex 'sls_mu' using exclusive access, expected shared access}}
sls_mu.ReaderUnlock();
}
// FIXME: Add support for functions (not only methods)
class LRBar {
public:
void aa_elr_fun() EXCLUSIVE_LOCKS_REQUIRED(aa_mu);
void aa_elr_fun_s() SHARED_LOCKS_REQUIRED(aa_mu);
void le_fun() __attribute__((locks_excluded(sls_mu)));
};
class LRFoo {
public:
void test() EXCLUSIVE_LOCKS_REQUIRED(sls_mu);
void testShared() SHARED_LOCKS_REQUIRED(sls_mu2);
};
void elr_fun() EXCLUSIVE_LOCKS_REQUIRED(sls_mu);
void elr_fun() {}
LRFoo MyLRFoo;
LRBar Bar;
void es_fun_0() {
aa_mu.Lock();
Bar.aa_elr_fun();
aa_mu.Unlock();
}
void es_fun_1() {
aa_mu.Lock();
Bar.aa_elr_fun_s();
aa_mu.Unlock();
}
void es_fun_2() {
aa_mu.ReaderLock();
Bar.aa_elr_fun_s();
aa_mu.Unlock();
}
void es_fun_3() {
sls_mu.Lock();
MyLRFoo.test();
sls_mu.Unlock();
}
void es_fun_4() {
sls_mu2.Lock();
MyLRFoo.testShared();
sls_mu2.Unlock();
}
void es_fun_5() {
sls_mu2.ReaderLock();
MyLRFoo.testShared();
sls_mu2.Unlock();
}
void es_fun_6() {
Bar.le_fun();
}
void es_fun_7() {
sls_mu.Lock();
elr_fun();
sls_mu.Unlock();
}
void es_fun_8() NO_THREAD_SAFETY_ANALYSIS;
void es_fun_8() {
Bar.aa_elr_fun_s();
}
void es_fun_9() SHARED_LOCKS_REQUIRED(aa_mu);
void es_fun_9() {
Bar.aa_elr_fun_s();
}
void es_fun_10() EXCLUSIVE_LOCKS_REQUIRED(aa_mu);
void es_fun_10() {
Bar.aa_elr_fun_s();
}
void es_bad_0() {
Bar.aa_elr_fun(); // \
// expected-warning {{calling function 'aa_elr_fun' requires holding mutex 'aa_mu' exclusively}}
}
void es_bad_1() {
aa_mu.ReaderLock();
Bar.aa_elr_fun(); // \
// expected-warning {{calling function 'aa_elr_fun' requires holding mutex 'aa_mu' exclusively}}
aa_mu.Unlock();
}
void es_bad_2() {
Bar.aa_elr_fun_s(); // \
// expected-warning {{calling function 'aa_elr_fun_s' requires holding mutex 'aa_mu'}}
}
void es_bad_3() {
MyLRFoo.test(); // \
// expected-warning {{calling function 'test' requires holding mutex 'sls_mu' exclusively}}
}
void es_bad_4() {
MyLRFoo.testShared(); // \
// expected-warning {{calling function 'testShared' requires holding mutex 'sls_mu2'}}
}
void es_bad_5() {
sls_mu.ReaderLock();
MyLRFoo.test(); // \
// expected-warning {{calling function 'test' requires holding mutex 'sls_mu' exclusively}}
sls_mu.Unlock();
}
void es_bad_6() {
sls_mu.Lock();
Bar.le_fun(); // \
// expected-warning {{cannot call function 'le_fun' while mutex 'sls_mu' is held}}
sls_mu.Unlock();
}
void es_bad_7() {
sls_mu.ReaderLock();
Bar.le_fun(); // \
// expected-warning {{cannot call function 'le_fun' while mutex 'sls_mu' is held}}
sls_mu.Unlock();
}
//-----------------------------------------------//
// Unparseable lock expressions
// ----------------------------------------------//
// FIXME -- derive new tests for unhandled expressions
//----------------------------------------------------------------------------//
// The following test cases are ported from the gcc thread safety implementation
// They are each wrapped inside a namespace with the test number of the gcc test
//
// FIXME: add all the gcc tests, once this analysis passes them.
//----------------------------------------------------------------------------//
//-----------------------------------------//
// Good testcases (no errors)
//-----------------------------------------//
namespace thread_annot_lock_20 {
class Bar {
public:
static int func1() EXCLUSIVE_LOCKS_REQUIRED(mu1_);
static int b_ GUARDED_BY(mu1_);
static Mutex mu1_;
static int a_ GUARDED_BY(mu1_);
};
Bar b1;
int Bar::func1()
{
int res = 5;
if (a_ == 4)
res = b_;
return res;
}
} // end namespace thread_annot_lock_20
namespace thread_annot_lock_22 {
// Test various usage of GUARDED_BY and PT_GUARDED_BY annotations, especially
// uses in class definitions.
Mutex mu;
class Bar {
public:
int a_ GUARDED_BY(mu1_);
int b_;
int *q PT_GUARDED_BY(mu);
Mutex mu1_ ACQUIRED_AFTER(mu);
};
Bar b1, *b3;
int *p GUARDED_BY(mu) PT_GUARDED_BY(mu);
int res GUARDED_BY(mu) = 5;
int func(int i)
{
int x;
mu.Lock();
b1.mu1_.Lock();
res = b1.a_ + b3->b_;
*p = i;
b1.a_ = res + b3->b_;
b3->b_ = *b1.q;
b1.mu1_.Unlock();
b1.b_ = res;
x = res;
mu.Unlock();
return x;
}
} // end namespace thread_annot_lock_22
namespace thread_annot_lock_27_modified {
// test lock annotations applied to function definitions
// Modified: applied annotations only to function declarations
Mutex mu1;
Mutex mu2 ACQUIRED_AFTER(mu1);
class Foo {
public:
int method1(int i) SHARED_LOCKS_REQUIRED(mu2) EXCLUSIVE_LOCKS_REQUIRED(mu1);
};
int Foo::method1(int i) {
return i;
}
int foo(int i) EXCLUSIVE_LOCKS_REQUIRED(mu2) SHARED_LOCKS_REQUIRED(mu1);
int foo(int i) {
return i;
}
static int bar(int i) EXCLUSIVE_LOCKS_REQUIRED(mu1);
static int bar(int i) {
return i;
}
void main() {
Foo a;
mu1.Lock();
mu2.Lock();
a.method1(1);
foo(2);
mu2.Unlock();
bar(3);
mu1.Unlock();
}
} // end namespace thread_annot_lock_27_modified
namespace thread_annot_lock_38 {
// Test the case where a template member function is annotated with lock
// attributes in a non-template class.
class Foo {
public:
void func1(int y) LOCKS_EXCLUDED(mu_);
template <typename T> void func2(T x) LOCKS_EXCLUDED(mu_);
private:
Mutex mu_;
};
Foo *foo;
void main()
{
foo->func1(5);
foo->func2(5);
}
} // end namespace thread_annot_lock_38
namespace thread_annot_lock_43 {
// Tests lock canonicalization
class Foo {
public:
Mutex *mu_;
};
class FooBar {
public:
Foo *foo_;
int GetA() EXCLUSIVE_LOCKS_REQUIRED(foo_->mu_) { return a_; }
int a_ GUARDED_BY(foo_->mu_);
};
FooBar *fb;
void main()
{
int x;
fb->foo_->mu_->Lock();
x = fb->GetA();
fb->foo_->mu_->Unlock();
}
} // end namespace thread_annot_lock_43
namespace thread_annot_lock_49 {
// Test the support for use of lock expression in the annotations
class Foo {
public:
Mutex foo_mu_;
};
class Bar {
private:
Foo *foo;
Mutex bar_mu_ ACQUIRED_AFTER(foo->foo_mu_);
public:
void Test1() {
foo->foo_mu_.Lock();
bar_mu_.Lock();
bar_mu_.Unlock();
foo->foo_mu_.Unlock();
}
};
void main() {
Bar bar;
bar.Test1();
}
} // end namespace thread_annot_lock_49
namespace thread_annot_lock_61_modified {
// Modified to fix the compiler errors
// Test the fix for a bug introduced by the support of pass-by-reference
// parameters.
struct Foo { Foo &operator<< (bool) {return *this;} };
Foo &getFoo();
struct Bar { Foo &func () {return getFoo();} };
struct Bas { void operator& (Foo &) {} };
void mumble()
{
Bas() & Bar().func() << "" << "";
Bas() & Bar().func() << "";
}
} // end namespace thread_annot_lock_61_modified
namespace thread_annot_lock_65 {
// Test the fix for a bug in the support of allowing reader locks for
// non-const, non-modifying overload functions. (We didn't handle the builtin
// properly.)
enum MyFlags {
Zero,
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine
};
inline MyFlags
operator|(MyFlags a, MyFlags b)
{
return MyFlags(static_cast<int>(a) | static_cast<int>(b));
}
inline MyFlags&
operator|=(MyFlags& a, MyFlags b)
{
return a = a | b;
}
} // end namespace thread_annot_lock_65
namespace thread_annot_lock_66_modified {
// Modified: Moved annotation to function defn
// Test annotations on out-of-line definitions of member functions where the
// annotations refer to locks that are also data members in the class.
Mutex mu;
class Foo {
public:
int method1(int i) SHARED_LOCKS_REQUIRED(mu1, mu, mu2);
int data GUARDED_BY(mu1);
Mutex *mu1;
Mutex *mu2;
};
int Foo::method1(int i)
{
return data + i;
}
void main()
{
Foo a;
a.mu2->Lock();
a.mu1->Lock();
mu.Lock();
a.method1(1);
mu.Unlock();
a.mu1->Unlock();
a.mu2->Unlock();
}
} // end namespace thread_annot_lock_66_modified
namespace thread_annot_lock_68_modified {
// Test a fix to a bug in the delayed name binding with nested template
// instantiation. We use a stack to make sure a name is not resolved to an
// inner context.
template <typename T>
class Bar {
Mutex mu_;
};
template <typename T>
class Foo {
public:
void func(T x) {
mu_.Lock();
count_ = x;
mu_.Unlock();
}
private:
T count_ GUARDED_BY(mu_);
Bar<T> bar_;
Mutex mu_;
};
void main()
{
Foo<int> *foo;
foo->func(5);
}
} // end namespace thread_annot_lock_68_modified
namespace thread_annot_lock_30_modified {
// Test delay parsing of lock attribute arguments with nested classes.
// Modified: trylocks replaced with exclusive_lock_fun
int a = 0;
class Bar {
struct Foo;
public:
void MyLock() EXCLUSIVE_LOCK_FUNCTION(mu);
int func() {
MyLock();
// if (foo == 0) {
// return 0;
// }
a = 5;
mu.Unlock();
return 1;
}
class FooBar {
int x;
int y;
};
private:
Mutex mu;
};
Bar *bar;
void main()
{
bar->func();
}
} // end namespace thread_annot_lock_30_modified
namespace thread_annot_lock_47 {
// Test the support for annotations on virtual functions.
// This is a good test case. (i.e. There should be no warning emitted by the
// compiler.)
class Base {
public:
virtual void func1() EXCLUSIVE_LOCKS_REQUIRED(mu_);
virtual void func2() LOCKS_EXCLUDED(mu_);
Mutex mu_;
};
class Child : public Base {
public:
virtual void func1() EXCLUSIVE_LOCKS_REQUIRED(mu_);
virtual void func2() LOCKS_EXCLUDED(mu_);
};
void main() {
Child *c;
Base *b = c;
b->mu_.Lock();
b->func1();
b->mu_.Unlock();
b->func2();
c->mu_.Lock();
c->func1();
c->mu_.Unlock();
c->func2();
}
} // end namespace thread_annot_lock_47
//-----------------------------------------//
// Tests which produce errors
//-----------------------------------------//
namespace thread_annot_lock_13 {
Mutex mu1;
Mutex mu2;
int g GUARDED_BY(mu1);
int w GUARDED_BY(mu2);
class Foo {
public:
void bar() LOCKS_EXCLUDED(mu_, mu1);
int foo() SHARED_LOCKS_REQUIRED(mu_) EXCLUSIVE_LOCKS_REQUIRED(mu2);
private:
int a_ GUARDED_BY(mu_);
public:
Mutex mu_ ACQUIRED_AFTER(mu1);
};
int Foo::foo()
{
int res;
w = 5;
res = a_ + 5;
return res;
}
void Foo::bar()
{
int x;
mu_.Lock();
x = foo(); // expected-warning {{calling function 'foo' requires holding mutex 'mu2' exclusively}}
a_ = x + 1;
mu_.Unlock();
if (x > 5) {
mu1.Lock();
g = 2;
mu1.Unlock();
}
}
void main()
{
Foo f1, *f2;
f1.mu_.Lock();
f1.bar(); // expected-warning {{cannot call function 'bar' while mutex 'f1.mu_' is held}}
mu2.Lock();
f1.foo();
mu2.Unlock();
f1.mu_.Unlock();
f2->mu_.Lock();
f2->bar(); // expected-warning {{cannot call function 'bar' while mutex 'f2->mu_' is held}}
f2->mu_.Unlock();
mu2.Lock();
w = 2;
mu2.Unlock();
}
} // end namespace thread_annot_lock_13
namespace thread_annot_lock_18_modified {
// Modified: Trylocks removed
// Test the ability to distnguish between the same lock field of
// different objects of a class.
class Bar {
public:
bool MyLock() EXCLUSIVE_LOCK_FUNCTION(mu1_);
void MyUnlock() UNLOCK_FUNCTION(mu1_);
int a_ GUARDED_BY(mu1_);
private:
Mutex mu1_;
};
Bar *b1, *b2;
void func()
{
b1->MyLock();
b1->a_ = 5;
b2->a_ = 3; // \
// expected-warning {{writing variable 'a_' requires holding mutex 'b2->mu1_' exclusively}} \
// expected-note {{found near match 'b1->mu1_'}}
b2->MyLock();
b2->MyUnlock();
b1->MyUnlock();
}
} // end namespace thread_annot_lock_18_modified
namespace thread_annot_lock_21 {
// Test various usage of GUARDED_BY and PT_GUARDED_BY annotations, especially
// uses in class definitions.
Mutex mu;
class Bar {
public:
int a_ GUARDED_BY(mu1_);
int b_;
int *q PT_GUARDED_BY(mu);
Mutex mu1_ ACQUIRED_AFTER(mu);
};
Bar b1, *b3;
int *p GUARDED_BY(mu) PT_GUARDED_BY(mu);
int res GUARDED_BY(mu) = 5;
int func(int i)
{
int x;
b3->mu1_.Lock();
res = b1.a_ + b3->b_; // expected-warning {{reading variable 'a_' requires holding mutex 'b1.mu1_'}} \
// expected-warning {{writing variable 'res' requires holding mutex 'mu' exclusively}} \
// expected-note {{found near match 'b3->mu1_'}}
*p = i; // expected-warning {{reading variable 'p' requires holding mutex 'mu'}} \
// expected-warning {{writing the value pointed to by 'p' requires holding mutex 'mu' exclusively}}
b1.a_ = res + b3->b_; // expected-warning {{reading variable 'res' requires holding mutex 'mu'}} \
// expected-warning {{writing variable 'a_' requires holding mutex 'b1.mu1_' exclusively}} \
// expected-note {{found near match 'b3->mu1_'}}
b3->b_ = *b1.q; // expected-warning {{reading the value pointed to by 'q' requires holding mutex 'mu'}}
b3->mu1_.Unlock();
b1.b_ = res; // expected-warning {{reading variable 'res' requires holding mutex 'mu'}}
x = res; // expected-warning {{reading variable 'res' requires holding mutex 'mu'}}
return x;
}
} // end namespace thread_annot_lock_21
namespace thread_annot_lock_35_modified {
// Test the analyzer's ability to distinguish the lock field of different
// objects.
class Foo {
private:
Mutex lock_;
int a_ GUARDED_BY(lock_);
public:
void Func(Foo* child) LOCKS_EXCLUDED(lock_) {
Foo *new_foo = new Foo;
lock_.Lock();
child->Func(new_foo); // There shouldn't be any warning here as the
// acquired lock is not in child.
child->bar(7); // \
// expected-warning {{calling function 'bar' requires holding mutex 'child->lock_' exclusively}} \
// expected-note {{found near match 'lock_'}}
child->a_ = 5; // \
// expected-warning {{writing variable 'a_' requires holding mutex 'child->lock_' exclusively}} \
// expected-note {{found near match 'lock_'}}
lock_.Unlock();
}
void bar(int y) EXCLUSIVE_LOCKS_REQUIRED(lock_) {
a_ = y;
}
};
Foo *x;
void main() {
Foo *child = new Foo;
x->Func(child);
}
} // end namespace thread_annot_lock_35_modified
namespace thread_annot_lock_36_modified {
// Modified to move the annotations to function defns.
// Test the analyzer's ability to distinguish the lock field of different
// objects
class Foo {
private:
Mutex lock_;
int a_ GUARDED_BY(lock_);
public:
void Func(Foo* child) LOCKS_EXCLUDED(lock_);
void bar(int y) EXCLUSIVE_LOCKS_REQUIRED(lock_);
};
void Foo::Func(Foo* child) {
Foo *new_foo = new Foo;
lock_.Lock();
child->lock_.Lock();
child->Func(new_foo); // expected-warning {{cannot call function 'Func' while mutex 'child->lock_' is held}}
child->bar(7);
child->a_ = 5;
child->lock_.Unlock();
lock_.Unlock();
}
void Foo::bar(int y) {
a_ = y;
}
Foo *x;
void main() {
Foo *child = new Foo;
x->Func(child);
}
} // end namespace thread_annot_lock_36_modified
namespace thread_annot_lock_42 {
// Test support of multiple lock attributes of the same kind on a decl.
class Foo {
private:
Mutex mu1, mu2, mu3;
int x GUARDED_BY(mu1) GUARDED_BY(mu2);
int y GUARDED_BY(mu2);
void f2() LOCKS_EXCLUDED(mu1) LOCKS_EXCLUDED(mu2) LOCKS_EXCLUDED(mu3) {
mu2.Lock();
y = 2;
mu2.Unlock();
}
public:
void f1() EXCLUSIVE_LOCKS_REQUIRED(mu2) EXCLUSIVE_LOCKS_REQUIRED(mu1) {
x = 5;
f2(); // expected-warning {{cannot call function 'f2' while mutex 'mu1' is held}} \
// expected-warning {{cannot call function 'f2' while mutex 'mu2' is held}}
}
};
Foo *foo;
void func()
{
foo->f1(); // expected-warning {{calling function 'f1' requires holding mutex 'foo->mu2' exclusively}} \
// expected-warning {{calling function 'f1' requires holding mutex 'foo->mu1' exclusively}}
}
} // end namespace thread_annot_lock_42
namespace thread_annot_lock_46 {
// Test the support for annotations on virtual functions.
class Base {
public:
virtual void func1() EXCLUSIVE_LOCKS_REQUIRED(mu_);
virtual void func2() LOCKS_EXCLUDED(mu_);
Mutex mu_;
};
class Child : public Base {
public:
virtual void func1() EXCLUSIVE_LOCKS_REQUIRED(mu_);
virtual void func2() LOCKS_EXCLUDED(mu_);
};
void main() {
Child *c;
Base *b = c;
b->func1(); // expected-warning {{calling function 'func1' requires holding mutex 'b->mu_' exclusively}}
b->mu_.Lock();
b->func2(); // expected-warning {{cannot call function 'func2' while mutex 'b->mu_' is held}}
b->mu_.Unlock();
c->func1(); // expected-warning {{calling function 'func1' requires holding mutex 'c->mu_' exclusively}}
c->mu_.Lock();
c->func2(); // expected-warning {{cannot call function 'func2' while mutex 'c->mu_' is held}}
c->mu_.Unlock();
}
} // end namespace thread_annot_lock_46
namespace thread_annot_lock_67_modified {
// Modified: attributes on definitions moved to declarations
// Test annotations on out-of-line definitions of member functions where the
// annotations refer to locks that are also data members in the class.
Mutex mu;
Mutex mu3;
class Foo {
public:
int method1(int i) SHARED_LOCKS_REQUIRED(mu1, mu, mu2, mu3);
int data GUARDED_BY(mu1);
Mutex *mu1;
Mutex *mu2;
};
int Foo::method1(int i) {
return data + i;
}
void main()
{
Foo a;
a.method1(1); // expected-warning {{calling function 'method1' requires holding mutex 'a.mu1'}} \
// expected-warning {{calling function 'method1' requires holding mutex 'mu'}} \
// expected-warning {{calling function 'method1' requires holding mutex 'a.mu2'}} \
// expected-warning {{calling function 'method1' requires holding mutex 'mu3'}}
}
} // end namespace thread_annot_lock_67_modified
namespace substitution_test {
class MyData {
public:
Mutex mu;
void lockData() EXCLUSIVE_LOCK_FUNCTION(mu);
void unlockData() UNLOCK_FUNCTION(mu);
void doSomething() EXCLUSIVE_LOCKS_REQUIRED(mu) { }
};
class DataLocker {
public:
void lockData (MyData *d) EXCLUSIVE_LOCK_FUNCTION(d->mu);
void unlockData(MyData *d) UNLOCK_FUNCTION(d->mu);
};
class Foo {
public:
void foo(MyData* d) EXCLUSIVE_LOCKS_REQUIRED(d->mu) { }
void bar1(MyData* d) {
d->lockData();
foo(d);
d->unlockData();
}
void bar2(MyData* d) {
DataLocker dlr;
dlr.lockData(d);
foo(d);
dlr.unlockData(d);
}
void bar3(MyData* d1, MyData* d2) {
DataLocker dlr;
dlr.lockData(d1); // expected-note {{mutex acquired here}}
dlr.unlockData(d2); // \
// expected-warning {{releasing mutex 'd2->mu' that was not held}}
} // expected-warning {{mutex 'd1->mu' is still held at the end of function}}
void bar4(MyData* d1, MyData* d2) {
DataLocker dlr;
dlr.lockData(d1);
foo(d2); // \
// expected-warning {{calling function 'foo' requires holding mutex 'd2->mu' exclusively}} \
// expected-note {{found near match 'd1->mu'}}
dlr.unlockData(d1);
}
};
// Automatic object destructor calls don't appear as expressions in the CFG,
// so we have to handle them separately whenever substitutions are required.
struct DestructorRequires {
Mutex mu;
~DestructorRequires() EXCLUSIVE_LOCKS_REQUIRED(mu);
};
void destructorRequires() {
DestructorRequires rd;
rd.mu.AssertHeld();
}
struct DestructorExcludes {
Mutex mu;
~DestructorExcludes() LOCKS_EXCLUDED(mu);
};
void destructorExcludes() {
DestructorExcludes ed;
ed.mu.Lock(); // expected-note {{mutex acquired here}}
} // expected-warning {{cannot call function '~DestructorExcludes' while mutex 'ed.mu' is held}}
// expected-warning@-1 {{mutex 'ed.mu' is still held at the end of function}}
} // end namespace substituation_test
namespace constructor_destructor_tests {
Mutex fooMu;
int myVar GUARDED_BY(fooMu);
class Foo {
public:
Foo() EXCLUSIVE_LOCK_FUNCTION(fooMu) { }
~Foo() UNLOCK_FUNCTION(fooMu) { }
};
void fooTest() {
Foo foo;
myVar = 0;
}
}
namespace template_member_test {
struct S { int n; };
struct T {
Mutex m;
S *s GUARDED_BY(this->m);
};
Mutex m;
struct U {
union {
int n;
};
} *u GUARDED_BY(m);
template<typename U>
struct IndirectLock {
int DoNaughtyThings(T *t) {
u->n = 0; // expected-warning {{reading variable 'u' requires holding mutex 'm'}}
return t->s->n; // expected-warning {{reading variable 's' requires holding mutex 't->m'}}
}
};
template struct IndirectLock<int>; // expected-note {{here}}
struct V {
void f(int);
void f(double);
Mutex m;
V *p GUARDED_BY(this->m);
};
template<typename U> struct W {
V v;
void f(U u) {
v.p->f(u); // expected-warning {{reading variable 'p' requires holding mutex 'v.m'}}
}
};
template struct W<int>; // expected-note {{here}}
}
namespace test_scoped_lockable {
struct TestScopedLockable {
Mutex mu1;
Mutex mu2;
int a __attribute__((guarded_by(mu1)));
int b __attribute__((guarded_by(mu2)));
bool getBool();
bool lock2Bool(MutexLock);
void foo1() {
MutexLock mulock(&mu1);
a = 5;
}
#ifdef __cpp_guaranteed_copy_elision
void const_lock() {
const MutexLock mulock = MutexLock(&mu1);
a = 5;
}
#endif
void temporary() {
MutexLock{&mu1}, a = 5;
}
void temporary_cfg(int x) {
// test the case where a pair of temporary Ctor and Dtor is in different CFG blocks
lock2Bool(MutexLock{&mu1}) || x;
MutexLock{&mu1}; // no-warn
}
void lifetime_extension() {
const MutexLock &mulock = MutexLock(&mu1);
a = 5;
}
void foo2() {
ReaderMutexLock mulock1(&mu1);
if (getBool()) {
MutexLock mulock2a(&mu2);
b = a + 1;
}
else {
MutexLock mulock2b(&mu2);
b = a + 2;
}
}
void foo3() {
MutexLock mulock_a(&mu1); // expected-note{{mutex acquired here}}
MutexLock mulock_b(&mu1); // \
// expected-warning {{acquiring mutex 'mu1' that is already held}}
}
void temporary_double_lock() {
MutexLock mulock_a(&mu1); // expected-note{{mutex acquired here}}
MutexLock{&mu1}; // \
// expected-warning {{acquiring mutex 'mu1' that is already held}}
}
void foo4() {
MutexLock mulock1(&mu1), mulock2(&mu2);
a = b+1;
b = a+1;
}
void foo5() {
DoubleMutexLock mulock(&mu1, &mu2);
a = b + 1;
b = a + 1;
}
};
} // end namespace test_scoped_lockable
namespace FunctionAttrTest {
class Foo {
public:
Mutex mu_;
int a GUARDED_BY(mu_);
};
Foo fooObj;
void foo() EXCLUSIVE_LOCKS_REQUIRED(fooObj.mu_);
void bar() {
foo(); // expected-warning {{calling function 'foo' requires holding mutex 'fooObj.mu_' exclusively}}
fooObj.mu_.Lock();
foo();
fooObj.mu_.Unlock();
}
}; // end namespace FunctionAttrTest
namespace TryLockTest {
struct TestTryLock {
Mutex mu;
int a GUARDED_BY(mu);
bool cond;
void foo1() {
if (mu.TryLock()) {
a = 1;
mu.Unlock();
}
}
void foo2() {
if (!mu.TryLock()) return;
a = 2;
mu.Unlock();
}
void foo2_builtin_expect() {
if (__builtin_expect(!mu.TryLock(), false))
return;
a = 2;
mu.Unlock();
}
void foo3() {
bool b = mu.TryLock();
if (b) {
a = 3;
mu.Unlock();
}
}
void foo3_builtin_expect() {
bool b = mu.TryLock();
if (__builtin_expect(b, true)) {
a = 3;
mu.Unlock();
}
}
void foo4() {
bool b = mu.TryLock();
if (!b) return;
a = 4;
mu.Unlock();
}
void foo5() {
while (mu.TryLock()) {
a = a + 1;
mu.Unlock();
}
}
void foo6() {
bool b = mu.TryLock();
b = !b;
if (b) return;
a = 6;
mu.Unlock();
}
void foo7() {
bool b1 = mu.TryLock();
bool b2 = !b1;
bool b3 = !b2;
if (b3) {
a = 7;
mu.Unlock();
}
}
// Test use-def chains: join points
void foo8() {
bool b = mu.TryLock();
bool b2 = b;
if (cond)
b = true;
if (b) { // b should be unknown at this point, because of the join point
a = 8; // expected-warning {{writing variable 'a' requires holding mutex 'mu' exclusively}}
}
if (b2) { // b2 should be known at this point.
a = 8;
mu.Unlock();
}
}
// Test use-def-chains: back edges
void foo9() {
bool b = mu.TryLock();
for (int i = 0; i < 10; ++i);
if (b) { // b is still known, because the loop doesn't alter it
a = 9;
mu.Unlock();
}
}
// Test use-def chains: back edges
void foo10() {
bool b = mu.TryLock();
while (cond) {
if (b) { // b should be unknown at this point b/c of the loop
a = 10; // expected-warning {{writing variable 'a' requires holding mutex 'mu' exclusively}}
}
b = !b;
}
}
// Test merge of exclusive trylock
void foo11() {
if (cond) {
if (!mu.TryLock())
return;
}
else {
mu.Lock();
}
a = 10;
mu.Unlock();
}
// Test merge of shared trylock
void foo12() {
if (cond) {
if (!mu.ReaderTryLock())
return;
}
else {
mu.ReaderLock();
}
int i = a;
mu.Unlock();
}
// Test with conditional operator
void foo13() {
if (mu.TryLock() ? 1 : 0)
mu.Unlock();
}
void foo14() {
if (mu.TryLock() ? 0 : 1)
return;
mu.Unlock();
}
void foo15() {
if (mu.TryLock() ? 0 : 1) // expected-note{{mutex acquired here}}
mu.Unlock(); // expected-warning{{releasing mutex 'mu' that was not held}}
} // expected-warning{{mutex 'mu' is not held on every path through here}}
}; // end TestTrylock
} // end namespace TrylockTest
namespace TestTemplateAttributeInstantiation {
class Foo1 {
public:
Mutex mu_;
int a GUARDED_BY(mu_);
};
class Foo2 {
public:
int a GUARDED_BY(mu_);
Mutex mu_;
};
class Bar {
public:
// Test non-dependent expressions in attributes on template functions
template <class T>
void barND(Foo1 *foo, T *fooT) EXCLUSIVE_LOCKS_REQUIRED(foo->mu_) {
foo->a = 0;
}
// Test dependent expressions in attributes on template functions
template <class T>
void barD(Foo1 *foo, T *fooT) EXCLUSIVE_LOCKS_REQUIRED(fooT->mu_) {
fooT->a = 0;
}
};
template <class T>
class BarT {
public:
Foo1 fooBase;
T fooBaseT;
// Test non-dependent expression in ordinary method on template class
void barND() EXCLUSIVE_LOCKS_REQUIRED(fooBase.mu_) {
fooBase.a = 0;
}
// Test dependent expressions in ordinary methods on template class
void barD() EXCLUSIVE_LOCKS_REQUIRED(fooBaseT.mu_) {
fooBaseT.a = 0;
}
// Test dependent expressions in template method in template class
template <class T2>
void barTD(T2 *fooT) EXCLUSIVE_LOCKS_REQUIRED(fooBaseT.mu_, fooT->mu_) {
fooBaseT.a = 0;
fooT->a = 0;
}
};
template <class T>
class Cell {
public:
Mutex mu_;
// Test dependent guarded_by
T data GUARDED_BY(mu_);
void fooEx() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
data = 0;
}
void foo() {
mu_.Lock();
data = 0;
mu_.Unlock();
}
};
void test() {
Bar b;
BarT<Foo2> bt;
Foo1 f1;
Foo2 f2;
f1.mu_.Lock();
f2.mu_.Lock();
bt.fooBase.mu_.Lock();
bt.fooBaseT.mu_.Lock();
b.barND(&f1, &f2);
b.barD(&f1, &f2);
bt.barND();
bt.barD();
bt.barTD(&f2);
f1.mu_.Unlock();
bt.barTD(&f1); // \
// expected-warning {{calling function 'barTD<TestTemplateAttributeInstantiation::Foo1>' requires holding mutex 'f1.mu_' exclusively}} \
// expected-note {{found near match 'bt.fooBase.mu_'}}
bt.fooBase.mu_.Unlock();
bt.fooBaseT.mu_.Unlock();
f2.mu_.Unlock();
Cell<int> cell;
cell.data = 0; // \
// expected-warning {{writing variable 'data' requires holding mutex 'cell.mu_' exclusively}}
cell.foo();
cell.mu_.Lock();
cell.fooEx();
cell.mu_.Unlock();
}
template <class T>
class CellDelayed {
public:
// Test dependent guarded_by
T data GUARDED_BY(mu_);
static T static_data GUARDED_BY(static_mu_);
void fooEx(CellDelayed<T> *other) EXCLUSIVE_LOCKS_REQUIRED(mu_, other->mu_) {
this->data = other->data;
}
template <class T2>
void fooExT(CellDelayed<T2> *otherT) EXCLUSIVE_LOCKS_REQUIRED(mu_, otherT->mu_) {
this->data = otherT->data;
}
void foo() {
mu_.Lock();
data = 0;
mu_.Unlock();
}
Mutex mu_;
static Mutex static_mu_;
};
void testDelayed() {
CellDelayed<int> celld;
CellDelayed<int> celld2;
celld.foo();
celld.mu_.Lock();
celld2.mu_.Lock();
celld.fooEx(&celld2);
celld.fooExT(&celld2);
celld2.mu_.Unlock();
celld.mu_.Unlock();
}
}; // end namespace TestTemplateAttributeInstantiation
namespace FunctionDeclDefTest {
class Foo {
public:
Mutex mu_;
int a GUARDED_BY(mu_);
virtual void foo1(Foo *f_declared) EXCLUSIVE_LOCKS_REQUIRED(f_declared->mu_);
};
// EXCLUSIVE_LOCKS_REQUIRED should be applied, and rewritten to f_defined->mu_
void Foo::foo1(Foo *f_defined) {
f_defined->a = 0;
};
void test() {
Foo myfoo;
myfoo.foo1(&myfoo); // \
// expected-warning {{calling function 'foo1' requires holding mutex 'myfoo.mu_' exclusively}}
myfoo.mu_.Lock();
myfoo.foo1(&myfoo);
myfoo.mu_.Unlock();
}
};
namespace GoingNative {
struct LOCKABLE mutex {
void lock() EXCLUSIVE_LOCK_FUNCTION();
void unlock() UNLOCK_FUNCTION();
// ...
};
bool foo();
bool bar();
mutex m;
void test() {
m.lock();
while (foo()) { // expected-warning {{expecting mutex 'm' to be held at start of each loop}}
m.unlock();
// ...
if (bar()) {
// ...
if (foo())
continue;
//...
}
// ...
m.lock(); // expected-note {{mutex acquired here}}
}
m.unlock();
}
}
namespace FunctionDefinitionTest {
class Foo {
public:
void foo1();
void foo2();
void foo3(Foo *other);
template<class T>
void fooT1(const T& dummy1);
template<class T>
void fooT2(const T& dummy2) EXCLUSIVE_LOCKS_REQUIRED(mu_);
Mutex mu_;
int a GUARDED_BY(mu_);
};
template<class T>
class FooT {
public:
void foo();
Mutex mu_;
T a GUARDED_BY(mu_);
};
void Foo::foo1() NO_THREAD_SAFETY_ANALYSIS {
a = 1;
}
void Foo::foo2() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
a = 2;
}
void Foo::foo3(Foo *other) EXCLUSIVE_LOCKS_REQUIRED(other->mu_) {
other->a = 3;
}
template<class T>
void Foo::fooT1(const T& dummy1) EXCLUSIVE_LOCKS_REQUIRED(mu_) {
a = dummy1;
}
/* TODO -- uncomment with template instantiation of attributes.
template<class T>
void Foo::fooT2(const T& dummy2) {
a = dummy2;
}
*/
void fooF1(Foo *f) EXCLUSIVE_LOCKS_REQUIRED(f->mu_) {
f->a = 1;
}
void fooF2(Foo *f);
void fooF2(Foo *f) EXCLUSIVE_LOCKS_REQUIRED(f->mu_) {
f->a = 2;
}
void fooF3(Foo *f) EXCLUSIVE_LOCKS_REQUIRED(f->mu_);
void fooF3(Foo *f) {
f->a = 3;
}
template<class T>
void FooT<T>::foo() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
a = 0;
}
void test() {
int dummy = 0;
Foo myFoo;
myFoo.foo2(); // \
// expected-warning {{calling function 'foo2' requires holding mutex 'myFoo.mu_' exclusively}}
myFoo.foo3(&myFoo); // \
// expected-warning {{calling function 'foo3' requires holding mutex 'myFoo.mu_' exclusively}}
myFoo.fooT1(dummy); // \
// expected-warning {{calling function 'fooT1<int>' requires holding mutex 'myFoo.mu_' exclusively}}
myFoo.fooT2(dummy); // \
// expected-warning {{calling function 'fooT2<int>' requires holding mutex 'myFoo.mu_' exclusively}}
fooF1(&myFoo); // \
// expected-warning {{calling function 'fooF1' requires holding mutex 'myFoo.mu_' exclusively}}
fooF2(&myFoo); // \
// expected-warning {{calling function 'fooF2' requires holding mutex 'myFoo.mu_' exclusively}}
fooF3(&myFoo); // \
// expected-warning {{calling function 'fooF3' requires holding mutex 'myFoo.mu_' exclusively}}
myFoo.mu_.Lock();
myFoo.foo2();
myFoo.foo3(&myFoo);
myFoo.fooT1(dummy);
myFoo.fooT2(dummy);
fooF1(&myFoo);
fooF2(&myFoo);
fooF3(&myFoo);
myFoo.mu_.Unlock();
FooT<int> myFooT;
myFooT.foo(); // \
// expected-warning {{calling function 'foo' requires holding mutex 'myFooT.mu_' exclusively}}
}
} // end namespace FunctionDefinitionTest
namespace SelfLockingTest {
class LOCKABLE MyLock {
public:
int foo GUARDED_BY(this);
void lock() EXCLUSIVE_LOCK_FUNCTION();
void unlock() UNLOCK_FUNCTION();
void doSomething() {
this->lock(); // allow 'this' as a lock expression
foo = 0;
doSomethingElse();
this->unlock();
}
void doSomethingElse() EXCLUSIVE_LOCKS_REQUIRED(this) {
foo = 1;
};
void test() {
foo = 2; // \
// expected-warning {{writing variable 'foo' requires holding mutex 'this' exclusively}}
}
};
class LOCKABLE MyLock2 {
public:
Mutex mu_;
int foo GUARDED_BY(this);
// don't check inside lock and unlock functions
void lock() EXCLUSIVE_LOCK_FUNCTION() { mu_.Lock(); }
void unlock() UNLOCK_FUNCTION() { mu_.Unlock(); }
// don't check inside constructors and destructors
MyLock2() { foo = 1; }
~MyLock2() { foo = 0; }
};
} // end namespace SelfLockingTest
namespace InvalidNonstatic {
// Forward decl here causes bogus "invalid use of non-static data member"
// on reference to mutex_ in guarded_by attribute.
class Foo;
class Foo {
Mutex* mutex_;
int foo __attribute__((guarded_by(mutex_)));
};
} // end namespace InvalidNonStatic
namespace NoReturnTest {
bool condition();
void fatal() __attribute__((noreturn));
Mutex mu_;
void test1() {
MutexLock lock(&mu_);
if (condition()) {
fatal();
return;
}
}
} // end namespace NoReturnTest
namespace TestMultiDecl {
class Foo {
public:
int GUARDED_BY(mu_) a;
int GUARDED_BY(mu_) b, c;
void foo() {
a = 0; // \
// expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
b = 0; // \
// expected-warning {{writing variable 'b' requires holding mutex 'mu_' exclusively}}
c = 0; // \
// expected-warning {{writing variable 'c' requires holding mutex 'mu_' exclusively}}
}
private:
Mutex mu_;
};
} // end namespace TestMultiDecl
namespace WarnNoDecl {
class Foo {
void foo(int a); __attribute__(( // \
// expected-warning {{declaration does not declare anything}}
exclusive_locks_required(a))); // \
// expected-warning {{attribute exclusive_locks_required ignored}}
};
} // end namespace WarnNoDecl
namespace MoreLockExpressions {
class Foo {
public:
Mutex mu_;
int a GUARDED_BY(mu_);
};
class Bar {
public:
int b;
Foo* f;
Foo& getFoo() { return *f; }
Foo& getFoo2(int c) { return *f; }
Foo& getFoo3(int c, int d) { return *f; }
Foo& getFooey() { return *f; }
};
Foo& getBarFoo(Bar &bar, int c) { return bar.getFoo2(c); }
void test() {
Foo foo;
Foo *fooArray;
Foo &(*fooFuncPtr)();
Bar bar;
int a;
int b;
int c;
bar.getFoo().mu_.Lock();
bar.getFoo().a = 0;
bar.getFoo().mu_.Unlock();
(bar.getFoo().mu_).Lock(); // test parenthesis
bar.getFoo().a = 0;
(bar.getFoo().mu_).Unlock();
bar.getFoo2(a).mu_.Lock();
bar.getFoo2(a).a = 0;
bar.getFoo2(a).mu_.Unlock();
bar.getFoo3(a, b).mu_.Lock();
bar.getFoo3(a, b).a = 0;
bar.getFoo3(a, b).mu_.Unlock();
getBarFoo(bar, a).mu_.Lock();
getBarFoo(bar, a).a = 0;
getBarFoo(bar, a).mu_.Unlock();
bar.getFoo2(10).mu_.Lock();
bar.getFoo2(10).a = 0;
bar.getFoo2(10).mu_.Unlock();
bar.getFoo2(a + 1).mu_.Lock();
bar.getFoo2(a + 1).a = 0;
bar.getFoo2(a + 1).mu_.Unlock();
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Lock();
(a > 0 ? fooArray[1] : fooArray[b]).a = 0;
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Unlock();
fooFuncPtr().mu_.Lock();
fooFuncPtr().a = 0;
fooFuncPtr().mu_.Unlock();
}
void test2() {
Foo *fooArray;
Bar bar;
int a;
int b;
int c;
bar.getFoo().mu_.Lock();
bar.getFooey().a = 0; // \
// expected-warning {{writing variable 'a' requires holding mutex 'bar.getFooey().mu_' exclusively}} \
// expected-note {{found near match 'bar.getFoo().mu_'}}
bar.getFoo().mu_.Unlock();
bar.getFoo2(a).mu_.Lock();
bar.getFoo2(b).a = 0; // \
// expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo2(b).mu_' exclusively}} \
// expected-note {{found near match 'bar.getFoo2(a).mu_'}}
bar.getFoo2(a).mu_.Unlock();
bar.getFoo3(a, b).mu_.Lock();
bar.getFoo3(a, c).a = 0; // \
// expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo3(a, c).mu_' exclusively}} \
// expected-note {{found near match 'bar.getFoo3(a, b).mu_'}}
bar.getFoo3(a, b).mu_.Unlock();
getBarFoo(bar, a).mu_.Lock();
getBarFoo(bar, b).a = 0; // \
// expected-warning {{writing variable 'a' requires holding mutex 'getBarFoo(bar, b).mu_' exclusively}} \
// expected-note {{found near match 'getBarFoo(bar, a).mu_'}}
getBarFoo(bar, a).mu_.Unlock();
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Lock();
(a > 0 ? fooArray[b] : fooArray[c]).a = 0; // \
// expected-warning {{writing variable 'a' requires holding mutex '((0 < a) ? fooArray[b] : fooArray[c]).mu_' exclusively}} \
// expected-note {{found near match '((0 < a) ? fooArray[1] : fooArray[b]).mu_'}}
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Unlock();
}
} // end namespace MoreLockExpressions
namespace TrylockJoinPoint {
class Foo {
Mutex mu;
bool c;
void foo() {
if (c) {
if (!mu.TryLock())
return;
} else {
mu.Lock();
}
mu.Unlock();
}
};
} // end namespace TrylockJoinPoint
namespace LockReturned {
class Foo {
public:
int a GUARDED_BY(mu_);
void foo() EXCLUSIVE_LOCKS_REQUIRED(mu_);
void foo2(Foo* f) EXCLUSIVE_LOCKS_REQUIRED(mu_, f->mu_);
static void sfoo(Foo* f) EXCLUSIVE_LOCKS_REQUIRED(f->mu_);
Mutex* getMu() LOCK_RETURNED(mu_);
Mutex mu_;
static Mutex* getMu(Foo* f) LOCK_RETURNED(f->mu_);
};
// Calls getMu() directly to lock and unlock
void test1(Foo* f1, Foo* f2) {
f1->a = 0; // expected-warning {{writing variable 'a' requires holding mutex 'f1->mu_' exclusively}}
f1->foo(); // expected-warning {{calling function 'foo' requires holding mutex 'f1->mu_' exclusively}}
f1->foo2(f2); // expected-warning {{calling function 'foo2' requires holding mutex 'f1->mu_' exclusively}} \
// expected-warning {{calling function 'foo2' requires holding mutex 'f2->mu_' exclusively}}
Foo::sfoo(f1); // expected-warning {{calling function 'sfoo' requires holding mutex 'f1->mu_' exclusively}}
f1->getMu()->Lock();
f1->a = 0;
f1->foo();
f1->foo2(f2); // \
// expected-warning {{calling function 'foo2' requires holding mutex 'f2->mu_' exclusively}} \
// expected-note {{found near match 'f1->mu_'}}
Foo::getMu(f2)->Lock();
f1->foo2(f2);
Foo::getMu(f2)->Unlock();
Foo::sfoo(f1);
f1->getMu()->Unlock();
}
Mutex* getFooMu(Foo* f) LOCK_RETURNED(Foo::getMu(f));
class Bar : public Foo {
public:
int b GUARDED_BY(getMu());
void bar() EXCLUSIVE_LOCKS_REQUIRED(getMu());
void bar2(Bar* g) EXCLUSIVE_LOCKS_REQUIRED(getMu(this), g->getMu());
static void sbar(Bar* g) EXCLUSIVE_LOCKS_REQUIRED(g->getMu());
static void sbar2(Bar* g) EXCLUSIVE_LOCKS_REQUIRED(getFooMu(g));
};
// Use getMu() within other attributes.
// This requires at lest levels of substitution, more in the case of
void test2(Bar* b1, Bar* b2) {
b1->b = 0; // expected-warning {{writing variable 'b' requires holding mutex 'b1->mu_' exclusively}}
b1->bar(); // expected-warning {{calling function 'bar' requires holding mutex 'b1->mu_' exclusively}}
b1->bar2(b2); // expected-warning {{calling function 'bar2' requires holding mutex 'b1->mu_' exclusively}} \
// expected-warning {{calling function 'bar2' requires holding mutex 'b2->mu_' exclusively}}
Bar::sbar(b1); // expected-warning {{calling function 'sbar' requires holding mutex 'b1->mu_' exclusively}}
Bar::sbar2(b1); // expected-warning {{calling function 'sbar2' requires holding mutex 'b1->mu_' exclusively}}
b1->getMu()->Lock();
b1->b = 0;
b1->bar();
b1->bar2(b2); // \
// expected-warning {{calling function 'bar2' requires holding mutex 'b2->mu_' exclusively}} \
// // expected-note {{found near match 'b1->mu_'}}
b2->getMu()->Lock();
b1->bar2(b2);
b2->getMu()->Unlock();
Bar::sbar(b1);
Bar::sbar2(b1);
b1->getMu()->Unlock();
}
// Lock the mutex directly, but use attributes that call getMu()
// Also lock the mutex using getFooMu, which calls a lock_returned function.
void test3(Bar* b1, Bar* b2) {
b1->mu_.Lock();
b1->b = 0;
b1->bar();
getFooMu(b2)->Lock();
b1->bar2(b2);
getFooMu(b2)->Unlock();
Bar::sbar(b1);
Bar::sbar2(b1);
b1->mu_.Unlock();
}
} // end namespace LockReturned
namespace ReleasableScopedLock {
class Foo {
Mutex mu_;
bool c;
int a GUARDED_BY(mu_);
void test1();
void test2();
void test3();
void test4();
void test5();
void test6();
};
void Foo::test1() {
ReleasableMutexLock rlock(&mu_);
rlock.Release();
}
void Foo::test2() {
ReleasableMutexLock rlock(&mu_);
if (c) { // test join point -- held/not held during release
rlock.Release();
}
// No warning on join point because the lock will be released by the scope object anyway.
}
void Foo::test3() {
ReleasableMutexLock rlock(&mu_);
a = 0;
rlock.Release();
a = 1; // expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
}
void Foo::test4() {
ReleasableMutexLock rlock(&mu_);
rlock.Release(); // expected-note{{mutex released here}}
rlock.Release(); // expected-warning {{releasing mutex 'mu_' that was not held}}
}
void Foo::test5() {
ReleasableMutexLock rlock(&mu_);
if (c) {
rlock.Release();
}
// No warning on join point because the lock will be released by the scope object anyway.
rlock.Release(); // expected-warning {{releasing mutex 'mu_' that was not held}}
}
void Foo::test6() {
ReleasableMutexLock rlock(&mu_);
do {
if (c) {
rlock.Release();
break;
}
} while (c);
// No warning on join point because the lock will be released by the scope object anyway
a = 1; // expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
}
} // end namespace ReleasableScopedLock
namespace RelockableScopedLock {
class DeferTraits {};
class SCOPED_LOCKABLE RelockableExclusiveMutexLock {
public:
RelockableExclusiveMutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu);
RelockableExclusiveMutexLock(Mutex *mu, DeferTraits) LOCKS_EXCLUDED(mu);
~RelockableExclusiveMutexLock() EXCLUSIVE_UNLOCK_FUNCTION();
void Lock() EXCLUSIVE_LOCK_FUNCTION();
void Unlock() UNLOCK_FUNCTION();
};
struct SharedTraits {};
struct ExclusiveTraits {};
class SCOPED_LOCKABLE RelockableMutexLock {
public:
RelockableMutexLock(Mutex *mu, DeferTraits) LOCKS_EXCLUDED(mu);
RelockableMutexLock(Mutex *mu, SharedTraits) SHARED_LOCK_FUNCTION(mu);
RelockableMutexLock(Mutex *mu, ExclusiveTraits) EXCLUSIVE_LOCK_FUNCTION(mu);
~RelockableMutexLock() UNLOCK_FUNCTION();
void Lock() EXCLUSIVE_LOCK_FUNCTION();
void Unlock() UNLOCK_FUNCTION();
void ReaderLock() SHARED_LOCK_FUNCTION();
void ReaderUnlock() UNLOCK_FUNCTION();
void PromoteShared() UNLOCK_FUNCTION() EXCLUSIVE_LOCK_FUNCTION();
void DemoteExclusive() UNLOCK_FUNCTION() SHARED_LOCK_FUNCTION();
};
Mutex mu;
int x GUARDED_BY(mu);
bool b;
void print(int);
void relock() {
RelockableExclusiveMutexLock scope(&mu);
x = 2;
scope.Unlock();
x = 3; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
scope.Lock();
x = 4;
}
void deferLock() {
RelockableExclusiveMutexLock scope(&mu, DeferTraits{});
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
scope.Lock();
x = 3;
}
void relockExclusive() {
RelockableMutexLock scope(&mu, SharedTraits{});
print(x);
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
scope.ReaderUnlock();
print(x); // expected-warning {{reading variable 'x' requires holding mutex 'mu'}}
scope.Lock();
print(x);
x = 4;
scope.DemoteExclusive();
print(x);
x = 5; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
void relockShared() {
RelockableMutexLock scope(&mu, ExclusiveTraits{});
print(x);
x = 2;
scope.Unlock();
print(x); // expected-warning {{reading variable 'x' requires holding mutex 'mu'}}
scope.ReaderLock();
print(x);
x = 4; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
scope.PromoteShared();
print(x);
x = 5;
}
void deferLockShared() {
RelockableMutexLock scope(&mu, DeferTraits{});
print(x); // expected-warning {{reading variable 'x' requires holding mutex 'mu'}}
scope.ReaderLock();
print(x);
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
void doubleUnlock() {
RelockableExclusiveMutexLock scope(&mu);
scope.Unlock(); // expected-note{{mutex released here}}
scope.Unlock(); // expected-warning {{releasing mutex 'mu' that was not held}}
}
void doubleLock1() {
RelockableExclusiveMutexLock scope(&mu); // expected-note{{mutex acquired here}}
scope.Lock(); // expected-warning {{acquiring mutex 'mu' that is already held}}
}
void doubleLock2() {
RelockableExclusiveMutexLock scope(&mu);
scope.Unlock();
scope.Lock(); // expected-note{{mutex acquired here}}
scope.Lock(); // expected-warning {{acquiring mutex 'mu' that is already held}}
}
void lockJoin() {
RelockableMutexLock scope(&mu, DeferTraits{});
if (b)
scope.Lock();
// No warning on join point because the lock will be released by the scope object anyway.
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
void unlockJoin() {
RelockableMutexLock scope(&mu, DeferTraits{});
scope.Lock();
if (b)
scope.Unlock();
// No warning on join point because the lock will be released by the scope object anyway.
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
void loopAcquire() {
RelockableMutexLock scope(&mu, DeferTraits{});
for (unsigned i = 1; i < 10; ++i)
scope.Lock(); // We could catch this double lock with negative capabilities.
}
void loopRelease() {
RelockableMutexLock scope(&mu, ExclusiveTraits{}); // expected-note {{mutex acquired here}}
// We have to warn on this join point despite the lock being managed ...
for (unsigned i = 1; i < 10; ++i) { // expected-warning {{expecting mutex 'mu' to be held at start of each loop}}
x = 1; // ... because we might miss that this doesn't always happen under lock.
if (i == 5)
scope.Unlock();
}
}
void loopPromote() {
RelockableMutexLock scope(&mu, SharedTraits{});
for (unsigned i = 1; i < 10; ++i) {
x = 1; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
if (i == 5)
scope.PromoteShared();
}
}
void loopDemote() {
RelockableMutexLock scope(&mu, ExclusiveTraits{}); // expected-note {{the other acquisition of mutex 'mu' is here}}
// We have to warn on this join point despite the lock being managed ...
for (unsigned i = 1; i < 10; ++i) {
x = 1; // ... because we might miss that this doesn't always happen under exclusive lock.
if (i == 5)
scope.DemoteExclusive(); // expected-warning {{mutex 'mu' is acquired exclusively and shared in the same scope}}
}
}
void loopAcquireContinue() {
RelockableMutexLock scope(&mu, DeferTraits{});
for (unsigned i = 1; i < 10; ++i) {
x = 1; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
if (i == 5) {
scope.Lock();
continue;
}
}
}
void loopReleaseContinue() {
RelockableMutexLock scope(&mu, ExclusiveTraits{}); // expected-note {{mutex acquired here}}
// We have to warn on this join point despite the lock being managed ...
for (unsigned i = 1; i < 10; ++i) { // expected-warning {{expecting mutex 'mu' to be held at start of each loop}}
x = 1; // ... because we might miss that this doesn't always happen under lock.
if (i == 5) {
scope.Unlock();
continue;
}
}
}
void loopPromoteContinue() {
RelockableMutexLock scope(&mu, SharedTraits{});
for (unsigned i = 1; i < 10; ++i) {
x = 1; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
if (i == 5) {
scope.PromoteShared();
continue;
}
}
}
void loopDemoteContinue() {
RelockableMutexLock scope(&mu, ExclusiveTraits{}); // expected-note {{the other acquisition of mutex 'mu' is here}}
// We have to warn on this join point despite the lock being managed ...
for (unsigned i = 1; i < 10; ++i) {
x = 1; // ... because we might miss that this doesn't always happen under exclusive lock.
if (i == 5) {
scope.DemoteExclusive(); // expected-warning {{mutex 'mu' is acquired exclusively and shared in the same scope}}
continue;
}
}
}
void exclusiveSharedJoin() {
RelockableMutexLock scope(&mu, DeferTraits{});
if (b)
scope.Lock();
else
scope.ReaderLock();
// No warning on join point because the lock will be released by the scope object anyway.
print(x);
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
void sharedExclusiveJoin() {
RelockableMutexLock scope(&mu, DeferTraits{});
if (b)
scope.ReaderLock();
else
scope.Lock();
// No warning on join point because the lock will be released by the scope object anyway.
print(x);
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
void assertJoin() {
RelockableMutexLock scope(&mu, DeferTraits{});
if (b)
scope.Lock();
else
mu.AssertHeld();
x = 2;
}
void assertSharedJoin() {
RelockableMutexLock scope(&mu, DeferTraits{});
if (b)
scope.ReaderLock();
else
mu.AssertReaderHeld();
print(x);
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
void assertStrongerJoin() {
RelockableMutexLock scope(&mu, DeferTraits{});
if (b)
scope.ReaderLock();
else
mu.AssertHeld();
print(x);
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
void assertWeakerJoin() {
RelockableMutexLock scope(&mu, DeferTraits{});
if (b)
scope.Lock();
else
mu.AssertReaderHeld();
print(x);
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
void directUnlock() {
RelockableExclusiveMutexLock scope(&mu);
mu.Unlock();
// Debatable that there is no warning. Currently we don't track in the scoped
// object whether it is active, but just check if the contained locks can be
// reacquired. Here they can, because mu has been unlocked manually.
scope.Lock();
}
void directRelock() {
RelockableExclusiveMutexLock scope(&mu);
scope.Unlock();
mu.Lock();
// Similarly debatable that there is no warning.
scope.Unlock();
}
// Doesn't make a lot of sense, just making sure there is no crash.
void destructLock() {
RelockableExclusiveMutexLock scope(&mu);
scope.~RelockableExclusiveMutexLock();
scope.Lock(); // Should be UB, so we don't really care.
}
class SCOPED_LOCKABLE MemberLock {
public:
MemberLock() EXCLUSIVE_LOCK_FUNCTION(mutex);
~MemberLock() UNLOCK_FUNCTION(mutex);
void Lock() EXCLUSIVE_LOCK_FUNCTION(mutex);
Mutex mutex;
};
void relockShared2() {
MemberLock lock; // expected-note{{mutex acquired here}}
lock.Lock(); // expected-warning {{acquiring mutex 'lock.mutex' that is already held}}
}
class SCOPED_LOCKABLE WeirdScope {
private:
Mutex *other;
public:
WeirdScope(Mutex *mutex) EXCLUSIVE_LOCK_FUNCTION(mutex);
void unlock() EXCLUSIVE_UNLOCK_FUNCTION() EXCLUSIVE_UNLOCK_FUNCTION(other);
void lock() EXCLUSIVE_LOCK_FUNCTION() EXCLUSIVE_LOCK_FUNCTION(other);
~WeirdScope() EXCLUSIVE_UNLOCK_FUNCTION();
void requireOther() EXCLUSIVE_LOCKS_REQUIRED(other);
};
void relockWeird() {
WeirdScope scope(&mu);
x = 1;
scope.unlock(); // expected-warning {{releasing mutex 'scope.other' that was not held}}
x = 2; // \
// expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
scope.requireOther(); // \
// expected-warning {{calling function 'requireOther' requires holding mutex 'scope.other' exclusively}}
scope.lock(); // expected-note {{mutex acquired here}}
x = 3;
scope.requireOther();
} // expected-warning {{mutex 'scope.other' is still held at the end of function}}
} // end namespace RelockableScopedLock
namespace ScopedUnlock {
class SCOPED_LOCKABLE MutexUnlock {
public:
MutexUnlock(Mutex *mu) EXCLUSIVE_UNLOCK_FUNCTION(mu);
~MutexUnlock() EXCLUSIVE_UNLOCK_FUNCTION();
void Lock() EXCLUSIVE_UNLOCK_FUNCTION();
void Unlock() EXCLUSIVE_LOCK_FUNCTION();
};
class SCOPED_LOCKABLE ReaderMutexUnlock {
public:
ReaderMutexUnlock(Mutex *mu) SHARED_UNLOCK_FUNCTION(mu);
~ReaderMutexUnlock() EXCLUSIVE_UNLOCK_FUNCTION();
void Lock() EXCLUSIVE_UNLOCK_FUNCTION();
void Unlock() EXCLUSIVE_LOCK_FUNCTION();
};
Mutex mu;
int x GUARDED_BY(mu);
bool c;
void print(int);
void simple() EXCLUSIVE_LOCKS_REQUIRED(mu) {
x = 1;
MutexUnlock scope(&mu);
x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
void simpleShared() SHARED_LOCKS_REQUIRED(mu) {
print(x);
ReaderMutexUnlock scope(&mu);
print(x); // expected-warning {{reading variable 'x' requires holding mutex 'mu'}}
}
void innerUnlock() {
MutexLock outer(&mu);
if (x == 0) {
MutexUnlock inner(&mu);
x = 1; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
x = 2;
}
void innerUnlockShared() {
ReaderMutexLock outer(&mu);
if (x == 0) {
ReaderMutexUnlock inner(&mu);
print(x); // expected-warning {{reading variable 'x' requires holding mutex 'mu'}}
}
print(x);
}
void manual() EXCLUSIVE_LOCKS_REQUIRED(mu) {
MutexUnlock scope(&mu);
scope.Lock();
x = 2;
scope.Unlock();
x = 3; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
void join() EXCLUSIVE_LOCKS_REQUIRED(mu) {
MutexUnlock scope(&mu);
if (c)
scope.Lock();
// No warning on join point because the lock will be released by the scope object anyway.
scope.Lock();
}
void doubleLock() EXCLUSIVE_LOCKS_REQUIRED(mu) {
MutexUnlock scope(&mu);
scope.Lock(); // expected-note{{mutex acquired here}}
scope.Lock(); // expected-warning {{acquiring mutex 'mu' that is already held}}
}
void doubleUnlock() EXCLUSIVE_LOCKS_REQUIRED(mu) {
MutexUnlock scope(&mu); // expected-note{{mutex released here}}
scope.Unlock(); // expected-warning {{releasing mutex 'mu' that was not held}}
}
class SCOPED_LOCKABLE MutexLockUnlock {
public:
MutexLockUnlock(Mutex *mu1, Mutex *mu2) EXCLUSIVE_UNLOCK_FUNCTION(mu1) EXCLUSIVE_LOCK_FUNCTION(mu2);
~MutexLockUnlock() EXCLUSIVE_UNLOCK_FUNCTION();
void Release() EXCLUSIVE_UNLOCK_FUNCTION();
void Acquire() EXCLUSIVE_LOCK_FUNCTION();
};
Mutex other;
void fn() EXCLUSIVE_LOCKS_REQUIRED(other);
void lockUnlock() EXCLUSIVE_LOCKS_REQUIRED(mu) {
MutexLockUnlock scope(&mu, &other);
fn();
x = 1; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
}
} // end namespace ScopedUnlock
namespace TrylockFunctionTest {
class Foo {
public:
Mutex mu1_;
Mutex mu2_;
bool c;
bool lockBoth() EXCLUSIVE_TRYLOCK_FUNCTION(true, mu1_, mu2_);
};
bool Foo::lockBoth() {
if (!mu1_.TryLock())
return false;
mu2_.Lock();
if (!c) {
mu1_.Unlock();
mu2_.Unlock();
return false;
}
return true;
}
} // end namespace TrylockFunctionTest
namespace DoubleLockBug {
class Foo {
public:
Mutex mu_;
int a GUARDED_BY(mu_);
void foo1() EXCLUSIVE_LOCKS_REQUIRED(mu_);
int foo2() SHARED_LOCKS_REQUIRED(mu_);
};
void Foo::foo1() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
a = 0;
}
int Foo::foo2() SHARED_LOCKS_REQUIRED(mu_) {
return a;
}
}
namespace UnlockBug {
class Foo {
public:
Mutex mutex_;
void foo1() EXCLUSIVE_LOCKS_REQUIRED(mutex_) { // expected-note {{mutex acquired here}}
mutex_.Unlock();
} // expected-warning {{expecting mutex 'mutex_' to be held at the end of function}}
void foo2() SHARED_LOCKS_REQUIRED(mutex_) { // expected-note {{mutex acquired here}}
mutex_.Unlock();
} // expected-warning {{expecting mutex 'mutex_' to be held at the end of function}}
};
} // end namespace UnlockBug
namespace FoolishScopedLockableBug {
class SCOPED_LOCKABLE WTF_ScopedLockable {
public:
WTF_ScopedLockable(Mutex* mu) EXCLUSIVE_LOCK_FUNCTION(mu);
// have to call release() manually;
~WTF_ScopedLockable();
void release() UNLOCK_FUNCTION();
};
class Foo {
Mutex mu_;
int a GUARDED_BY(mu_);
bool c;
void doSomething();
void test1() {
WTF_ScopedLockable wtf(&mu_);
wtf.release();
}
void test2() {
WTF_ScopedLockable wtf(&mu_); // expected-note {{mutex acquired here}}
} // expected-warning {{mutex 'mu_' is still held at the end of function}}
void test3() {
if (c) {
WTF_ScopedLockable wtf(&mu_);
wtf.release();
}
}
void test4() {
if (c) {
doSomething();
}
else {
WTF_ScopedLockable wtf(&mu_);
wtf.release();
}
}
void test5() {
if (c) {
WTF_ScopedLockable wtf(&mu_); // expected-note {{mutex acquired here}}
}
} // expected-warning {{mutex 'mu_' is not held on every path through here}}
void test6() {
if (c) {
doSomething();
}
else {
WTF_ScopedLockable wtf(&mu_); // expected-note {{mutex acquired here}}
}
} // expected-warning {{mutex 'mu_' is not held on every path through here}}
};
} // end namespace FoolishScopedLockableBug
namespace TemporaryCleanupExpr {
class Foo {
int a GUARDED_BY(getMutexPtr().get());
SmartPtr<Mutex> getMutexPtr();
void test();
};
void Foo::test() {
{
ReaderMutexLock lock(getMutexPtr().get());
int b = a;
}
int b = a; // expected-warning {{reading variable 'a' requires holding mutex 'getMutexPtr()'}}
}
#ifdef __cpp_guaranteed_copy_elision
void guaranteed_copy_elision() {
MutexLock lock = MutexLock{&sls_mu};
sls_guard_var = 0;
}
void guaranteed_copy_elision_const() {
const MutexLock lock = MutexLock{&sls_mu};
sls_guard_var = 0;
}
#endif
} // end namespace TemporaryCleanupExpr
namespace SmartPointerTests {
class Foo {
public:
SmartPtr<Mutex> mu_;
int a GUARDED_BY(mu_);
int b GUARDED_BY(mu_.get());
int c GUARDED_BY(*mu_);
void Lock() EXCLUSIVE_LOCK_FUNCTION(mu_);
void Unlock() UNLOCK_FUNCTION(mu_);
void test0();
void test1();
void test2();
void test3();
void test4();
void test5();
void test6();
void test7();
void test8();
};
void Foo::test0() {
a = 0; // expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
b = 0; // expected-warning {{writing variable 'b' requires holding mutex 'mu_' exclusively}}
c = 0; // expected-warning {{writing variable 'c' requires holding mutex 'mu_' exclusively}}
}
void Foo::test1() {
mu_->Lock();
a = 0;
b = 0;
c = 0;
mu_->Unlock();
}
void Foo::test2() {
(*mu_).Lock();
a = 0;
b = 0;
c = 0;
(*mu_).Unlock();
}
void Foo::test3() {
mu_.get()->Lock();
a = 0;
b = 0;
c = 0;
mu_.get()->Unlock();
}
void Foo::test4() {
MutexLock lock(mu_.get());
a = 0;
b = 0;
c = 0;
}
void Foo::test5() {
MutexLock lock(&(*mu_));
a = 0;
b = 0;
c = 0;
}
void Foo::test6() {
Lock();
a = 0;
b = 0;
c = 0;
Unlock();
}
void Foo::test7() {
{
Lock();
mu_->Unlock();
}
{
mu_->Lock();
Unlock();
}
{
mu_.get()->Lock();
mu_->Unlock();
}
{
mu_->Lock();
mu_.get()->Unlock();
}
{
mu_.get()->Lock();
(*mu_).Unlock();
}
{
(*mu_).Lock();
mu_->Unlock();
}
}
void Foo::test8() {
mu_->Lock(); // expected-note 2 {{mutex acquired here}}
mu_.get()->Lock(); // expected-warning {{acquiring mutex 'mu_' that is already held}}
(*mu_).Lock(); // expected-warning {{acquiring mutex 'mu_' that is already held}}
mu_.get()->Unlock(); // expected-note {{mutex released here}}
Unlock(); // expected-warning {{releasing mutex 'mu_' that was not held}}
}
class Bar {
SmartPtr<Foo> foo;
void test0();
void test1();
void test2();
void test3();
};
void Bar::test0() {
foo->a = 0; // expected-warning {{writing variable 'a' requires holding mutex 'foo->mu_' exclusively}}
(*foo).b = 0; // expected-warning {{writing variable 'b' requires holding mutex 'foo->mu_' exclusively}}
foo.get()->c = 0; // expected-warning {{writing variable 'c' requires holding mutex 'foo->mu_' exclusively}}
}
void Bar::test1() {
foo->mu_->Lock();
foo->a = 0;
(*foo).b = 0;
foo.get()->c = 0;
foo->mu_->Unlock();
}
void Bar::test2() {
(*foo).mu_->Lock();
foo->a = 0;
(*foo).b = 0;
foo.get()->c = 0;
foo.get()->mu_->Unlock();
}
void Bar::test3() {
MutexLock lock(foo->mu_.get());
foo->a = 0;
(*foo).b = 0;
foo.get()->c = 0;
}
} // end namespace SmartPointerTests
namespace DuplicateAttributeTest {
class LOCKABLE Foo {
public:
Mutex mu1_;
Mutex mu2_;
Mutex mu3_;
int a GUARDED_BY(mu1_);
int b GUARDED_BY(mu2_);
int c GUARDED_BY(mu3_);
void lock() EXCLUSIVE_LOCK_FUNCTION();
void unlock() UNLOCK_FUNCTION();
void lock1() EXCLUSIVE_LOCK_FUNCTION(mu1_);
void slock1() SHARED_LOCK_FUNCTION(mu1_);
void lock3() EXCLUSIVE_LOCK_FUNCTION(mu1_, mu2_, mu3_);
void locklots()
EXCLUSIVE_LOCK_FUNCTION(mu1_)
EXCLUSIVE_LOCK_FUNCTION(mu2_)
EXCLUSIVE_LOCK_FUNCTION(mu1_, mu2_, mu3_);
void unlock1() UNLOCK_FUNCTION(mu1_);
void unlock3() UNLOCK_FUNCTION(mu1_, mu2_, mu3_);
void unlocklots()
UNLOCK_FUNCTION(mu1_)
UNLOCK_FUNCTION(mu2_)
UNLOCK_FUNCTION(mu1_, mu2_, mu3_);
};
void Foo::lock() EXCLUSIVE_LOCK_FUNCTION() { }
void Foo::unlock() UNLOCK_FUNCTION() { }
void Foo::lock1() EXCLUSIVE_LOCK_FUNCTION(mu1_) {
mu1_.Lock();
}
void Foo::slock1() SHARED_LOCK_FUNCTION(mu1_) {
mu1_.ReaderLock();
}
void Foo::lock3() EXCLUSIVE_LOCK_FUNCTION(mu1_, mu2_, mu3_) {
mu1_.Lock();
mu2_.Lock();
mu3_.Lock();
}
void Foo::locklots()
EXCLUSIVE_LOCK_FUNCTION(mu1_, mu2_)
EXCLUSIVE_LOCK_FUNCTION(mu2_, mu3_) {
mu1_.Lock();
mu2_.Lock();
mu3_.Lock();
}
void Foo::unlock1() UNLOCK_FUNCTION(mu1_) {
mu1_.Unlock();
}
void Foo::unlock3() UNLOCK_FUNCTION(mu1_, mu2_, mu3_) {
mu1_.Unlock();
mu2_.Unlock();
mu3_.Unlock();
}
void Foo::unlocklots()
UNLOCK_FUNCTION(mu1_, mu2_)
UNLOCK_FUNCTION(mu2_, mu3_) {
mu1_.Unlock();
mu2_.Unlock();
mu3_.Unlock();
}
void test0() {
Foo foo;
foo.lock();
foo.unlock();
foo.lock(); // expected-note{{mutex acquired here}}
foo.lock(); // expected-warning {{acquiring mutex 'foo' that is already held}}
foo.unlock(); // expected-note{{mutex released here}}
foo.unlock(); // expected-warning {{releasing mutex 'foo' that was not held}}
}
void test1() {
Foo foo;
foo.lock1();
foo.a = 0;
foo.unlock1();
foo.lock1(); // expected-note{{mutex acquired here}}
foo.lock1(); // expected-warning {{acquiring mutex 'foo.mu1_' that is already held}}
foo.a = 0;
foo.unlock1(); // expected-note{{mutex released here}}
foo.unlock1(); // expected-warning {{releasing mutex 'foo.mu1_' that was not held}}
}
int test2() {
Foo foo;
foo.slock1();
int d1 = foo.a;
foo.unlock1();
foo.slock1(); // expected-note{{mutex acquired here}}
foo.slock1(); // expected-warning {{acquiring mutex 'foo.mu1_' that is already held}}
int d2 = foo.a;
foo.unlock1(); // expected-note{{mutex released here}}
foo.unlock1(); // expected-warning {{releasing mutex 'foo.mu1_' that was not held}}
return d1 + d2;
}
void test3() {
Foo foo;
foo.lock3();
foo.a = 0;
foo.b = 0;
foo.c = 0;
foo.unlock3();
foo.lock3(); // expected-note 3 {{mutex acquired here}}
foo.lock3(); // \
// expected-warning {{acquiring mutex 'foo.mu1_' that is already held}} \
// expected-warning {{acquiring mutex 'foo.mu2_' that is already held}} \
// expected-warning {{acquiring mutex 'foo.mu3_' that is already held}}
foo.a = 0;
foo.b = 0;
foo.c = 0;
foo.unlock3(); // expected-note 3 {{mutex released here}}
foo.unlock3(); // \
// expected-warning {{releasing mutex 'foo.mu1_' that was not held}} \
// expected-warning {{releasing mutex 'foo.mu2_' that was not held}} \
// expected-warning {{releasing mutex 'foo.mu3_' that was not held}}
}
void testlots() {
Foo foo;
foo.locklots();
foo.a = 0;
foo.b = 0;
foo.c = 0;
foo.unlocklots();
foo.locklots(); // expected-note 3 {{mutex acquired here}}
foo.locklots(); // \
// expected-warning {{acquiring mutex 'foo.mu1_' that is already held}} \
// expected-warning {{acquiring mutex 'foo.mu2_' that is already held}} \
// expected-warning {{acquiring mutex 'foo.mu3_' that is already held}}
foo.a = 0;
foo.b = 0;
foo.c = 0;
foo.unlocklots(); // expected-note 3 {{mutex released here}}
foo.unlocklots(); // \
// expected-warning {{releasing mutex 'foo.mu1_' that was not held}} \
// expected-warning {{releasing mutex 'foo.mu2_' that was not held}} \
// expected-warning {{releasing mutex 'foo.mu3_' that was not held}}
}
} // end namespace DuplicateAttributeTest
namespace TryLockEqTest {
class Foo {
Mutex mu_;
int a GUARDED_BY(mu_);
bool c;
int tryLockMutexI() EXCLUSIVE_TRYLOCK_FUNCTION(1, mu_);
Mutex* tryLockMutexP() EXCLUSIVE_TRYLOCK_FUNCTION(1, mu_);
void unlock() UNLOCK_FUNCTION(mu_);
void test1();
void test2();
};
void Foo::test1() {
if (tryLockMutexP() == 0) {
a = 0; // expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
return;
}
a = 0;
unlock();
if (tryLockMutexP() != 0) {
a = 0;
unlock();
}
if (0 != tryLockMutexP()) {
a = 0;
unlock();
}
if (!(tryLockMutexP() == 0)) {
a = 0;
unlock();
}
if (tryLockMutexI() == 0) {
a = 0; // expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
return;
}
a = 0;
unlock();
if (0 == tryLockMutexI()) {
a = 0; // expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
return;
}
a = 0;
unlock();
if (tryLockMutexI() == 1) {
a = 0;
unlock();
}
if (mu_.TryLock() == false) {
a = 0; // expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
return;
}
a = 0;
unlock();
if (mu_.TryLock() == true) {
a = 0;
unlock();
}
else {
a = 0; // expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
}
#if __has_feature(cxx_nullptr)
if (tryLockMutexP() == nullptr) {
a = 0; // expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
return;
}
a = 0;
unlock();
#endif
}
} // end namespace TryLockEqTest
namespace ExistentialPatternMatching {
class Graph {
public:
Mutex mu_;
};
void LockAllGraphs() EXCLUSIVE_LOCK_FUNCTION(&Graph::mu_);
void UnlockAllGraphs() UNLOCK_FUNCTION(&Graph::mu_);
class Node {
public:
int a GUARDED_BY(&Graph::mu_);
void foo() EXCLUSIVE_LOCKS_REQUIRED(&Graph::mu_) {
a = 0;
}
void foo2() LOCKS_EXCLUDED(&Graph::mu_);
};
void test() {
Graph g1;
Graph g2;
Node n1;
n1.a = 0; // expected-warning {{writing variable 'a' requires holding mutex '&ExistentialPatternMatching::Graph::mu_' exclusively}}
n1.foo(); // expected-warning {{calling function 'foo' requires holding mutex '&ExistentialPatternMatching::Graph::mu_' exclusively}}
n1.foo2();
g1.mu_.Lock();
n1.a = 0;
n1.foo();
n1.foo2(); // expected-warning {{cannot call function 'foo2' while mutex '&ExistentialPatternMatching::Graph::mu_' is held}}
g1.mu_.Unlock();
g2.mu_.Lock();
n1.a = 0;
n1.foo();
n1.foo2(); // expected-warning {{cannot call function 'foo2' while mutex '&ExistentialPatternMatching::Graph::mu_' is held}}
g2.mu_.Unlock();
LockAllGraphs();
n1.a = 0;
n1.foo();
n1.foo2(); // expected-warning {{cannot call function 'foo2' while mutex '&ExistentialPatternMatching::Graph::mu_' is held}}
UnlockAllGraphs();
LockAllGraphs();
g1.mu_.Unlock();
LockAllGraphs();
g2.mu_.Unlock();
LockAllGraphs(); // expected-note{{mutex acquired here}}
g1.mu_.Lock(); // expected-warning {{acquiring mutex 'g1.mu_' that is already held}}
g1.mu_.Unlock();
}
} // end namespace ExistentialPatternMatching
namespace StringIgnoreTest {
class Foo {
public:
Mutex mu_;
void lock() EXCLUSIVE_LOCK_FUNCTION("");
void unlock() UNLOCK_FUNCTION("");
void goober() EXCLUSIVE_LOCKS_REQUIRED("");
void roober() SHARED_LOCKS_REQUIRED("");
};
class Bar : public Foo {
public:
void bar(Foo* f) {
f->unlock();
f->goober();
f->roober();
f->lock();
};
};
} // end namespace StringIgnoreTest
namespace LockReturnedScopeFix {
class Base {
protected:
struct Inner;
bool c;
const Mutex& getLock(const Inner* i);
void lockInner (Inner* i) EXCLUSIVE_LOCK_FUNCTION(getLock(i));
void unlockInner(Inner* i) UNLOCK_FUNCTION(getLock(i));
void foo(Inner* i) EXCLUSIVE_LOCKS_REQUIRED(getLock(i));
void bar(Inner* i);
};
struct Base::Inner {
Mutex lock_;
void doSomething() EXCLUSIVE_LOCKS_REQUIRED(lock_);
};
const Mutex& Base::getLock(const Inner* i) LOCK_RETURNED(i->lock_) {
return i->lock_;
}
void Base::foo(Inner* i) {
i->doSomething();
}
void Base::bar(Inner* i) {
if (c) {
i->lock_.Lock();
unlockInner(i);
}
else {
lockInner(i);
i->lock_.Unlock();
}
}
} // end namespace LockReturnedScopeFix
namespace TrylockWithCleanups {
struct Foo {
Mutex mu_;
int a GUARDED_BY(mu_);
};
Foo* GetAndLockFoo(const MyString& s)
EXCLUSIVE_TRYLOCK_FUNCTION(true, &Foo::mu_);
static void test() {
Foo* lt = GetAndLockFoo("foo");
if (!lt) return;
int a = lt->a;
lt->mu_.Unlock();
}
} // end namespace TrylockWithCleanups
namespace UniversalLock {
class Foo {
Mutex mu_;
bool c;
int a GUARDED_BY(mu_);
void r_foo() SHARED_LOCKS_REQUIRED(mu_);
void w_foo() EXCLUSIVE_LOCKS_REQUIRED(mu_);
void test1() {
int b;
beginNoWarnOnReads();
b = a;
r_foo();
endNoWarnOnReads();
beginNoWarnOnWrites();
a = 0;
w_foo();
endNoWarnOnWrites();
}
// don't warn on joins with universal lock
void test2() {
if (c) {
beginNoWarnOnWrites();
}
a = 0; // \
// expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
endNoWarnOnWrites(); // \
// expected-warning {{releasing wildcard '*' that was not held}}
}
// make sure the universal lock joins properly
void test3() {
if (c) {
mu_.Lock();
beginNoWarnOnWrites();
}
else {
beginNoWarnOnWrites();
mu_.Lock();
}
a = 0;
endNoWarnOnWrites();
mu_.Unlock();
}
// combine universal lock with other locks
void test4() {
beginNoWarnOnWrites();
mu_.Lock();
mu_.Unlock();
endNoWarnOnWrites();
mu_.Lock();
beginNoWarnOnWrites();
endNoWarnOnWrites();
mu_.Unlock();
mu_.Lock();
beginNoWarnOnWrites();
mu_.Unlock();
endNoWarnOnWrites();
}
};
} // end namespace UniversalLock
namespace TemplateLockReturned {
template<class T>
class BaseT {
public:
virtual void baseMethod() = 0;
Mutex* get_mutex() LOCK_RETURNED(mutex_) { return &mutex_; }
Mutex mutex_;
int a GUARDED_BY(mutex_);
};
class Derived : public BaseT<int> {
public:
void baseMethod() EXCLUSIVE_LOCKS_REQUIRED(get_mutex()) {
a = 0;
}
};
} // end namespace TemplateLockReturned
namespace ExprMatchingBugFix {
class Foo {
public:
Mutex mu_;
};
class Bar {
public:
bool c;
Foo* foo;
Bar(Foo* f) : foo(f) { }
struct Nested {
Foo* foo;
Nested(Foo* f) : foo(f) { }
void unlockFoo() UNLOCK_FUNCTION(&Foo::mu_);
};
void test();
};
void Bar::test() {
foo->mu_.Lock();
if (c) {
Nested *n = new Nested(foo);
n->unlockFoo();
}
else {
foo->mu_.Unlock();
}
}
}; // end namespace ExprMatchingBugfix
namespace ComplexNameTest {
class Foo {
public:
static Mutex mu_;
Foo() EXCLUSIVE_LOCKS_REQUIRED(mu_) { }
~Foo() EXCLUSIVE_LOCKS_REQUIRED(mu_) { }
int operator[](int i) EXCLUSIVE_LOCKS_REQUIRED(mu_) { return 0; }
};
class Bar {
public:
static Mutex mu_;
Bar() LOCKS_EXCLUDED(mu_) { }
~Bar() LOCKS_EXCLUDED(mu_) { }
int operator[](int i) LOCKS_EXCLUDED(mu_) { return 0; }
};
void test1() {
Foo f; // expected-warning {{calling function 'Foo' requires holding mutex 'mu_' exclusively}}
int a = f[0]; // expected-warning {{calling function 'operator[]' requires holding mutex 'mu_' exclusively}}
} // expected-warning {{calling function '~Foo' requires holding mutex 'mu_' exclusively}}
void test2() {
Bar::mu_.Lock();
{
Bar b; // expected-warning {{cannot call function 'Bar' while mutex 'mu_' is held}}
int a = b[0]; // expected-warning {{cannot call function 'operator[]' while mutex 'mu_' is held}}
} // expected-warning {{cannot call function '~Bar' while mutex 'mu_' is held}}
Bar::mu_.Unlock();
}
}; // end namespace ComplexNameTest
namespace UnreachableExitTest {
class FemmeFatale {
public:
FemmeFatale();
~FemmeFatale() __attribute__((noreturn));
};
void exitNow() __attribute__((noreturn));
void exitDestruct(const MyString& ms) __attribute__((noreturn));
Mutex fatalmu_;
void test1() EXCLUSIVE_LOCKS_REQUIRED(fatalmu_) {
exitNow();
}
void test2() EXCLUSIVE_LOCKS_REQUIRED(fatalmu_) {
FemmeFatale femme;
}
bool c;
void test3() EXCLUSIVE_LOCKS_REQUIRED(fatalmu_) {
if (c) {
exitNow();
}
else {
FemmeFatale femme;
}
}
void test4() EXCLUSIVE_LOCKS_REQUIRED(fatalmu_) {
exitDestruct("foo");
}
} // end namespace UnreachableExitTest
namespace VirtualMethodCanonicalizationTest {
class Base {
public:
virtual Mutex* getMutex() = 0;
};
class Base2 : public Base {
public:
Mutex* getMutex();
};
class Base3 : public Base2 {
public:
Mutex* getMutex();
};
class Derived : public Base3 {
public:
Mutex* getMutex(); // overrides Base::getMutex()
};
void baseFun(Base *b) EXCLUSIVE_LOCKS_REQUIRED(b->getMutex()) { }
void derivedFun(Derived *d) EXCLUSIVE_LOCKS_REQUIRED(d->getMutex()) {
baseFun(d);
}
} // end namespace VirtualMethodCanonicalizationTest
namespace TemplateFunctionParamRemapTest {
template <class T>
struct Cell {
T dummy_;
Mutex* mu_;
};
class Foo {
public:
template <class T>
void elr(Cell<T>* c) EXCLUSIVE_LOCKS_REQUIRED(c->mu_);
void test();
};
template<class T>
void Foo::elr(Cell<T>* c1) { }
void Foo::test() {
Cell<int> cell;
elr(&cell); // \
// expected-warning {{calling function 'elr<int>' requires holding mutex 'cell.mu_' exclusively}}
}
template<class T>
void globalELR(Cell<T>* c) EXCLUSIVE_LOCKS_REQUIRED(c->mu_);
template<class T>
void globalELR(Cell<T>* c1) { }
void globalTest() {
Cell<int> cell;
globalELR(&cell); // \
// expected-warning {{calling function 'globalELR<int>' requires holding mutex 'cell.mu_' exclusively}}
}
template<class T>
void globalELR2(Cell<T>* c) EXCLUSIVE_LOCKS_REQUIRED(c->mu_);
// second declaration
template<class T>
void globalELR2(Cell<T>* c2);
template<class T>
void globalELR2(Cell<T>* c3) { }
// re-declaration after definition
template<class T>
void globalELR2(Cell<T>* c4);
void globalTest2() {
Cell<int> cell;
globalELR2(&cell); // \
// expected-warning {{calling function 'globalELR2<int>' requires holding mutex 'cell.mu_' exclusively}}
}
template<class T>
class FooT {
public:
void elr(Cell<T>* c) EXCLUSIVE_LOCKS_REQUIRED(c->mu_);
};
template<class T>
void FooT<T>::elr(Cell<T>* c1) { }
void testFooT() {
Cell<int> cell;
FooT<int> foo;
foo.elr(&cell); // \
// expected-warning {{calling function 'elr' requires holding mutex 'cell.mu_' exclusively}}
}
} // end namespace TemplateFunctionParamRemapTest
namespace SelfConstructorTest {
class SelfLock {
public:
SelfLock() EXCLUSIVE_LOCK_FUNCTION(mu_);
~SelfLock() UNLOCK_FUNCTION(mu_);
void foo() EXCLUSIVE_LOCKS_REQUIRED(mu_);
Mutex mu_;
};
class LOCKABLE SelfLock2 {
public:
SelfLock2() EXCLUSIVE_LOCK_FUNCTION();
~SelfLock2() UNLOCK_FUNCTION();
void foo() EXCLUSIVE_LOCKS_REQUIRED(this);
};
class SelfLockDeferred {
public:
SelfLockDeferred() LOCKS_EXCLUDED(mu_);
~SelfLockDeferred() UNLOCK_FUNCTION(mu_);
Mutex mu_;
};
class LOCKABLE SelfLockDeferred2 {
public:
SelfLockDeferred2() LOCKS_EXCLUDED(this);
~SelfLockDeferred2() UNLOCK_FUNCTION();
};
void test() {
SelfLock s;
s.foo();
}
void test2() {
SelfLock2 s2;
s2.foo();
}
void testDeferredTemporary() {
SelfLockDeferred(); // expected-warning {{releasing mutex '<temporary>.mu_' that was not held}}
}
void testDeferredTemporary2() {
SelfLockDeferred2(); // expected-warning {{releasing mutex '<temporary>' that was not held}}
}
} // end namespace SelfConstructorTest
namespace MultipleAttributeTest {
class Foo {
Mutex mu1_;
Mutex mu2_;
int a GUARDED_BY(mu1_);
int b GUARDED_BY(mu2_);
int c GUARDED_BY(mu1_) GUARDED_BY(mu2_);
int* d PT_GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_);
void foo1() EXCLUSIVE_LOCKS_REQUIRED(mu1_)
EXCLUSIVE_LOCKS_REQUIRED(mu2_);
void foo2() SHARED_LOCKS_REQUIRED(mu1_)
SHARED_LOCKS_REQUIRED(mu2_);
void foo3() LOCKS_EXCLUDED(mu1_)
LOCKS_EXCLUDED(mu2_);
void lock() EXCLUSIVE_LOCK_FUNCTION(mu1_)
EXCLUSIVE_LOCK_FUNCTION(mu2_);
void readerlock() SHARED_LOCK_FUNCTION(mu1_)
SHARED_LOCK_FUNCTION(mu2_);
void unlock() UNLOCK_FUNCTION(mu1_)
UNLOCK_FUNCTION(mu2_);
bool trylock() EXCLUSIVE_TRYLOCK_FUNCTION(true, mu1_)
EXCLUSIVE_TRYLOCK_FUNCTION(true, mu2_);
bool readertrylock() SHARED_TRYLOCK_FUNCTION(true, mu1_)
SHARED_TRYLOCK_FUNCTION(true, mu2_);
void assertBoth() ASSERT_EXCLUSIVE_LOCK(mu1_)
ASSERT_EXCLUSIVE_LOCK(mu2_);
void alsoAssertBoth() ASSERT_EXCLUSIVE_LOCK(mu1_, mu2_);
void assertShared() ASSERT_SHARED_LOCK(mu1_)
ASSERT_SHARED_LOCK(mu2_);
void alsoAssertShared() ASSERT_SHARED_LOCK(mu1_, mu2_);
void test();
void testAssert();
void testAssertShared();
};
void Foo::foo1() {
a = 1;
b = 2;
}
void Foo::foo2() {
int result = a + b;
}
void Foo::foo3() { }
void Foo::lock() { mu1_.Lock(); mu2_.Lock(); }
void Foo::readerlock() { mu1_.ReaderLock(); mu2_.ReaderLock(); }
void Foo::unlock() { mu1_.Unlock(); mu2_.Unlock(); }
bool Foo::trylock() { return true; }
bool Foo::readertrylock() { return true; }
void Foo::test() {
mu1_.Lock();
foo1(); // expected-warning {{}}
c = 0; // expected-warning {{}}
*d = 0; // expected-warning {{}}
mu1_.Unlock();
mu1_.ReaderLock();
foo2(); // expected-warning {{}}
int x = c; // expected-warning {{}}
int y = *d; // expected-warning {{}}
mu1_.Unlock();
mu2_.Lock();
foo3(); // expected-warning {{}}
mu2_.Unlock();
lock();
a = 0;
b = 0;
unlock();
readerlock();
int z = a + b;
unlock();
if (trylock()) {
a = 0;
b = 0;
unlock();
}
if (readertrylock()) {
int zz = a + b;
unlock();
}
}
// Force duplication of attributes
void Foo::assertBoth() { }
void Foo::alsoAssertBoth() { }
void Foo::assertShared() { }
void Foo::alsoAssertShared() { }
void Foo::testAssert() {
{
assertBoth();
a = 0;
b = 0;
}
{
alsoAssertBoth();
a = 0;
b = 0;
}
}
void Foo::testAssertShared() {
{
assertShared();
int zz = a + b;
}
{
alsoAssertShared();
int zz = a + b;
}
}
} // end namespace MultipleAttributeTest
namespace GuardedNonPrimitiveTypeTest {
class Data {
public:
Data(int i) : dat(i) { }
int getValue() const { return dat; }
void setValue(int i) { dat = i; }
int operator[](int i) const { return dat; }
int& operator[](int i) { return dat; }
void operator()() { }
Data& operator+=(int);
Data& operator-=(int);
Data& operator*=(int);
Data& operator/=(int);
Data& operator%=(int);
Data& operator^=(int);
Data& operator&=(int);
Data& operator|=(int);
Data& operator<<=(int);
Data& operator>>=(int);
Data& operator++();
Data& operator++(int);
Data& operator--();
Data& operator--(int);
private:
int dat;
};
class DataCell {
public:
DataCell(const Data& d) : dat(d) { }
private:
Data dat;
};
void showDataCell(const DataCell& dc);
class Foo {
public:
// method call tests
void test() {
data_.setValue(0); // FIXME -- should be writing \
// expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}}
int a = data_.getValue(); // \
// expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}}
datap1_->setValue(0); // FIXME -- should be writing \
// expected-warning {{reading variable 'datap1_' requires holding mutex 'mu_'}}
a = datap1_->getValue(); // \
// expected-warning {{reading variable 'datap1_' requires holding mutex 'mu_'}}
datap2_->setValue(0); // FIXME -- should be writing \
// expected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}}
a = datap2_->getValue(); // \
// expected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}}
(*datap2_).setValue(0); // FIXME -- should be writing \
// expected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}}
a = (*datap2_).getValue(); // \
// expected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}}
mu_.Lock();
data_.setValue(1);
datap1_->setValue(1);
datap2_->setValue(1);
mu_.Unlock();
mu_.ReaderLock();
a = data_.getValue();
datap1_->setValue(0); // reads datap1_, writes *datap1_
a = datap1_->getValue();
a = datap2_->getValue();
mu_.Unlock();
}
// operator tests
void test2() {
data_ = Data(1); // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
*datap1_ = data_; // expected-warning {{reading variable 'datap1_' requires holding mutex 'mu_'}} \
// expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}}
*datap2_ = data_; // expected-warning {{writing the value pointed to by 'datap2_' requires holding mutex 'mu_' exclusively}} \
// expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}}
data_ = *datap1_; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} \
// expected-warning {{reading variable 'datap1_' requires holding mutex 'mu_'}}
data_ = *datap2_; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} \
// expected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}}
data_ += 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
data_ -= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
data_ *= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
data_ /= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
data_ %= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
data_ ^= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
data_ &= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
data_ |= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
data_ <<= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
data_ >>= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
++data_; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
data_++; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
--data_; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
data_--; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}}
data_[0] = 0; // expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}}
(*datap2_)[0] = 0; // expected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}}
data_(); // expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}}
}
// const operator tests
void test3() const {
Data mydat(data_); // expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}}
//FIXME
//showDataCell(data_); // xpected-warning {{reading variable 'data_' requires holding mutex 'mu_'}}
//showDataCell(*datap2_); // xpected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}}
int a = data_[0]; // expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}}
}
private:
Mutex mu_;
Data data_ GUARDED_BY(mu_);
Data* datap1_ GUARDED_BY(mu_);
Data* datap2_ PT_GUARDED_BY(mu_);
};
} // end namespace GuardedNonPrimitiveTypeTest
namespace GuardedNonPrimitive_MemberAccess {
class Cell {
public:
Cell(int i);
void cellMethod();
int a;
};
class Foo {
public:
int a;
Cell c GUARDED_BY(cell_mu_);
Cell* cp PT_GUARDED_BY(cell_mu_);
void myMethod();
Mutex cell_mu_;
};
class Bar {
private:
Mutex mu_;
Foo foo GUARDED_BY(mu_);
Foo* foop PT_GUARDED_BY(mu_);
void test() {
foo.myMethod(); // expected-warning {{reading variable 'foo' requires holding mutex 'mu_'}}
int fa = foo.a; // expected-warning {{reading variable 'foo' requires holding mutex 'mu_'}}
foo.a = fa; // expected-warning {{writing variable 'foo' requires holding mutex 'mu_' exclusively}}
fa = foop->a; // expected-warning {{reading the value pointed to by 'foop' requires holding mutex 'mu_'}}
foop->a = fa; // expected-warning {{writing the value pointed to by 'foop' requires holding mutex 'mu_' exclusively}}
fa = (*foop).a; // expected-warning {{reading the value pointed to by 'foop' requires holding mutex 'mu_'}}
(*foop).a = fa; // expected-warning {{writing the value pointed to by 'foop' requires holding mutex 'mu_' exclusively}}
foo.c = Cell(0); // expected-warning {{writing variable 'foo' requires holding mutex 'mu_'}} \
// expected-warning {{writing variable 'c' requires holding mutex 'foo.cell_mu_' exclusively}}
foo.c.cellMethod(); // expected-warning {{reading variable 'foo' requires holding mutex 'mu_'}} \
// expected-warning {{reading variable 'c' requires holding mutex 'foo.cell_mu_'}}
foop->c = Cell(0); // expected-warning {{writing the value pointed to by 'foop' requires holding mutex 'mu_'}} \
// expected-warning {{writing variable 'c' requires holding mutex 'foop->cell_mu_' exclusively}}
foop->c.cellMethod(); // expected-warning {{reading the value pointed to by 'foop' requires holding mutex 'mu_'}} \
// expected-warning {{reading variable 'c' requires holding mutex 'foop->cell_mu_'}}
(*foop).c = Cell(0); // expected-warning {{writing the value pointed to by 'foop' requires holding mutex 'mu_'}} \
// expected-warning {{writing variable 'c' requires holding mutex 'foop->cell_mu_' exclusively}}
(*foop).c.cellMethod(); // expected-warning {{reading the value pointed to by 'foop' requires holding mutex 'mu_'}} \
// expected-warning {{reading variable 'c' requires holding mutex 'foop->cell_mu_'}}
};
};
} // namespace GuardedNonPrimitive_MemberAccess
namespace TestThrowExpr {
class Foo {
Mutex mu_;
bool hasError();
void test() {
mu_.Lock();
if (hasError()) {
throw "ugly";
}
mu_.Unlock();
}
};
} // end namespace TestThrowExpr
namespace UnevaluatedContextTest {
// parse attribute expressions in an unevaluated context.
static inline Mutex* getMutex1();
static inline Mutex* getMutex2();
void bar() EXCLUSIVE_LOCKS_REQUIRED(getMutex1());
void bar2() EXCLUSIVE_LOCKS_REQUIRED(getMutex1(), getMutex2());
} // end namespace UnevaluatedContextTest
namespace LockUnlockFunctionTest {
// Check built-in lock functions
class LOCKABLE MyLockable {
public:
void lock() EXCLUSIVE_LOCK_FUNCTION() { mu_.Lock(); }
void readerLock() SHARED_LOCK_FUNCTION() { mu_.ReaderLock(); }
void unlock() UNLOCK_FUNCTION() { mu_.Unlock(); }
private:
Mutex mu_;
};
class Foo {
public:
// Correct lock/unlock functions
void lock() EXCLUSIVE_LOCK_FUNCTION(mu_) {
mu_.Lock();
}
void readerLock() SHARED_LOCK_FUNCTION(mu_) {
mu_.ReaderLock();
}
void unlock() UNLOCK_FUNCTION(mu_) {
mu_.Unlock();
}
void unlockExclusive() EXCLUSIVE_UNLOCK_FUNCTION(mu_) {
mu_.Unlock();
}
void unlockShared() SHARED_UNLOCK_FUNCTION(mu_) {
mu_.ReaderUnlock();
}
// Check failure to lock.
void lockBad() EXCLUSIVE_LOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}}
mu2_.Lock();
mu2_.Unlock();
} // expected-warning {{expecting mutex 'mu_' to be held at the end of function}}
void readerLockBad() SHARED_LOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}}
mu2_.Lock();
mu2_.Unlock();
} // expected-warning {{expecting mutex 'mu_' to be held at the end of function}}
void unlockBad() UNLOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}}
mu2_.Lock();
mu2_.Unlock();
} // expected-warning {{mutex 'mu_' is still held at the end of function}}
// Check locking the wrong thing.
void lockBad2() EXCLUSIVE_LOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}}
mu2_.Lock(); // expected-note {{mutex acquired here}}
} // expected-warning {{expecting mutex 'mu_' to be held at the end of function}} \
// expected-warning {{mutex 'mu2_' is still held at the end of function}}
void readerLockBad2() SHARED_LOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}}
mu2_.ReaderLock(); // expected-note {{mutex acquired here}}
} // expected-warning {{expecting mutex 'mu_' to be held at the end of function}} \
// expected-warning {{mutex 'mu2_' is still held at the end of function}}
void unlockBad2() UNLOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}}
mu2_.Unlock(); // expected-warning {{releasing mutex 'mu2_' that was not held}}
} // expected-warning {{mutex 'mu_' is still held at the end of function}}
private:
Mutex mu_;
Mutex mu2_;
};
} // end namespace LockUnlockFunctionTest
namespace AssertHeldTest {
class Foo {
public:
int c;
int a GUARDED_BY(mu_);
Mutex mu_;
void test1() {
mu_.AssertHeld();
int b = a;
a = 0;
}
void test2() {
mu_.AssertReaderHeld();
int b = a;
a = 0; // expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
}
void test3() {
if (c) {
mu_.AssertHeld();
}
else {
mu_.AssertHeld();
}
int b = a;
a = 0;
}
void test4() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
mu_.AssertHeld();
int b = a;
a = 0;
}
void test5() UNLOCK_FUNCTION(mu_) {
mu_.AssertHeld();
mu_.Unlock();
}
void test6() {
mu_.AssertHeld();
mu_.Unlock(); // should this be a warning?
}
void test7() {
if (c) {
mu_.AssertHeld();
}
else {
mu_.Lock();
}
int b = a;
a = 0;
mu_.Unlock();
}
void test8() {
if (c) {
mu_.Lock();
}
else {
mu_.AssertHeld();
}
// FIXME: should warn, because it's unclear whether we need to release or not.
int b = a;
a = 0;
mu_.Unlock(); // should this be a warning?
}
void test9() {
if (c) {
mu_.AssertHeld();
}
else {
mu_.Lock(); // expected-note {{mutex acquired here}}
}
} // expected-warning {{mutex 'mu_' is still held at the end of function}}
void test10() {
if (c) {
mu_.Lock(); // expected-note {{mutex acquired here}}
}
else {
mu_.AssertHeld();
}
} // expected-warning {{mutex 'mu_' is still held at the end of function}}
void assertMu() ASSERT_EXCLUSIVE_LOCK(mu_);
void test11() {
assertMu();
int b = a;
a = 0;
}
void test12() {
if (c)
mu_.ReaderLock(); // expected-warning {{mutex 'mu_' is acquired exclusively and shared in the same scope}}
else
mu_.AssertHeld(); // expected-note {{the other acquisition of mutex 'mu_' is here}}
// FIXME: should instead warn because it's unclear whether we need to release or not.
int b = a;
a = 0;
mu_.Unlock();
}
void test13() {
if (c)
mu_.Lock(); // expected-warning {{mutex 'mu_' is acquired exclusively and shared in the same scope}}
else
mu_.AssertReaderHeld(); // expected-note {{the other acquisition of mutex 'mu_' is here}}
// FIXME: should instead warn because it's unclear whether we need to release or not.
int b = a;
a = 0;
mu_.Unlock();
}
};
} // end namespace AssertHeldTest
namespace LogicalConditionalTryLock {
class Foo {
public:
Mutex mu;
int a GUARDED_BY(mu);
bool c;
bool newc();
void test1() {
if (c && mu.TryLock()) {
a = 0;
mu.Unlock();
}
}
void test2() {
bool b = mu.TryLock();
if (c && b) {
a = 0;
mu.Unlock();
}
}
void test3() {
if (c || !mu.TryLock())
return;
a = 0;
mu.Unlock();
}
void test4() {
while (c && mu.TryLock()) {
a = 0;
c = newc();
mu.Unlock();
}
}
void test5() {
while (c) {
if (newc() || !mu.TryLock())
break;
a = 0;
mu.Unlock();
}
}
void test6() {
mu.Lock();
do {
a = 0;
mu.Unlock();
} while (newc() && mu.TryLock());
}
void test7() {
for (bool b = mu.TryLock(); c && b;) {
a = 0;
mu.Unlock();
}
}
void test8() {
if (c && newc() && mu.TryLock()) {
a = 0;
mu.Unlock();
}
}
void test9() {
if (!(c && newc() && mu.TryLock()))
return;
a = 0;
mu.Unlock();
}
void test10() {
if (!(c || !mu.TryLock())) {
a = 0;
mu.Unlock();
}
}
};
} // end namespace LogicalConditionalTryLock
namespace PtGuardedByTest {
void doSomething();
class Cell {
public:
int a;
};
// This mainly duplicates earlier tests, but just to make sure...
class PtGuardedByCorrectnessTest {
Mutex mu1;
Mutex mu2;
int* a GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
Cell* c GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
int sa[10] GUARDED_BY(mu1);
Cell sc[10] GUARDED_BY(mu1);
static constexpr int Cell::*pa = &Cell::a;
void test1() {
mu1.Lock();
if (a == 0) doSomething(); // OK, we don't dereference.
a = 0;
c = 0;
if (sa[0] == 42) doSomething();
sa[0] = 57;
if (sc[0].a == 42) doSomething();
sc[0].a = 57;
mu1.Unlock();
}
void test2() {
mu1.ReaderLock();
if (*a == 0) doSomething(); // expected-warning {{reading the value pointed to by 'a' requires holding mutex 'mu2'}}
*a = 0; // expected-warning {{writing the value pointed to by 'a' requires holding mutex 'mu2' exclusively}}
if (c->a == 0) doSomething(); // expected-warning {{reading the value pointed to by 'c' requires holding mutex 'mu2'}}
c->a = 0; // expected-warning {{writing the value pointed to by 'c' requires holding mutex 'mu2' exclusively}}
c->*pa = 0; // expected-warning {{writing the value pointed to by 'c' requires holding mutex 'mu2' exclusively}}
if ((*c).a == 0) doSomething(); // expected-warning {{reading the value pointed to by 'c' requires holding mutex 'mu2'}}
(*c).a = 0; // expected-warning {{writing the value pointed to by 'c' requires holding mutex 'mu2' exclusively}}
(*c).*pa = 0; // expected-warning {{writing the value pointed to by 'c' requires holding mutex 'mu2' exclusively}}
if (a[0] == 42) doSomething(); // expected-warning {{reading the value pointed to by 'a' requires holding mutex 'mu2'}}
a[0] = 57; // expected-warning {{writing the value pointed to by 'a' requires holding mutex 'mu2' exclusively}}
if (c[0].a == 42) doSomething(); // expected-warning {{reading the value pointed to by 'c' requires holding mutex 'mu2'}}
c[0].a = 57; // expected-warning {{writing the value pointed to by 'c' requires holding mutex 'mu2' exclusively}}
mu1.Unlock();
}
void test3() {
mu2.Lock();
if (*a == 0) doSomething(); // expected-warning {{reading variable 'a' requires holding mutex 'mu1'}}
*a = 0; // expected-warning {{reading variable 'a' requires holding mutex 'mu1'}}
if (c->a == 0) doSomething(); // expected-warning {{reading variable 'c' requires holding mutex 'mu1'}}
c->a = 0; // expected-warning {{reading variable 'c' requires holding mutex 'mu1'}}
if ((*c).a == 0) doSomething(); // expected-warning {{reading variable 'c' requires holding mutex 'mu1'}}
(*c).a = 0; // expected-warning {{reading variable 'c' requires holding mutex 'mu1'}}
if (a[0] == 42) doSomething(); // expected-warning {{reading variable 'a' requires holding mutex 'mu1'}}
a[0] = 57; // expected-warning {{reading variable 'a' requires holding mutex 'mu1'}}
if (c[0].a == 42) doSomething(); // expected-warning {{reading variable 'c' requires holding mutex 'mu1'}}
c[0].a = 57; // expected-warning {{reading variable 'c' requires holding mutex 'mu1'}}
mu2.Unlock();
}
void test4() { // Literal arrays
if (sa[0] == 42) doSomething(); // expected-warning {{reading variable 'sa' requires holding mutex 'mu1'}}
sa[0] = 57; // expected-warning {{writing variable 'sa' requires holding mutex 'mu1' exclusively}}
if (sc[0].a == 42) doSomething(); // expected-warning {{reading variable 'sc' requires holding mutex 'mu1'}}
sc[0].a = 57; // expected-warning {{writing variable 'sc' requires holding mutex 'mu1' exclusively}}
sc[0].*pa = 57; // expected-warning {{writing variable 'sc' requires holding mutex 'mu1' exclusively}}
if (*sa == 42) doSomething(); // expected-warning {{reading variable 'sa' requires holding mutex 'mu1'}}
*sa = 57; // expected-warning {{writing variable 'sa' requires holding mutex 'mu1' exclusively}}
if ((*sc).a == 42) doSomething(); // expected-warning {{reading variable 'sc' requires holding mutex 'mu1'}}
(*sc).a = 57; // expected-warning {{writing variable 'sc' requires holding mutex 'mu1' exclusively}}
if (sc->a == 42) doSomething(); // expected-warning {{reading variable 'sc' requires holding mutex 'mu1'}}
sc->a = 57; // expected-warning {{writing variable 'sc' requires holding mutex 'mu1' exclusively}}
}
void test5() {
mu1.ReaderLock(); // OK -- correct use.
mu2.Lock();
if (*a == 0) doSomething();
*a = 0;
if (c->a == 0) doSomething();
c->a = 0;
if ((*c).a == 0) doSomething();
(*c).a = 0;
mu2.Unlock();
mu1.Unlock();
}
};
class SmartPtr_PtGuardedBy_Test {
Mutex mu1;
Mutex mu2;
SmartPtr<int> sp GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
SmartPtr<Cell> sq GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
static constexpr int Cell::*pa = &Cell::a;
void test1() {
mu1.ReaderLock();
mu2.Lock();
sp.get();
if (*sp == 0) doSomething();
*sp = 0;
sq->a = 0;
sq->*pa = 0;
if (sp[0] == 0) doSomething();
sp[0] = 0;
mu2.Unlock();
mu1.Unlock();
}
void test2() {
mu2.Lock();
sp.get(); // expected-warning {{reading variable 'sp' requires holding mutex 'mu1'}}
if (*sp == 0) doSomething(); // expected-warning {{reading variable 'sp' requires holding mutex 'mu1'}}
*sp = 0; // expected-warning {{reading variable 'sp' requires holding mutex 'mu1'}}
sq->a = 0; // expected-warning {{reading variable 'sq' requires holding mutex 'mu1'}}
sq->*pa = 0; // expected-warning {{reading variable 'sq' requires holding mutex 'mu1'}}
if (sp[0] == 0) doSomething(); // expected-warning {{reading variable 'sp' requires holding mutex 'mu1'}}
sp[0] = 0; // expected-warning {{reading variable 'sp' requires holding mutex 'mu1'}}
if (sq[0].a == 0) doSomething(); // expected-warning {{reading variable 'sq' requires holding mutex 'mu1'}}
sq[0].a = 0; // expected-warning {{reading variable 'sq' requires holding mutex 'mu1'}}
mu2.Unlock();
}
void test3() {
mu1.Lock();
sp.get();
if (*sp == 0) doSomething(); // expected-warning {{reading the value pointed to by 'sp' requires holding mutex 'mu2'}}
*sp = 0; // expected-warning {{reading the value pointed to by 'sp' requires holding mutex 'mu2'}}
sq->a = 0; // expected-warning {{reading the value pointed to by 'sq' requires holding mutex 'mu2'}}
sq->*pa = 0; // expected-warning {{reading the value pointed to by 'sq' requires holding mutex 'mu2'}}
if (sp[0] == 0) doSomething(); // expected-warning {{reading the value pointed to by 'sp' requires holding mutex 'mu2'}}
sp[0] = 0; // expected-warning {{reading the value pointed to by 'sp' requires holding mutex 'mu2'}}
if (sq[0].a == 0) doSomething(); // expected-warning {{reading the value pointed to by 'sq' requires holding mutex 'mu2'}}
sq[0].a = 0; // expected-warning {{reading the value pointed to by 'sq' requires holding mutex 'mu2'}}
mu1.Unlock();
}
};
} // end namespace PtGuardedByTest
namespace NonMemberCalleeICETest {
class A {
void Run() {
(RunHelper)(); // expected-warning {{calling function 'RunHelper' requires holding mutex 'M' exclusively}}
}
void RunHelper() EXCLUSIVE_LOCKS_REQUIRED(M);
Mutex M;
};
} // end namespace NonMemberCalleeICETest
namespace pt_guard_attribute_type {
int i PT_GUARDED_BY(sls_mu); // expected-warning {{'pt_guarded_by' only applies to pointer types; type here is 'int'}}
int j PT_GUARDED_VAR; // expected-warning {{'pt_guarded_var' only applies to pointer types; type here is 'int'}}
void test() {
int i PT_GUARDED_BY(sls_mu); // expected-warning {{'pt_guarded_by' attribute only applies to non-static data members and global variables}}
int j PT_GUARDED_VAR; // expected-warning {{'pt_guarded_var' attribute only applies to non-static data members and global variables}}
typedef int PT_GUARDED_BY(sls_mu) bad1; // expected-warning {{'pt_guarded_by' attribute only applies to}}
typedef int PT_GUARDED_VAR bad2; // expected-warning {{'pt_guarded_var' attribute only applies to}}
}
} // end namespace pt_guard_attribute_type
namespace ThreadAttributesOnLambdas {
class Foo {
Mutex mu_;
void LockedFunction() EXCLUSIVE_LOCKS_REQUIRED(mu_);
void test() {
auto func1 = [this]() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
LockedFunction();
};
auto func2 = [this]() NO_THREAD_SAFETY_ANALYSIS {
LockedFunction();
};
auto func3 = [this]() EXCLUSIVE_LOCK_FUNCTION(mu_) {
mu_.Lock();
};
func1(); // expected-warning {{calling function 'operator()' requires holding mutex 'mu_' exclusively}}
func2();
func3();
mu_.Unlock();
}
};
} // end namespace ThreadAttributesOnLambdas
namespace AttributeExpressionCornerCases {
class Foo {
int a GUARDED_BY(getMu());
Mutex* getMu() LOCK_RETURNED("");
Mutex* getUniv() LOCK_RETURNED("*");
void test1() {
a = 0;
}
void test2() EXCLUSIVE_LOCKS_REQUIRED(getUniv()) {
a = 0;
}
void foo(Mutex* mu) EXCLUSIVE_LOCKS_REQUIRED(mu);
void test3() {
foo(nullptr);
}
};
class MapTest {
struct MuCell { Mutex* mu; };
MyMap<MyString, Mutex*> map;
MyMap<MyString, MuCell> mapCell;
int a GUARDED_BY(map["foo"]);
int b GUARDED_BY(mapCell["foo"].mu);
void test() {
map["foo"]->Lock();
a = 0;
map["foo"]->Unlock();
}
void test2() {
mapCell["foo"].mu->Lock();
b = 0;
mapCell["foo"].mu->Unlock();
}
};
class PreciseSmartPtr {
SmartPtr<Mutex> mu;
int val GUARDED_BY(mu);
static bool compare(PreciseSmartPtr& a, PreciseSmartPtr &b) {
a.mu->Lock();
bool result = (a.val == b.val); // expected-warning {{reading variable 'val' requires holding mutex 'b.mu'}} \
// expected-note {{found near match 'a.mu'}}
a.mu->Unlock();
return result;
}
};
class SmartRedeclare {
SmartPtr<Mutex> mu;
int val GUARDED_BY(mu);
void test() EXCLUSIVE_LOCKS_REQUIRED(mu);
void test2() EXCLUSIVE_LOCKS_REQUIRED(mu.get());
void test3() EXCLUSIVE_LOCKS_REQUIRED(mu.get());
};
void SmartRedeclare::test() EXCLUSIVE_LOCKS_REQUIRED(mu.get()) {
val = 0;
}
void SmartRedeclare::test2() EXCLUSIVE_LOCKS_REQUIRED(mu) {
val = 0;
}
void SmartRedeclare::test3() {
val = 0;
}
namespace CustomMutex {
class LOCKABLE BaseMutex { };
class DerivedMutex : public BaseMutex { };
void customLock(const BaseMutex *m) EXCLUSIVE_LOCK_FUNCTION(m);
void customUnlock(const BaseMutex *m) UNLOCK_FUNCTION(m);
static struct DerivedMutex custMu;
static void doSomethingRequiringLock() EXCLUSIVE_LOCKS_REQUIRED(custMu) { }
void customTest() {
customLock(reinterpret_cast<BaseMutex*>(&custMu)); // ignore casts
doSomethingRequiringLock();
customUnlock(reinterpret_cast<BaseMutex*>(&custMu));
}
} // end namespace CustomMutex
} // end AttributeExpressionCornerCases
namespace ScopedLockReturnedInvalid {
class Opaque;
Mutex* getMutex(Opaque* o) LOCK_RETURNED("");
void test(Opaque* o) {
MutexLock lock(getMutex(o));
}
} // end namespace ScopedLockReturnedInvalid
namespace NegativeRequirements {
class Bar {
Mutex mu;
int a GUARDED_BY(mu);
public:
void baz() EXCLUSIVE_LOCKS_REQUIRED(!mu) {
mu.Lock();
a = 0;
mu.Unlock();
}
};
class Foo {
Mutex mu;
int a GUARDED_BY(mu);
public:
void foo() {
mu.Lock(); // warning? needs !mu?
baz(); // expected-warning {{cannot call function 'baz' while mutex 'mu' is held}}
bar();
mu.Unlock();
}
void bar() {
bar2(); // expected-warning {{calling function 'bar2' requires negative capability '!mu'}}
}
void bar2() EXCLUSIVE_LOCKS_REQUIRED(!mu) {
baz();
}
void baz() EXCLUSIVE_LOCKS_REQUIRED(!mu) {
mu.Lock();
a = 0;
mu.Unlock();
}
void test() {
Bar b;
b.baz(); // no warning -- in different class.
}
};
} // end namespace NegativeRequirements
namespace NegativeThreadRoles {
typedef int __attribute__((capability("role"))) ThreadRole;
void acquire(ThreadRole R) EXCLUSIVE_LOCK_FUNCTION(R) NO_THREAD_SAFETY_ANALYSIS {}
void release(ThreadRole R) UNLOCK_FUNCTION(R) NO_THREAD_SAFETY_ANALYSIS {}
ThreadRole FlightControl, Logger;
extern void enque_log_msg(const char *msg);
void log_msg(const char *msg) {
enque_log_msg(msg);
}
void dispatch_log(const char *msg) __attribute__((requires_capability(!FlightControl))) {}
void dispatch_log2(const char *msg) __attribute__((requires_capability(Logger))) {}
void flight_control_entry(void) __attribute__((requires_capability(FlightControl))) {
dispatch_log("wrong"); /* expected-warning {{cannot call function 'dispatch_log' while role 'FlightControl' is held}} */
dispatch_log2("also wrong"); /* expected-warning {{calling function 'dispatch_log2' requires holding role 'Logger' exclusively}} */
}
void spawn_fake_flight_control_thread(void) {
acquire(FlightControl);
flight_control_entry();
release(FlightControl);
}
extern const char *deque_log_msg(void) __attribute__((requires_capability(Logger)));
void logger_entry(void) __attribute__((requires_capability(Logger)))
__attribute__((requires_capability(!FlightControl))) {
const char *msg;
while ((msg = deque_log_msg())) {
dispatch_log(msg);
}
}
void spawn_fake_logger_thread(void) __attribute__((requires_capability(!FlightControl))) {
acquire(Logger);
logger_entry();
release(Logger);
}
int main(void) __attribute__((requires_capability(!FlightControl))) {
spawn_fake_flight_control_thread();
spawn_fake_logger_thread();
for (;;)
; /* Pretend to dispatch things. */
return 0;
}
} // end namespace NegativeThreadRoles
namespace AssertSharedExclusive {
void doSomething();
class Foo {
Mutex mu;
int a GUARDED_BY(mu);
void test() SHARED_LOCKS_REQUIRED(mu) {
mu.AssertHeld();
if (a > 0)
doSomething();
}
};
} // end namespace AssertSharedExclusive
namespace RangeBasedForAndReferences {
class Foo {
struct MyStruct {
int a;
};
Mutex mu;
int a GUARDED_BY(mu);
MyContainer<int> cntr GUARDED_BY(mu);
MyStruct s GUARDED_BY(mu);
int arr[10] GUARDED_BY(mu);
void nonref_test() {
int b = a; // expected-warning {{reading variable 'a' requires holding mutex 'mu'}}
b = 0; // no warning
}
void auto_test() {
auto b = a; // expected-warning {{reading variable 'a' requires holding mutex 'mu'}}
b = 0; // no warning
auto &c = a; // no warning
c = 0; // expected-warning {{writing variable 'a' requires holding mutex 'mu' exclusively}}
}
void ref_test() {
int &b = a;
int &c = b;
int &d = c;
b = 0; // expected-warning {{writing variable 'a' requires holding mutex 'mu' exclusively}}
c = 0; // expected-warning {{writing variable 'a' requires holding mutex 'mu' exclusively}}
d = 0; // expected-warning {{writing variable 'a' requires holding mutex 'mu' exclusively}}
MyStruct &rs = s;
rs.a = 0; // expected-warning {{writing variable 's' requires holding mutex 'mu' exclusively}}
int (&rarr)[10] = arr;
rarr[2] = 0; // expected-warning {{writing variable 'arr' requires holding mutex 'mu' exclusively}}
}
void ptr_test() {
int *b = &a;
*b = 0; // no expected warning yet
}
void for_test() {
int total = 0;
for (int i : cntr) { // expected-warning2 {{reading variable 'cntr' requires holding mutex 'mu'}}
total += i;
}
}
};
} // end namespace RangeBasedForAndReferences
namespace PassByRefTest {
class Foo {
public:
Foo() : a(0), b(0) { }
int a;
int b;
void operator+(const Foo& f);
void operator[](const Foo& g);
void operator()();
};
template<class T>
T&& mymove(T& f);
// test top-level functions
void copy(Foo f);
void write1(Foo& f);
void write2(int a, Foo& f);
void read1(const Foo& f);
void read2(int a, const Foo& f);
void destroy(Foo&& f);
void operator/(const Foo& f, const Foo& g);
void operator*(const Foo& f, const Foo& g);
// Test constructors.
struct FooRead {
FooRead(const Foo &);
};
struct FooWrite {
FooWrite(Foo &);
};
// Test variadic functions
template<typename... T>
void copyVariadic(T...) {}
template<typename... T>
void writeVariadic(T&...) {}
template<typename... T>
void readVariadic(const T&...) {}
void copyVariadicC(int, ...);
class Bar {
public:
Mutex mu;
Foo foo GUARDED_BY(mu);
Foo foo2 GUARDED_BY(mu);
Foo* foop PT_GUARDED_BY(mu);
SmartPtr<Foo> foosp PT_GUARDED_BY(mu);
// test methods.
void mwrite1(Foo& f);
void mwrite2(int a, Foo& f);
void mread1(const Foo& f);
void mread2(int a, const Foo& f);
// static methods
static void smwrite1(Foo& f);
static void smwrite2(int a, Foo& f);
static void smread1(const Foo& f);
static void smread2(int a, const Foo& f);
void operator<<(const Foo& f);
void test1() {
copy(foo); // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
write1(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
write2(10, foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
read1(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
read2(10, foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
destroy(mymove(foo)); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
copyVariadic(foo); // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
readVariadic(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
writeVariadic(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
copyVariadicC(1, foo); // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
FooRead reader(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
FooWrite writer(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
mwrite1(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
mwrite2(10, foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
mread1(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
mread2(10, foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
smwrite1(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
smwrite2(10, foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
smread1(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
smread2(10, foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
foo + foo2; // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}} \
// expected-warning {{passing variable 'foo2' by reference requires holding mutex 'mu'}}
foo / foo2; // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}} \
// expected-warning {{passing variable 'foo2' by reference requires holding mutex 'mu'}}
foo * foo2; // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}} \
// expected-warning {{passing variable 'foo2' by reference requires holding mutex 'mu'}}
foo[foo2]; // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}} \
// expected-warning {{passing variable 'foo2' by reference requires holding mutex 'mu'}}
foo(); // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
(*this) << foo; // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
copy(*foop); // expected-warning {{reading the value pointed to by 'foop' requires holding mutex 'mu'}}
write1(*foop); // expected-warning {{passing the value that 'foop' points to by reference requires holding mutex 'mu'}}
write2(10, *foop); // expected-warning {{passing the value that 'foop' points to by reference requires holding mutex 'mu'}}
read1(*foop); // expected-warning {{passing the value that 'foop' points to by reference requires holding mutex 'mu'}}
read2(10, *foop); // expected-warning {{passing the value that 'foop' points to by reference requires holding mutex 'mu'}}
destroy(mymove(*foop)); // expected-warning {{passing the value that 'foop' points to by reference requires holding mutex 'mu'}}
copy(*foosp); // expected-warning {{reading the value pointed to by 'foosp' requires holding mutex 'mu'}}
write1(*foosp); // expected-warning {{reading the value pointed to by 'foosp' requires holding mutex 'mu'}}
write2(10, *foosp); // expected-warning {{reading the value pointed to by 'foosp' requires holding mutex 'mu'}}
read1(*foosp); // expected-warning {{reading the value pointed to by 'foosp' requires holding mutex 'mu'}}
read2(10, *foosp); // expected-warning {{reading the value pointed to by 'foosp' requires holding mutex 'mu'}}
destroy(mymove(*foosp)); // expected-warning {{reading the value pointed to by 'foosp' requires holding mutex 'mu'}}
// TODO -- these require better smart pointer handling.
copy(*foosp.get());
write1(*foosp.get());
write2(10, *foosp.get());
read1(*foosp.get());
read2(10, *foosp.get());
destroy(mymove(*foosp.get()));
}
};
class Return {
Mutex mu;
Foo foo GUARDED_BY(mu);
Foo* foo_ptr PT_GUARDED_BY(mu);
Foo returns_value_locked() {
MutexLock lock(&mu);
return foo;
}
Foo returns_value_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
return foo;
}
Foo returns_value_releases_lock_after_return() UNLOCK_FUNCTION(mu) {
MutexLock lock(&mu, true);
return foo;
}
Foo returns_value_aquires_lock() EXCLUSIVE_LOCK_FUNCTION(mu) {
mu.Lock();
return foo;
}
Foo returns_value_not_locked() {
return foo; // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
}
Foo returns_value_releases_lock_before_return() UNLOCK_FUNCTION(mu) {
mu.Unlock();
return foo; // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
}
Foo &returns_ref_not_locked() {
return foo; // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
}
Foo &returns_ref_locked() {
MutexLock lock(&mu);
return foo; // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu'}}
}
Foo &returns_ref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
return foo; // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
}
Foo &returns_ref_exclusive_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
return foo;
}
Foo &returns_ref_releases_lock_after_return() UNLOCK_FUNCTION(mu) {
MutexLock lock(&mu, true);
return foo; // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
}
Foo& returns_ref_releases_lock_before_return() UNLOCK_FUNCTION(mu) {
mu.Unlock();
return foo; // // expected-warning {{returning variable 'foo' by reference requires holding mutex 'mu' exclusively}}
}
Foo &returns_ref_aquires_lock() EXCLUSIVE_LOCK_FUNCTION(mu) {
mu.Lock();
return foo;
}
const Foo &returns_constref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
return foo;
}
Foo *returns_ptr() {
return &foo; // FIXME -- Do we want to warn on this ?
}
Foo &returns_ref2() {
return *foo_ptr; // expected-warning {{returning the value that 'foo_ptr' points to by reference requires holding mutex 'mu' exclusively}}
}
};
} // end namespace PassByRefTest
namespace AcquiredBeforeAfterText {
class Foo {
Mutex mu1 ACQUIRED_BEFORE(mu2, mu3);
Mutex mu2;
Mutex mu3;
void test1() {
mu1.Lock();
mu2.Lock();
mu3.Lock();
mu3.Unlock();
mu2.Unlock();
mu1.Unlock();
}
void test2() {
mu2.Lock();
mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu2'}}
mu1.Unlock();
mu2.Unlock();
}
void test3() {
mu3.Lock();
mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu3'}}
mu1.Unlock();
mu3.Unlock();
}
void test4() EXCLUSIVE_LOCKS_REQUIRED(mu1) {
mu2.Lock();
mu2.Unlock();
}
void test5() EXCLUSIVE_LOCKS_REQUIRED(mu2) {
mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu2'}}
mu1.Unlock();
}
void test6() EXCLUSIVE_LOCKS_REQUIRED(mu2) {
mu1.AssertHeld();
}
void test7() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2, mu3) { }
void test8() EXCLUSIVE_LOCKS_REQUIRED(mu3, mu2, mu1) { }
};
class Foo2 {
Mutex mu1;
Mutex mu2 ACQUIRED_AFTER(mu1);
Mutex mu3 ACQUIRED_AFTER(mu1);
void test1() {
mu1.Lock();
mu2.Lock();
mu3.Lock();
mu3.Unlock();
mu2.Unlock();
mu1.Unlock();
}
void test2() {
mu2.Lock();
mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu2'}}
mu1.Unlock();
mu2.Unlock();
}
void test3() {
mu3.Lock();
mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu3'}}
mu1.Unlock();
mu3.Unlock();
}
};
class Foo3 {
Mutex mu1 ACQUIRED_BEFORE(mu2);
Mutex mu2;
Mutex mu3 ACQUIRED_AFTER(mu2) ACQUIRED_BEFORE(mu4);
Mutex mu4;
void test1() {
mu1.Lock();
mu2.Lock();
mu3.Lock();
mu4.Lock();
mu4.Unlock();
mu3.Unlock();
mu2.Unlock();
mu1.Unlock();
}
void test2() {
mu4.Lock();
mu2.Lock(); // expected-warning {{mutex 'mu2' must be acquired before 'mu4'}}
mu2.Unlock();
mu4.Unlock();
}
void test3() {
mu4.Lock();
mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu4'}}
mu1.Unlock();
mu4.Unlock();
}
void test4() {
mu3.Lock();
mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu3'}}
mu1.Unlock();
mu3.Unlock();
}
};
// Test transitive DAG traversal with AFTER
class Foo4 {
Mutex mu1;
Mutex mu2 ACQUIRED_AFTER(mu1);
Mutex mu3 ACQUIRED_AFTER(mu1);
Mutex mu4 ACQUIRED_AFTER(mu2, mu3);
Mutex mu5 ACQUIRED_AFTER(mu4);
Mutex mu6 ACQUIRED_AFTER(mu4);
Mutex mu7 ACQUIRED_AFTER(mu5, mu6);
Mutex mu8 ACQUIRED_AFTER(mu7);
void test() {
mu8.Lock();
mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu8'}}
mu1.Unlock();
mu8.Unlock();
}
};
// Test transitive DAG traversal with BEFORE
class Foo5 {
Mutex mu1 ACQUIRED_BEFORE(mu2, mu3);
Mutex mu2 ACQUIRED_BEFORE(mu4);
Mutex mu3 ACQUIRED_BEFORE(mu4);
Mutex mu4 ACQUIRED_BEFORE(mu5, mu6);
Mutex mu5 ACQUIRED_BEFORE(mu7);
Mutex mu6 ACQUIRED_BEFORE(mu7);
Mutex mu7 ACQUIRED_BEFORE(mu8);
Mutex mu8;
void test() {
mu8.Lock();
mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu8'}}
mu1.Unlock();
mu8.Unlock();
}
};
class Foo6 {
Mutex mu1 ACQUIRED_AFTER(mu3); // expected-warning {{cycle in acquired_before/after dependencies, starting with 'mu1'}}
Mutex mu2 ACQUIRED_AFTER(mu1); // expected-warning {{cycle in acquired_before/after dependencies, starting with 'mu2'}}
Mutex mu3 ACQUIRED_AFTER(mu2); // expected-warning {{cycle in acquired_before/after dependencies, starting with 'mu3'}}
Mutex mu_b ACQUIRED_BEFORE(mu_b); // expected-warning {{cycle in acquired_before/after dependencies, starting with 'mu_b'}}
Mutex mu_a ACQUIRED_AFTER(mu_a); // expected-warning {{cycle in acquired_before/after dependencies, starting with 'mu_a'}}
void test0() {
mu_a.Lock();
mu_b.Lock();
mu_b.Unlock();
mu_a.Unlock();
}
void test1a() {
mu1.Lock();
mu1.Unlock();
}
void test1b() {
mu1.Lock();
mu_a.Lock();
mu_b.Lock();
mu_b.Unlock();
mu_a.Unlock();
mu1.Unlock();
}
void test() {
mu2.Lock();
mu2.Unlock();
}
void test3() {
mu3.Lock();
mu3.Unlock();
}
};
} // end namespace AcquiredBeforeAfterTest
namespace ScopedAdoptTest {
class Foo {
Mutex mu;
int a GUARDED_BY(mu);
int b;
void test1() EXCLUSIVE_UNLOCK_FUNCTION(mu) {
MutexLock slock(&mu, true);
a = 0;
}
void test2() SHARED_UNLOCK_FUNCTION(mu) {
ReaderMutexLock slock(&mu, true);
b = a;
}
void test3() EXCLUSIVE_LOCKS_REQUIRED(mu) { // expected-note {{mutex acquired here}}
MutexLock slock(&mu, true);
a = 0;
} // expected-warning {{expecting mutex 'mu' to be held at the end of function}}
void test4() SHARED_LOCKS_REQUIRED(mu) { // expected-note {{mutex acquired here}}
ReaderMutexLock slock(&mu, true);
b = a;
} // expected-warning {{expecting mutex 'mu' to be held at the end of function}}
};
} // end namespace ScopedAdoptTest
namespace TestReferenceNoThreadSafetyAnalysis {
#define TS_UNCHECKED_READ(x) ts_unchecked_read(x)
// Takes a reference to a guarded data member, and returns an unguarded
// reference.
template <class T>
inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS {
return v;
}
template <class T>
inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS {
return v;
}
class Foo {
public:
Foo(): a(0) { }
int a;
};
class Bar {
public:
Bar() : a(0) { }
Mutex mu;
int a GUARDED_BY(mu);
Foo foo GUARDED_BY(mu);
};
void test() {
Bar bar;
const Bar cbar;
int a = TS_UNCHECKED_READ(bar.a); // nowarn
TS_UNCHECKED_READ(bar.a) = 1; // nowarn
int b = TS_UNCHECKED_READ(bar.foo).a; // nowarn
TS_UNCHECKED_READ(bar.foo).a = 1; // nowarn
int c = TS_UNCHECKED_READ(cbar.a); // nowarn
}
#undef TS_UNCHECKED_READ
} // end namespace TestReferenceNoThreadSafetyAnalysis
namespace GlobalAcquiredBeforeAfterTest {
Mutex mu1;
Mutex mu2 ACQUIRED_AFTER(mu1);
void test3() {
mu2.Lock();
mu1.Lock(); // expected-warning {{mutex 'mu1' must be acquired before 'mu2'}}
mu1.Unlock();
mu2.Unlock();
}
} // end namespace GlobalAcquiredBeforeAfterTest
namespace LifetimeExtensionText {
struct Holder {
virtual ~Holder() throw() {}
int i = 0;
};
void test() {
// Should not crash.
const auto &value = Holder().i;
}
} // end namespace LifetimeExtensionTest
namespace LockableUnions {
union LOCKABLE MutexUnion {
int a;
char* b;
void Lock() EXCLUSIVE_LOCK_FUNCTION();
void Unlock() UNLOCK_FUNCTION();
};
MutexUnion muun2;
MutexUnion muun1 ACQUIRED_BEFORE(muun2);
void test() {
muun2.Lock();
muun1.Lock(); // expected-warning {{mutex 'muun1' must be acquired before 'muun2'}}
muun1.Unlock();
muun2.Unlock();
}
} // end namespace LockableUnions
// This used to crash.
class acquired_before_empty_str {
void WaitUntilSpaceAvailable() {
lock_.ReaderLock(); // expected-note {{acquired here}}
} // expected-warning {{mutex 'lock_' is still held at the end of function}}
Mutex lock_ ACQUIRED_BEFORE("");
};
namespace PR34800 {
struct A {
operator int() const;
};
struct B {
bool g() __attribute__((locks_excluded(h))); // expected-warning {{'locks_excluded' attribute requires arguments whose type is annotated with 'capability' attribute; type here is 'int'}}
int h;
};
struct C {
B *operator[](int);
};
C c;
void f() { c[A()]->g(); }
} // namespace PR34800
#ifdef __cpp_guaranteed_copy_elision
namespace ReturnScopedLockable {
class Object {
public:
MutexLock lock() EXCLUSIVE_LOCK_FUNCTION(mutex) {
return MutexLock(&mutex);
}
ReaderMutexLock lockShared() SHARED_LOCK_FUNCTION(mutex) {
return ReaderMutexLock(&mutex);
}
MutexLock adopt() EXCLUSIVE_LOCKS_REQUIRED(mutex) {
return MutexLock(&mutex, true);
}
ReaderMutexLock adoptShared() SHARED_LOCKS_REQUIRED(mutex) {
return ReaderMutexLock(&mutex, true);
}
int x GUARDED_BY(mutex);
void needsLock() EXCLUSIVE_LOCKS_REQUIRED(mutex);
void testInside() {
MutexLock scope = lock();
x = 1;
needsLock();
}
Mutex mutex;
};
Object obj;
void testLock() {
MutexLock scope = obj.lock();
obj.x = 1;
obj.needsLock();
}
int testSharedLock() {
ReaderMutexLock scope = obj.lockShared();
obj.x = 1; // expected-warning {{writing variable 'x' requires holding mutex 'obj.mutex' exclusively}}
return obj.x;
}
void testAdopt() {
obj.mutex.Lock();
MutexLock scope = obj.adopt();
obj.x = 1;
}
int testAdoptShared() {
obj.mutex.Lock();
ReaderMutexLock scope = obj.adoptShared();
obj.x = 1;
return obj.x;
}
} // namespace ReturnScopedLockable
#endif
namespace PR38640 {
void f() {
// Self-referencing assignment previously caused an infinite loop when thread
// safety analysis was enabled.
int &i = i; // expected-warning {{reference 'i' is not yet bound to a value when used within its own initialization}}
}
}
namespace Derived_Smart_Pointer {
template <class T>
class SmartPtr_Derived : public SmartPtr<T> {};
class Foo {
public:
SmartPtr_Derived<Mutex> mu_;
int a GUARDED_BY(mu_);
int b GUARDED_BY(mu_.get());
int c GUARDED_BY(*mu_);
void Lock() EXCLUSIVE_LOCK_FUNCTION(mu_);
void Unlock() UNLOCK_FUNCTION(mu_);
void test0() {
a = 1; // expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}}
b = 1; // expected-warning {{writing variable 'b' requires holding mutex 'mu_' exclusively}}
c = 1; // expected-warning {{writing variable 'c' requires holding mutex 'mu_' exclusively}}
}
void test1() {
Lock();
a = 1;
b = 1;
c = 1;
Unlock();
}
};
class Bar {
SmartPtr_Derived<Foo> foo;
void test0() {
foo->a = 1; // expected-warning {{writing variable 'a' requires holding mutex 'foo->mu_' exclusively}}
(*foo).b = 1; // expected-warning {{writing variable 'b' requires holding mutex 'foo->mu_' exclusively}}
foo.get()->c = 1; // expected-warning {{writing variable 'c' requires holding mutex 'foo->mu_' exclusively}}
}
void test1() {
foo->Lock();
foo->a = 1;
foo->Unlock();
foo->mu_->Lock();
foo->b = 1;
foo->mu_->Unlock();
MutexLock lock(foo->mu_.get());
foo->c = 1;
}
};
class PointerGuard {
Mutex mu1;
Mutex mu2;
SmartPtr_Derived<int> i GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
void test0() {
i.get(); // expected-warning {{reading variable 'i' requires holding mutex 'mu1'}}
*i = 2; // expected-warning {{reading variable 'i' requires holding mutex 'mu1'}} \
// expected-warning {{reading the value pointed to by 'i' requires holding mutex 'mu2'}}
}
void test1() {
mu1.Lock();
i.get();
*i = 2; // expected-warning {{reading the value pointed to by 'i' requires holding mutex 'mu2'}}
mu1.Unlock();
}
void test2() {
mu2.Lock();
i.get(); // expected-warning {{reading variable 'i' requires holding mutex 'mu1'}}
*i = 2; // expected-warning {{reading variable 'i' requires holding mutex 'mu1'}}
mu2.Unlock();
}
void test3() {
mu1.Lock();
mu2.Lock();
i.get();
*i = 2;
mu2.Unlock();
mu1.Unlock();
}
};
}