| // RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety %s |
| |
| |
| //-----------------------------------------// |
| // Helper fields |
| //-----------------------------------------// |
| |
| |
| class __attribute__((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)); |
| }; |
| |
| |
| 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))); // FIXME: scoping error |
| }; |
| |
| MutexWrapper sls_mw; |
| |
| void sls_fun_0() { |
| sls_mw.mu.Lock(); |
| // sls_mw.x = 5; // FIXME: turn mu into sls_mw.mu |
| 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_bad_1() { |
| sls_mu.Unlock(); // \ |
| // expected-warning{{unlocking 'sls_mu' that was not locked}} |
| } |
| |
| void sls_fun_bad_2() { |
| sls_mu.Lock(); |
| sls_mu.Lock(); // \ |
| // expected-warning{{locking 'sls_mu' that is already locked}} |
| sls_mu.Unlock(); |
| } |
| |
| void sls_fun_bad_3() { |
| sls_mu.Lock(); // \ |
| // expected-warning{{mutex 'sls_mu' is still held at the end of function 'sls_fun_bad_3'}} |
| } |
| |
| void sls_fun_bad_4() { |
| if (getBool()) |
| sls_mu.Lock(); // \ |
| // expected-warning{{mutex 'sls_mu' is still held at the end of its scope}} |
| else |
| sls_mu2.Lock(); // \ |
| // expected-warning{{mutex 'sls_mu2' is still held at the end of its scope}} |
| } |
| |
| void sls_fun_bad_5() { |
| sls_mu.Lock(); // \ |
| // expected-warning{{mutex 'sls_mu' is still held at the end of its scope}} |
| if (getBool()) |
| sls_mu.Unlock(); |
| } |
| |
| void sls_fun_bad_6() { |
| if (getBool()) { |
| sls_mu.Lock(); // \ |
| // expected-warning{{mutex 'sls_mu' is still held at the end of its scope}} |
| } else { |
| if (getBool()) { |
| getBool(); // EMPTY |
| } else { |
| getBool(); // EMPTY |
| } |
| } |
| sls_mu.Unlock(); // \ |
| // expected-warning{{unlocking 'sls_mu' that was not locked}} |
| } |
| |
| void sls_fun_bad_7() { |
| sls_mu.Lock(); |
| while (getBool()) { // \ |
| // expected-warning{{expecting lock on 'sls_mu' to be held at start of each loop}} |
| sls_mu.Unlock(); |
| if (getBool()) { |
| if (getBool()) { |
| continue; |
| } |
| } |
| sls_mu.Lock(); // \ |
| // expected-warning{{mutex 'sls_mu' is still held at the end of its scope}} |
| } |
| sls_mu.Unlock(); |
| } |
| |
| void sls_fun_bad_8() { |
| sls_mu.Lock(); |
| do { |
| sls_mu.Unlock(); // \ |
| // expected-warning{{expecting lock on 'sls_mu' to be held at start of each loop}} |
| } while (getBool()); |
| } |
| |
| void sls_fun_bad_9() { |
| do { |
| sls_mu.Lock(); // \ |
| // expected-warning{{mutex 'sls_mu' is still held at the end of its scope}} |
| } while (getBool()); |
| sls_mu.Unlock(); |
| } |
| |
| void sls_fun_bad_10() { |
| sls_mu.Lock(); // \ |
| // expected-warning{{mutex 'sls_mu' is still held at the end of function 'sls_fun_bad_10'}} |
| while(getBool()) { // \ |
| // expected-warning{{expecting lock on 'sls_mu' to be held at start of each loop}} |
| sls_mu.Unlock(); |
| } |
| } |
| |
| void sls_fun_bad_11() { |
| while (getBool()) { |
| sls_mu.Lock(); // \ |
| // expected-warning{{mutex 'sls_mu' is still held at the end of its scope}} |
| } |
| sls_mu.Unlock(); // \ |
| // expected-warning{{unlocking 'sls_mu' that was not locked}} |
| } |
| |
| //-----------------------------------------// |
| // 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{{unlocking 'aa_mu' that was not locked}} |
| } |
| |
| void aa_fun_bad_2() { |
| glock.globalLock(); |
| glock.globalLock(); // \ |
| // expected-warning{{locking 'aa_mu' that is already locked}} |
| glock.globalUnlock(); |
| } |
| |
| void aa_fun_bad_3() { |
| glock.globalLock(); // \ |
| // expected-warning{{mutex 'aa_mu' is still held at the end of function 'aa_fun_bad_3'}} |
| } |
| |
| //--------------------------------------------------// |
| // Regression tests for unusual method names |
| //--------------------------------------------------// |
| |
| Mutex wmu; |
| |
| // Test diagnostics for other method names. |
| class WeirdMethods { |
| WeirdMethods() { |
| wmu.Lock(); // \ |
| // expected-warning {{mutex 'wmu' is still held at the end of function 'WeirdMethods'}} |
| } |
| ~WeirdMethods() { |
| wmu.Lock(); // \ |
| // expected-warning {{mutex 'wmu' is still held at the end of function '~WeirdMethods'}} |
| } |
| void operator++() { |
| wmu.Lock(); // \ |
| // expected-warning {{mutex 'wmu' is still held at the end of function 'operator++'}} |
| } |
| operator int*() { |
| wmu.Lock(); // \ |
| // expected-warning {{mutex 'wmu' is still held at the end of function 'operator int *'}} |
| return 0; |
| } |
| }; |
| |
| //-----------------------------------------------// |
| // 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 lock on 'sls_mu2' to be held exclusively}} |
| *pgb_field = x; // expected-warning {{reading variable 'pgb_field' requires lock on 'sls_mu2' to be held}} \ |
| // expected-warning {{writing the value pointed to by 'pgb_field' requires lock on 'sls_mu' to be held exclusively}} |
| x = *pgb_field; // expected-warning {{reading variable 'pgb_field' requires lock on 'sls_mu2' to be held}} \ |
| // expected-warning {{reading the value pointed to by 'pgb_field' requires lock on 'sls_mu' to be held}} |
| (*pgb_field)++; // expected-warning {{reading variable 'pgb_field' requires lock on 'sls_mu2' to be held}} \ |
| // expected-warning {{writing the value pointed to by 'pgb_field' requires lock on 'sls_mu' to be held exclusively}} |
| } |
| }; |
| |
| class GBFoo { |
| public: |
| int gb_field __attribute__((guarded_by(sls_mu))); |
| |
| void testFoo() { |
| gb_field = 0; // \ |
| // expected-warning {{writing variable 'gb_field' requires lock on 'sls_mu' to be held 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 lock on 'any mutex' to be held exclusively}} |
| } |
| |
| void gb_bad_1() { |
| int x = sls_guard_var; // \ |
| // expected-warning{{reading variable 'sls_guard_var' requires lock on 'any mutex' to be held}} |
| } |
| |
| void gb_bad_2() { |
| sls_guardby_var = 1; // \ |
| // expected-warning {{writing variable 'sls_guardby_var' requires lock on 'sls_mu' to be held exclusively}} |
| } |
| |
| void gb_bad_3() { |
| int x = sls_guardby_var; // \ |
| // expected-warning {{reading variable 'sls_guardby_var' requires lock on 'sls_mu' to be held}} |
| } |
| |
| void gb_bad_4() { |
| *pgb_gvar = 1; // \ |
| // expected-warning {{writing the value pointed to by 'pgb_gvar' requires lock on 'any mutex' to be held exclusively}} |
| } |
| |
| void gb_bad_5() { |
| int x = *pgb_gvar; // \ |
| // expected-warning {{reading the value pointed to by 'pgb_gvar' requires lock on 'any mutex' to be held}} |
| } |
| |
| void gb_bad_6() { |
| *pgb_var = 1; // \ |
| // expected-warning {{writing the value pointed to by 'pgb_var' requires lock on 'sls_mu' to be held exclusively}} |
| } |
| |
| void gb_bad_7() { |
| int x = *pgb_var; // \ |
| // expected-warning {{reading the value pointed to by 'pgb_var' requires lock on 'sls_mu' to be held}} |
| } |
| |
| void gb_bad_8() { |
| GBFoo G; |
| G.gb_field = 0; // \ |
| // expected-warning {{writing variable 'gb_field' requires lock on 'sls_mu'}} |
| } |
| |
| void gb_bad_9() { |
| sls_guard_var++; // \ |
| // expected-warning{{writing variable 'sls_guard_var' requires lock on 'any mutex' to be held exclusively}} |
| sls_guard_var--; // \ |
| // expected-warning{{writing variable 'sls_guard_var' requires lock on 'any mutex' to be held exclusively}} |
| ++sls_guard_var; // \ |
| // expected-warning{{writing variable 'sls_guard_var' requires lock on 'any mutex' to be held exclusively}} |
| --sls_guard_var;// \ |
| // expected-warning{{writing variable 'sls_guard_var' requires lock on 'any mutex' to be held 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 lock on 'mu' to be held exclusively}} |
| b = a; // \ |
| // expected-warning {{reading variable 'a' requires lock on 'mu' to be held}} |
| c = 0; // \ |
| // expected-warning {{writing variable 'c' requires lock on 'mu' to be held 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 lock on 'mu' to be held exclusively}} |
| 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 lock on 'mu' to be held}} |
| 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 lock on 'mu' to be held exclusively}} |
| BarA.FooPointer->mu.Unlock(); |
| } |
| |
| void late_bad_3() { |
| LateBar BarA; |
| BarA.Foo.mu.Lock(); |
| BarA.FooPointer->a = 2; // \ |
| // expected-warning{{writing variable 'a' requires lock on 'mu' to be held exclusively}} |
| BarA.Foo.mu.Unlock(); |
| } |
| |
| void late_bad_4() { |
| LateBar BarA; |
| BarA.Foo.mu.Lock(); |
| BarA.Foo2.a = 2; // \ |
| // expected-warning{{writing variable 'a' requires lock on 'mu' to be held exclusively}} |
| 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(); |
| do { |
| sls_mu.Unlock(); |
| sls_mu.Lock(); // \ |
| // expected-warning {{lock 'sls_mu' is exclusive and shared in the same scope}} |
| } while (getBool()); |
| sls_mu.Unlock(); |
| } |
| |
| void shared_fun_3() { |
| if (getBool()) |
| sls_mu.Lock(); |
| else |
| sls_mu.Lock(); |
| *pgb_var = 1; |
| sls_mu.Unlock(); |
| } |
| |
| void shared_fun_4() { |
| if (getBool()) |
| sls_mu.ReaderLock(); |
| else |
| sls_mu.ReaderLock(); |
| int x = sls_guardby_var; |
| sls_mu.Unlock(); |
| } |
| |
| void shared_fun_8() { |
| if (getBool()) |
| sls_mu.Lock(); // \ |
| // expected-warning {{lock 'sls_mu' is exclusive and shared in the same scope}} |
| else |
| sls_mu.ReaderLock(); // \ |
| // expected-note {{the other lock of mutex 'sls_mu' is here}} |
| sls_mu.Unlock(); |
| } |
| |
| void shared_bad_0() { |
| sls_mu.Lock(); |
| do { |
| sls_mu.Unlock(); |
| sls_mu.ReaderLock(); // \ |
| // expected-warning {{lock 'sls_mu' is exclusive and shared in the same scope}} |
| } while (getBool()); |
| sls_mu.Unlock(); |
| } |
| |
| void shared_bad_1() { |
| if (getBool()) |
| sls_mu.Lock(); // \ |
| // expected-warning {{lock 'sls_mu' is exclusive and shared in the same scope}} |
| else |
| sls_mu.ReaderLock(); // \ |
| // expected-note {{the other lock of mutex 'sls_mu' is here}} |
| *pgb_var = 1; |
| sls_mu.Unlock(); |
| } |
| |
| void shared_bad_2() { |
| if (getBool()) |
| sls_mu.ReaderLock(); // \ |
| // expected-warning {{lock 'sls_mu' is exclusive and shared in the same scope}} |
| else |
| sls_mu.Lock(); // \ |
| // expected-note {{the other lock 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_bad_0() { |
| Bar.aa_elr_fun(); // \ |
| // expected-warning {{calling function 'aa_elr_fun' requires exclusive lock on 'aa_mu'}} |
| } |
| |
| void es_bad_1() { |
| aa_mu.ReaderLock(); |
| Bar.aa_elr_fun(); // \ |
| // expected-warning {{calling function 'aa_elr_fun' requires exclusive lock on 'aa_mu'}} |
| aa_mu.Unlock(); |
| } |
| |
| void es_bad_2() { |
| Bar.aa_elr_fun_s(); // \ |
| // expected-warning {{calling function 'aa_elr_fun_s' requires shared lock on 'aa_mu'}} |
| } |
| |
| void es_bad_3() { |
| MyLRFoo.test(); // \ |
| // expected-warning {{calling function 'test' requires exclusive lock on 'sls_mu'}} |
| } |
| |
| void es_bad_4() { |
| MyLRFoo.testShared(); // \ |
| // expected-warning {{calling function 'testShared' requires shared lock on 'sls_mu2'}} |
| } |
| |
| void es_bad_5() { |
| sls_mu.ReaderLock(); |
| MyLRFoo.test(); // \ |
| // expected-warning {{calling function 'test' requires exclusive lock on 'sls_mu'}} |
| sls_mu.Unlock(); |
| } |
| |
| void es_bad_6() { |
| sls_mu.Lock(); |
| Bar.le_fun(); // \ |
| // expected-warning {{cannot call function 'le_fun' while holding mutex 'sls_mu'}} |
| sls_mu.Unlock(); |
| } |
| |
| void es_bad_7() { |
| sls_mu.ReaderLock(); |
| Bar.le_fun(); // \ |
| // expected-warning {{cannot call function 'le_fun' while holding mutex 'sls_mu'}} |
| sls_mu.Unlock(); |
| } |