Coverage Report

Created: 2026-05-19 15:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
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
};