forked from OSchip/llvm-project
5277 lines
122 KiB
C++
5277 lines
122 KiB
C++
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_ASSERT_CAPABILITY=0 %s
|
|
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_ASSERT_CAPABILITY=1 %s
|
|
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 -Wthread-safety -Wthread-safety-beta -Wno-thread-safety-negative -fcxx-exceptions -DUSE_ASSERT_CAPABILITY=0 %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
|
|
|
|
#define LOCKABLE __attribute__((lockable))
|
|
#define SCOPED_LOCKABLE __attribute__((scoped_lockable))
|
|
#define GUARDED_BY(x) __attribute__((guarded_by(x)))
|
|
#define GUARDED_VAR __attribute__((guarded_var))
|
|
#define PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x)))
|
|
#define PT_GUARDED_VAR __attribute__((pt_guarded_var))
|
|
#define ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__)))
|
|
#define ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__)))
|
|
#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__((exclusive_lock_function(__VA_ARGS__)))
|
|
#define SHARED_LOCK_FUNCTION(...) __attribute__((shared_lock_function(__VA_ARGS__)))
|
|
|
|
#if USE_ASSERT_CAPABILITY
|
|
#define ASSERT_EXCLUSIVE_LOCK(...) __attribute__((assert_capability(__VA_ARGS__)))
|
|
#define ASSERT_SHARED_LOCK(...) __attribute__((assert_shared_capability(__VA_ARGS__)))
|
|
#else
|
|
#define ASSERT_EXCLUSIVE_LOCK(...) __attribute__((assert_exclusive_lock(__VA_ARGS__)))
|
|
#define ASSERT_SHARED_LOCK(...) __attribute__((assert_shared_lock(__VA_ARGS__)))
|
|
#endif
|
|
|
|
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__((exclusive_trylock_function(__VA_ARGS__)))
|
|
#define SHARED_TRYLOCK_FUNCTION(...) __attribute__((shared_trylock_function(__VA_ARGS__)))
|
|
#define UNLOCK_FUNCTION(...) __attribute__((unlock_function(__VA_ARGS__)))
|
|
#define EXCLUSIVE_UNLOCK_FUNCTION(...) __attribute__((release_capability(__VA_ARGS__)))
|
|
#define SHARED_UNLOCK_FUNCTION(...) __attribute__((release_shared_capability(__VA_ARGS__)))
|
|
#define LOCK_RETURNED(x) __attribute__((lock_returned(x)))
|
|
#define LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__)))
|
|
#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__)))
|
|
#define SHARED_LOCKS_REQUIRED(...) __attribute__((shared_locks_required(__VA_ARGS__)))
|
|
#define NO_THREAD_SAFETY_ANALYSIS __attribute__((no_thread_safety_analysis))
|
|
|
|
|
|
class LOCKABLE Mutex {
|
|
public:
|
|
void Lock() __attribute__((exclusive_lock_function));
|
|
void ReaderLock() __attribute__((shared_lock_function));
|
|
void Unlock() __attribute__((unlock_function));
|
|
bool TryLock() __attribute__((exclusive_trylock_function(true)));
|
|
bool ReaderTryLock() __attribute__((shared_trylock_function(true)));
|
|
void LockWhen(const int &cond) __attribute__((exclusive_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 __attribute__((scoped_lockable)) DoubleMutexLock {
|
|
public:
|
|
DoubleMutexLock(Mutex *mu1, Mutex *mu2)
|
|
__attribute__((exclusive_lock_function(mu1, mu2)));
|
|
~DoubleMutexLock() __attribute__((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_;
|
|
};
|
|
|
|
|
|
// 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() __attribute__((exclusive_lock_function(mu)));
|
|
};
|
|
|
|
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();
|
|
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()) {
|
|
sls_mu.Unlock();
|
|
if (getBool()) {
|
|
if (getBool()) {
|
|
continue; // \
|
|
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();
|
|
}
|
|
|
|
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; // expected-warning{{mutex 'sls_mu' is not held on every path through here}}
|
|
}
|
|
}
|
|
sls_mu.Lock();
|
|
}
|
|
sls_mu.Unlock();
|
|
}
|
|
|
|
//-----------------------------------------//
|
|
// Handling lock expressions in attribute args
|
|
// -------------------------------------------//
|
|
|
|
Mutex aa_mu;
|
|
|
|
class GlobalLocker {
|
|
public:
|
|
void globalLock() __attribute__((exclusive_lock_function(aa_mu)));
|
|
void globalUnlock() __attribute__((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();
|
|
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() __attribute__((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() __attribute__((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-warning {{mutex 'sls_mu' is acquired exclusively and shared in the same scope}}
|
|
do {
|
|
sls_mu.Unlock();
|
|
sls_mu.Lock(); // \
|
|
// expected-note {{the other acquisition of mutex 'sls_mu' is here}}
|
|
} 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_bad_0() {
|
|
sls_mu.Lock(); // \
|
|
// expected-warning {{mutex 'sls_mu' is acquired exclusively and shared in the same scope}}
|
|
do {
|
|
sls_mu.Unlock();
|
|
sls_mu.ReaderLock(); // \
|
|
// expected-note {{the other acquisition of mutex 'sls_mu' is here}}
|
|
} 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();
|
|
}
|
|
|
|
// FIXME: Add support for functions (not only methods)
|
|
class LRBar {
|
|
public:
|
|
void aa_elr_fun() __attribute__((exclusive_locks_required(aa_mu)));
|
|
void aa_elr_fun_s() __attribute__((shared_locks_required(aa_mu)));
|
|
void le_fun() __attribute__((locks_excluded(sls_mu)));
|
|
};
|
|
|
|
class LRFoo {
|
|
public:
|
|
void test() __attribute__((exclusive_locks_required(sls_mu)));
|
|
void testShared() __attribute__((shared_locks_required(sls_mu2)));
|
|
};
|
|
|
|
void elr_fun() __attribute__((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() __attribute__((no_thread_safety_analysis));
|
|
|
|
void es_fun_8() {
|
|
Bar.aa_elr_fun_s();
|
|
}
|
|
|
|
void es_fun_9() __attribute__((shared_locks_required(aa_mu)));
|
|
void es_fun_9() {
|
|
Bar.aa_elr_fun_s();
|
|
}
|
|
|
|
void es_fun_10() __attribute__((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
|
|
// paramters.
|
|
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() __attribute__((exclusive_lock_function(mu)));
|
|
void unlockData() __attribute__((unlock_function(mu)));
|
|
|
|
void doSomething() __attribute__((exclusive_locks_required(mu))) { }
|
|
};
|
|
|
|
|
|
class DataLocker {
|
|
public:
|
|
void lockData (MyData *d) __attribute__((exclusive_lock_function(d->mu)));
|
|
void unlockData(MyData *d) __attribute__((unlock_function(d->mu)));
|
|
};
|
|
|
|
|
|
class Foo {
|
|
public:
|
|
void foo(MyData* d) __attribute__((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);
|
|
}
|
|
};
|
|
} // end namespace substituation_test
|
|
|
|
|
|
|
|
namespace constructor_destructor_tests {
|
|
Mutex fooMu;
|
|
int myVar GUARDED_BY(fooMu);
|
|
|
|
class Foo {
|
|
public:
|
|
Foo() __attribute__((exclusive_lock_function(fooMu))) { }
|
|
~Foo() __attribute__((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();
|
|
|
|
void foo1() {
|
|
MutexLock mulock(&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);
|
|
MutexLock mulock_b(&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 foo3() {
|
|
bool b = mu.TryLock();
|
|
if (b) {
|
|
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 uknown 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();
|
|
}
|
|
}; // 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' 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 __attribute__((lockable)) mutex {
|
|
void lock() __attribute__((exclusive_lock_function));
|
|
void unlock() __attribute__((unlock_function));
|
|
// ...
|
|
};
|
|
bool foo();
|
|
bool bar();
|
|
mutex m;
|
|
void test() {
|
|
m.lock();
|
|
while (foo()) {
|
|
m.unlock();
|
|
// ...
|
|
if (bar()) {
|
|
// ...
|
|
if (foo())
|
|
continue; // expected-warning {{expecting mutex 'm' to be held at start of each loop}}
|
|
//...
|
|
}
|
|
// ...
|
|
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' requires holding mutex 'myFoo.mu_' exclusively}}
|
|
|
|
myFoo.fooT2(dummy); // \
|
|
// expected-warning {{calling function 'fooT2' 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;
|
|
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();
|
|
}
|
|
|
|
|
|
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();
|
|
}
|
|
|
|
|
|
// Sanity check -- 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 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();
|
|
}
|
|
}
|
|
|
|
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();
|
|
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 for managed lock.
|
|
rlock.Release(); // expected-warning {{releasing mutex 'mu_' that was not held}}
|
|
}
|
|
|
|
|
|
} // end namespace ReleasableScopedLock
|
|
|
|
|
|
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()'}}
|
|
}
|
|
|
|
} // 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();
|
|
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();
|
|
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();
|
|
foo.lock(); // expected-warning {{acquiring mutex 'foo' that is already held}}
|
|
foo.unlock();
|
|
foo.unlock(); // expected-warning {{releasing mutex 'foo' that was not held}}
|
|
}
|
|
|
|
|
|
void test1() {
|
|
Foo foo;
|
|
foo.lock1();
|
|
foo.a = 0;
|
|
foo.unlock1();
|
|
|
|
foo.lock1();
|
|
foo.lock1(); // expected-warning {{acquiring mutex 'foo.mu1_' that is already held}}
|
|
foo.a = 0;
|
|
foo.unlock1();
|
|
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();
|
|
foo.slock1(); // expected-warning {{acquiring mutex 'foo.mu1_' that is already held}}
|
|
int d2 = foo.a;
|
|
foo.unlock1();
|
|
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();
|
|
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();
|
|
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();
|
|
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();
|
|
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();
|
|
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 mutex '*' 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) __attribute__((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' requires holding mutex 'cell.mu_' exclusively}}
|
|
}
|
|
|
|
|
|
template<class T>
|
|
void globalELR(Cell<T>* c) __attribute__((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' requires holding mutex 'cell.mu_' exclusively}}
|
|
}
|
|
|
|
|
|
template<class T>
|
|
void globalELR2(Cell<T>* c) __attribute__((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' requires holding mutex 'cell.mu_' exclusively}}
|
|
}
|
|
|
|
|
|
template<class T>
|
|
class FooT {
|
|
public:
|
|
void elr(Cell<T>* c) __attribute__((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);
|
|
};
|
|
|
|
|
|
void test() {
|
|
SelfLock s;
|
|
s.foo();
|
|
}
|
|
|
|
void test2() {
|
|
SelfLock2 s2;
|
|
s2.foo();
|
|
}
|
|
|
|
} // 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()() { }
|
|
|
|
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_[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();
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
int b = a;
|
|
a = 0;
|
|
mu_.Unlock();
|
|
}
|
|
|
|
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;
|
|
}
|
|
};
|
|
|
|
} // 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 PtGuardedBySanityTest {
|
|
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);
|
|
|
|
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}}
|
|
|
|
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}}
|
|
|
|
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}}
|
|
|
|
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);
|
|
|
|
void test1() {
|
|
mu1.ReaderLock();
|
|
mu2.Lock();
|
|
|
|
sp.get();
|
|
if (*sp == 0) doSomething();
|
|
*sp = 0;
|
|
sq->a = 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'}}
|
|
|
|
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'}}
|
|
|
|
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() __attribute__((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 holding '!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) __attribute__((exclusive_lock_function(R))) __attribute__((no_thread_safety_analysis)) {}
|
|
void release(ThreadRole R) __attribute__((unlock_function(R))) __attribute__((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 mutex '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))) {
|
|
const char *msg;
|
|
|
|
while ((msg = deque_log_msg())) {
|
|
dispatch_log(msg);
|
|
}
|
|
}
|
|
|
|
void spawn_fake_logger_thread(void) {
|
|
acquire(Logger);
|
|
logger_entry();
|
|
release(Logger);
|
|
}
|
|
|
|
int main(void) {
|
|
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);
|
|
};
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
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'}}
|
|
|
|
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'}}
|
|
(*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()));
|
|
}
|
|
};
|
|
|
|
|
|
} // 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
|
|
|
|
namespace ReturnScopedLockable {
|
|
template<typename Object> class SCOPED_LOCKABLE ReadLockedPtr {
|
|
public:
|
|
ReadLockedPtr(Object *ptr) SHARED_LOCK_FUNCTION((*this)->mutex);
|
|
ReadLockedPtr(ReadLockedPtr &&) SHARED_LOCK_FUNCTION((*this)->mutex);
|
|
~ReadLockedPtr() UNLOCK_FUNCTION();
|
|
|
|
Object *operator->() const { return object; }
|
|
|
|
private:
|
|
Object *object;
|
|
};
|
|
|
|
struct Object {
|
|
int f() SHARED_LOCKS_REQUIRED(mutex);
|
|
Mutex mutex;
|
|
};
|
|
|
|
ReadLockedPtr<Object> get();
|
|
int use() {
|
|
auto ptr = get();
|
|
return ptr->f();
|
|
}
|
|
}
|