be/src/common/thread_safety_annotations.h
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | // Thread safety annotation macros and annotated mutex wrappers for |
19 | | // Clang's -Wthread-safety static analysis. |
20 | | // Reference: https://clang.llvm.org/docs/ThreadSafetyAnalysis.html |
21 | | |
22 | | #pragma once |
23 | | |
24 | | #include <mutex> |
25 | | #include <shared_mutex> |
26 | | |
27 | | #ifdef BE_TEST |
28 | | namespace doris { |
29 | | void mock_random_sleep(); |
30 | | } // namespace doris |
31 | | #endif |
32 | | |
33 | | // Enable thread safety attributes only with clang. |
34 | | // The attributes can be safely erased when compiling with other compilers. |
35 | | #if defined(__clang__) && (!defined(SWIG)) |
36 | | #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) |
37 | | #else |
38 | | #define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op |
39 | | #endif |
40 | | |
41 | | #define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) |
42 | | |
43 | | #define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) |
44 | | |
45 | | #define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) |
46 | | |
47 | | #define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) |
48 | | |
49 | | #define ACQUIRED_BEFORE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) |
50 | | |
51 | | #define ACQUIRED_AFTER(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) |
52 | | |
53 | | #define REQUIRES(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) |
54 | | |
55 | | #define REQUIRES_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__)) |
56 | | |
57 | | #define ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) |
58 | | |
59 | | #define ACQUIRE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) |
60 | | |
61 | | #define RELEASE(...) THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) |
62 | | |
63 | | #define RELEASE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) |
64 | | |
65 | | #define TRY_ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) |
66 | | |
67 | | #define TRY_ACQUIRE_SHARED(...) \ |
68 | | THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) |
69 | | |
70 | | #define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) |
71 | | |
72 | | #define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) |
73 | | |
74 | | #define ASSERT_SHARED_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) |
75 | | |
76 | | #define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) |
77 | | |
78 | | #define NO_THREAD_SAFETY_ANALYSIS THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) |
79 | | |
80 | | // Annotated mutex wrapper for use with Clang thread safety analysis. |
81 | | // Wraps std::mutex and provides the CAPABILITY annotation so that |
82 | | // GUARDED_BY / REQUIRES / etc. annotations can reference it. |
83 | | class CAPABILITY("mutex") AnnotatedMutex { |
84 | | public: |
85 | 2.81k | void lock() ACQUIRE() { _mutex.lock(); } |
86 | 2.81k | void unlock() RELEASE() { _mutex.unlock(); } |
87 | 0 | bool try_lock() TRY_ACQUIRE(true) { return _mutex.try_lock(); } |
88 | | |
89 | | // Access the underlying std::mutex (e.g., for std::condition_variable). |
90 | | // Use with care — this bypasses thread safety annotations. |
91 | 0 | std::mutex& native_handle() { return _mutex; } |
92 | | |
93 | | private: |
94 | | std::mutex _mutex; |
95 | | }; |
96 | | |
97 | | // Annotated shared mutex wrapper for use with Clang thread safety analysis. |
98 | | // Wraps std::shared_mutex and provides both exclusive and shared capability |
99 | | // operations so GUARDED_BY / REQUIRES_SHARED / etc. can reference it. |
100 | | class CAPABILITY("mutex") AnnotatedSharedMutex { |
101 | | public: |
102 | 15 | void lock() ACQUIRE() { _mutex.lock(); } |
103 | 15 | void unlock() RELEASE() { _mutex.unlock(); } |
104 | 0 | bool try_lock() TRY_ACQUIRE(true) { return _mutex.try_lock(); } |
105 | | |
106 | 14 | void lock_shared() ACQUIRE_SHARED() { _mutex.lock_shared(); } |
107 | 14 | void unlock_shared() RELEASE_SHARED() { _mutex.unlock_shared(); } |
108 | 0 | bool try_lock_shared() TRY_ACQUIRE_SHARED(true) { return _mutex.try_lock_shared(); } |
109 | | |
110 | | // Access the underlying std::shared_mutex (e.g., for std::condition_variable_any). |
111 | | // Use with care — this bypasses thread safety annotations. |
112 | 0 | std::shared_mutex& native_handle() { return _mutex; } |
113 | | |
114 | | private: |
115 | | std::shared_mutex _mutex; |
116 | | }; |
117 | | |
118 | | // RAII scoped lock guard annotated for thread safety analysis. |
119 | | // In BE_TEST builds, injects a random sleep before acquiring and after |
120 | | // releasing the lock to exercise concurrent code paths. |
121 | | template <typename MutexType> |
122 | | class SCOPED_CAPABILITY LockGuard { |
123 | | public: |
124 | 2.49k | explicit LockGuard(MutexType& mu) ACQUIRE(mu) : _mu(mu) { |
125 | 2.49k | #ifdef BE_TEST |
126 | 2.49k | doris::mock_random_sleep(); |
127 | 2.49k | #endif |
128 | 2.49k | _mu.lock(); |
129 | 2.49k | } _ZN9LockGuardI14AnnotatedMutexEC2ERS0_ Line | Count | Source | 124 | 2.47k | explicit LockGuard(MutexType& mu) ACQUIRE(mu) : _mu(mu) { | 125 | 2.47k | #ifdef BE_TEST | 126 | 2.47k | doris::mock_random_sleep(); | 127 | 2.47k | #endif | 128 | 2.47k | _mu.lock(); | 129 | 2.47k | } |
_ZN9LockGuardI20AnnotatedSharedMutexEC2ERS0_ Line | Count | Source | 124 | 15 | explicit LockGuard(MutexType& mu) ACQUIRE(mu) : _mu(mu) { | 125 | 15 | #ifdef BE_TEST | 126 | 15 | doris::mock_random_sleep(); | 127 | 15 | #endif | 128 | 15 | _mu.lock(); | 129 | 15 | } |
|
130 | 2.49k | ~LockGuard() RELEASE() { |
131 | 2.49k | _mu.unlock(); |
132 | 2.49k | #ifdef BE_TEST |
133 | 2.49k | doris::mock_random_sleep(); |
134 | 2.49k | #endif |
135 | 2.49k | } _ZN9LockGuardI14AnnotatedMutexED2Ev Line | Count | Source | 130 | 2.47k | ~LockGuard() RELEASE() { | 131 | 2.47k | _mu.unlock(); | 132 | 2.47k | #ifdef BE_TEST | 133 | 2.47k | doris::mock_random_sleep(); | 134 | 2.47k | #endif | 135 | 2.47k | } |
_ZN9LockGuardI20AnnotatedSharedMutexED2Ev Line | Count | Source | 130 | 15 | ~LockGuard() RELEASE() { | 131 | 15 | _mu.unlock(); | 132 | 15 | #ifdef BE_TEST | 133 | 15 | doris::mock_random_sleep(); | 134 | 15 | #endif | 135 | 15 | } |
|
136 | | |
137 | | LockGuard(const LockGuard&) = delete; |
138 | | LockGuard& operator=(const LockGuard&) = delete; |
139 | | |
140 | | private: |
141 | | MutexType& _mu; |
142 | | }; |
143 | | |
144 | | // RAII scoped shared lock guard annotated for thread safety analysis. |
145 | | // In BE_TEST builds, injects a random sleep before acquiring and after |
146 | | // releasing the lock to exercise concurrent code paths. |
147 | | template <typename MutexType> |
148 | | class SCOPED_CAPABILITY SharedLockGuard { |
149 | | public: |
150 | 14 | explicit SharedLockGuard(MutexType& mu) ACQUIRE_SHARED(mu) : _mu(mu) { |
151 | 14 | #ifdef BE_TEST |
152 | 14 | doris::mock_random_sleep(); |
153 | 14 | #endif |
154 | 14 | _mu.lock_shared(); |
155 | 14 | } |
156 | 14 | ~SharedLockGuard() RELEASE() { |
157 | 14 | _mu.unlock_shared(); |
158 | 14 | #ifdef BE_TEST |
159 | 14 | doris::mock_random_sleep(); |
160 | 14 | #endif |
161 | 14 | } |
162 | | |
163 | | SharedLockGuard(const SharedLockGuard&) = delete; |
164 | | SharedLockGuard& operator=(const SharedLockGuard&) = delete; |
165 | | |
166 | | private: |
167 | | MutexType& _mu; |
168 | | }; |
169 | | |
170 | | // RAII unique lock annotated for thread safety analysis. |
171 | | // Supports manual lock/unlock while preserving capability tracking. |
172 | | template <typename MutexType> |
173 | | class SCOPED_CAPABILITY UniqueLock { |
174 | | public: |
175 | 336 | explicit UniqueLock(MutexType& mu) ACQUIRE(mu) : _mu(&mu), _locked(true) { |
176 | 336 | #ifdef BE_TEST |
177 | 336 | doris::mock_random_sleep(); |
178 | 336 | #endif |
179 | 336 | _mu->lock(); |
180 | 336 | } |
181 | | |
182 | | UniqueLock(MutexType& mu, std::adopt_lock_t) REQUIRES(mu) : _mu(&mu), _locked(true) {} |
183 | | |
184 | | UniqueLock(MutexType& mu, std::defer_lock_t) EXCLUDES(mu) : _mu(&mu), _locked(false) {} |
185 | | |
186 | 336 | ~UniqueLock() RELEASE() { |
187 | 336 | if (_locked) { |
188 | 314 | _mu->unlock(); |
189 | 314 | #ifdef BE_TEST |
190 | 314 | doris::mock_random_sleep(); |
191 | 314 | #endif |
192 | 314 | } |
193 | 336 | } |
194 | | |
195 | | void lock() ACQUIRE() { |
196 | | #ifdef BE_TEST |
197 | | doris::mock_random_sleep(); |
198 | | #endif |
199 | | _mu->lock(); |
200 | | _locked = true; |
201 | | } |
202 | | |
203 | 22 | void unlock() RELEASE() { |
204 | 22 | _mu->unlock(); |
205 | 22 | _locked = false; |
206 | 22 | #ifdef BE_TEST |
207 | 22 | doris::mock_random_sleep(); |
208 | 22 | #endif |
209 | 22 | } |
210 | | |
211 | | bool owns_lock() const { return _locked; } |
212 | | |
213 | | UniqueLock(const UniqueLock&) = delete; |
214 | | UniqueLock& operator=(const UniqueLock&) = delete; |
215 | | |
216 | | private: |
217 | | MutexType* _mu; |
218 | | bool _locked; |
219 | | }; |