티스토리 뷰
https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
clang TSA (thread safety analysis) for C (not just C++)
https://github.com/jhi/clang-thread-safety-analysis-for-c/blob/master/tsa.h
newpolaris@Donghyuns-MacBook-Pro test2 % clang++ -Wthread-safety main.cpp --std=c++14 -stdlib=libc++
main.cpp:7:17: warning: 'guarded_by' attribute requires arguments whose type is annotated with 'capability' attribute;
type here is 'std::mutex' [-Wthread-safety-attributes]
int balance GUARDED_BY(mu);
^
./mutex.h:21:33: note: expanded from macro 'GUARDED_BY'
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
^
main.cpp:10:9: warning: writing variable 'balance' requires holding mutex 'mu' exclusively [-Wthread-safety-analysis]
balance -= amount;
clang 최신 버전에는 std 함수에도 반영되었다고 하는데 왜 저럴까?
mutex 소스에는 해당 처리가 되어있다, 하지만 define 등으로 guard 되어있는데,
#define _LIBCPP_HAS_THREAD_SAFETY_ANNOTATIONS
#include <mutex>
newpolaris@Donghyuns-MacBook-Pro test2 % clang++ -Wthread-safety main.cpp --std=c++11
main.cpp:10:9: warning: writing variable 'balance' requires holding mutex 'mu' exclusively [-Wthread-safety-analysis]
balance -= amount;
해당 define을 추가하면 동작은 한다
benchmark의 예제를 보면,
// NOTE: Wrappers for std::mutex and std::unique_lock are provided so that
// we can annotate them with thread safety attributes and use the
// -Wthread-safety warning with clang. The standard library types cannot be
// used directly because they do not provide the required annotations.
class CAPABILITY("mutex") Mutex {
그냥 추가하자
class BankAccount {
public:
Mutex mu;
int balance GUARDED_BY(mu);
void depositImpl(int amount) {
balance -= amount;
}
void withdrawImpl(int amount) REQUIRES(mu) {
balance -= amount; // OK. Caller must have locked mu.
}
};
int main() {
BankAccount account;
account.depositImpl(10);
account.mu.lock();
account.withdrawImpl(100);
return 0;
}
Lock 풀지 않았을 때 경고가 나오고,
newpolaris@Donghyuns-MacBook-Pro test2 % clang++ -Wthread-safety main.cpp --std=c++11
main.cpp:27:9: warning: writing variable 'balance' requires holding mutex 'mu' exclusively [-Wthread-safety-analysis]
balance -= amount;
^
main.cpp:44:1: warning: mutex 'account.mu' is still held at the end of function [-Wthread-safety-analysis]
}
^
main.cpp:39:16: note: mutex acquired here
account.mu.lock();
^
2 warnings generated.
그걸, Google benchmark의 Berrier에 적용해보면
class Barrier {
public:
Barrier(int num_threads) : running_threads_(num_threads) {}
// Called by each thread
bool wait() EXCLUDES(lock_) {
bool last_thread = false;
{
MutexLock ml(lock_);
last_thread = createBarrier(ml);
}
if (last_thread) phase_condition_.notify_all();
return last_thread;
}
void removeThread() EXCLUDES(lock_) {
MutexLock ml(lock_);
--running_threads_;
if (entered_ != 0) phase_condition_.notify_all();
}
private:
Mutex lock_;
Condition phase_condition_;
int running_threads_;
// State for barrier management
int phase_number_ GUARDED_BY(lock_) = 0;
int entered_ GUARDED_BY(lock_) = 0; // Number of threads that have entered this barrier
// Enter the barrier and wait until all other threads have also
// entered the barrier. Returns iff this is the last thread to
// enter the barrier.
bool createBarrier(MutexLock& ml) REQUIRES(lock_) {
entered_++;
if (entered_ < running_threads_) {
// Wait for all threads to enter
int phase_number_cp = phase_number_;
auto cb = [this, phase_number_cp]() REQUIRES(lock_) {
return this->phase_number_ > phase_number_cp ||
entered_ == running_threads_; // A thread has aborted in error
};
phase_condition_.wait(ml.native_handle(), cb);
if (phase_number_ > phase_number_cp) return false;
// else (running_threads_ == entered_) and we are the last thread.
}
// Last thread has reached the barrier
phase_number_++;
entered_ = 0;
return true;
}
};
std::condition_variable 에 REQUIRES를 붙이는 건
https://stackoverflow.com/questions/40468897/clang-thread-safety-with-stdcondition-variable
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크