From: Caitlin Sadowski Date: Thu, 15 Sep 2011 18:07:32 +0000 (+0000) Subject: Thread safety: test cases originally from gcc annotalysis branch. We are X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=988b5ae14c2157cef36cc5850b294130846c578e;p=clang Thread safety: test cases originally from gcc annotalysis branch. We are relicensing them under the license for llvm. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@139812 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/test/SemaCXX/warn-thread-safety-analysis.cpp b/test/SemaCXX/warn-thread-safety-analysis.cpp index 5107d5b9a0..1981b2a8fc 100644 --- a/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -1,5 +1,28 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety %s +#include // for test 50 +#include // for test 58 + +#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__))) +#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 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)) //-----------------------------------------// // Helper fields @@ -747,3 +770,656 @@ void testUnparse2() { // expected-warning{{cannot resolve lock expression to a specific lockable object}} } + +//----------------------------------------------------------------------------// +// 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 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(a) | static_cast(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 +class Bar { + Mutex mu_; +}; + +template +class Foo { + public: + void func(T x) { + mu_.Lock(); + count_ = x; + mu_.Unlock(); + } + + private: + T count_ GUARDED_BY(mu_); + Bar bar_; + Mutex mu_; +}; + +void main() +{ + Foo *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.2; + res = a_ + 5; + return res; +} + +void Foo::bar() +{ + int x; + mu_.Lock(); + x = foo(); // expected-warning {{calling function 'foo' requires exclusive lock on 'mu2'}} + a_ = x + 1; + mu_.Unlock(); + if (x > 5) { + mu1.Lock(); + g = 2.3; + mu1.Unlock(); + } +} + +void main() +{ + Foo f1, *f2; + f1.mu_.Lock(); + f1.bar(); // expected-warning {{cannot call function 'bar' while holding mutex 'mu_'}} + mu2.Lock(); + f1.foo(); + mu2.Unlock(); + f1.mu_.Unlock(); + f2->mu_.Lock(); + f2->bar(); // expected-warning {{cannot call function 'bar' while holding mutex 'mu_'}} + f2->mu_.Unlock(); + mu2.Lock(); + w = 2.5; + 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 lock on 'mu1_' to be held exclusively}} + 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 lock on 'mu1_' to be held}} \ + // expected-warning {{writing variable 'res' requires lock on 'mu' to be held exclusively}} + *p = i; // expected-warning {{reading variable 'p' requires lock on 'mu' to be held}} \ + // expected-warning {{writing the value pointed to by 'p' requires lock on 'mu' to be held exclusively}} + b1.a_ = res + b3->b_; // expected-warning {{reading variable 'res' requires lock on 'mu' to be held}} \ + // expected-warning {{writing variable 'a_' requires lock on 'mu1_' to be held exclusively}} + b3->b_ = *b1.q; // expected-warning {{reading the value pointed to by 'q' requires lock on 'mu' to be held}} + b3->mu1_.Unlock(); + b1.b_ = res; // expected-warning {{reading variable 'res' requires lock on 'mu' to be held}} + x = res; // expected-warning {{reading variable 'res' requires lock on 'mu' to be held}} + 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 exclusive lock on 'lock_'}} + child->a_ = 5; // expected-warning {{writing variable 'a_' requires lock on 'lock_' to be held exclusively}} + 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 holding mutex 'lock_'}} + 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 holding mutex 'mu1'}} \ + // expected-warning {{cannot call function 'f2' while holding mutex 'mu2'}} + } +}; + +Foo *foo; + +void func() +{ + foo->f1(); // expected-warning {{calling function 'f1' requires exclusive lock on 'mu2'}} \ + // expected-warning {{calling function 'f1' requires exclusive lock on 'mu1'}} +} +} // 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 exclusive lock on 'mu_'}} + b->mu_.Lock(); + b->func2(); // expected-warning {{cannot call function 'func2' while holding mutex 'mu_'}} + b->mu_.Unlock(); + + c->func1(); // expected-warning {{calling function 'func1' requires exclusive lock on 'mu_'}} + c->mu_.Lock(); + c->func2(); // expected-warning {{cannot call function 'func2' while holding mutex 'mu_'}} + 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 shared lock on 'mu1'}} \ + // expected-warning {{calling function 'method1' requires shared lock on 'mu'}} \ + // expected-warning {{calling function 'method1' requires shared lock on 'mu2'}} \ + // expected-warning {{calling function 'method1' requires shared lock on 'mu3'}} +} +} // end namespace thread_annot_lock_67_modified + +