本文內容來自 Thread Safety Analysis,如需完整學習,請參考相關連接。html
Clang線程安全分析工具是C++語言的一種擴展,用於警告代碼中潛在的競爭條件。它在編譯期間進行靜態分析,無運行期性能損耗。即便該工具仍處在開發階段,但已足夠成熟,適合部署在生產環境上。編程
它的工做原理相似於一個針對多線程編程的類型系統。例如,變量 foo
可被多線程訪問,當分析工具檢測到該變量在讀寫時沒有被對應的鎖所保護時,會提示一個警告。安全
clang的線程安全分析工具使用capabilities
來保護資源。這裏的資源指的是成員數據,或者是提供訪問底層資源的函數或方法。它能確保調用線程只有先擁有capability
,才能訪問對應資源。多線程
假如 mu
表明一個 mutex
,當線程執行 mu.Lock()
後,表明它得到了訪問 mu
所保護資源的 capability
,當它調用 mu.unLock()
後,表明它釋放了該 capability
。該線程擁有的capability
是不可拷貝的,也不能銷燬它,它只能釋放該capability
,以便其餘線程得到該capability
。函數
線程安全分析工具使用屬性註解來聲明依賴,這些屬性註解是附加在類、方法、數據成員之上的。官方推薦使用宏(mutex.h)來使屬性註解可讀性更好、可理解。工具
運行方法很簡單,執行性能
clang -c -Wthread-safety example.cpp
GUARDED_BY: 該屬性聲明,線程在對該變量進行讀寫以前,必定要得到對應的鎖,以確保對該變量的操做是線程安全的。學習
PT_GUARDED_BY: 和GUARDED_BY相似,它用於指針和智能指針上,對指針自身沒有約束,但對它所指向的數據施加屬性保護。ui
Mutex mu; int *p1 GUARDED_BY(mu); int *p2 PT_GUARDED_BY(mu); unique_ptr<int> p3 PT_GUARDED_BY(mu); void test() { p1 = 0; // Warning! *p2 = 42; // Warning! p2 = new int; // OK. *p3 = 42; // Warning! p3.reset(new int); // OK. }
REQUIRES(mu) 指示調用線程在調用該函數前,必須先得到mu鎖。它假設調用者在調用該函數前,已經擁有mu鎖,內部對共享變量的修改無需額外加鎖。this
REQUIRES_SHARED(), 與 REQUIRES 相似,但僅要求共享讀訪問權限。
Mutex mu1, mu2; int a GUARDED_BY(mu1); int b GUARDED_BY(mu2); void foo() REQUIRES(mu1, mu2) { a = 0; b = 0; } void test() { mu1.Lock(); foo(); // Warning! Requires mu2. mu1.Unlock(); }
Mutex mu; MyClass myObject GUARDED_BY(mu); void lockAndInit() ACQUIRE(mu) { mu.Lock(); myObject.init(); } void cleanupAndUnlock() RELEASE(mu) { myObject.cleanup(); } // Warning! Need to unlock mu. void test() { lockAndInit(); myObject.doSomething(); cleanupAndUnlock(); myObject.doSomething(); // Warning, mu is not locked. }
若是沒有參數傳遞給 ACQUIRE 或 RELEASE,則假定入參爲 this ,分析工具不會檢查函數內部實現。經常使用在隱藏抽象接口的內部鎖的實現細節。
capabilities
, 該註解用來預防死鎖,對於不可重入的鎖,當同一個函數重入時,會得到2次鎖,形成死鎖。Mutex mu; int a GUARDED_BY(mu); void clear() EXCLUDES(mu) { mu.Lock(); a = 0; mu.Unlock(); } void reset() { mu.Lock(); clear(); // Warning! Caller cannot hold 'mu'. mu.Unlock(); }
CAPABILITY(
capaility
,string參數用來代表該
capaility
的種類,在警告時會輸出。
SCOPED_CAPABILITY:指明該類用於RAII風格的資源管理。