Coverage Report

Created: 2025-10-28 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/brpc/src/butil/scoped_lock.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
#ifndef BUTIL_BAIDU_SCOPED_LOCK_H
19
#define BUTIL_BAIDU_SCOPED_LOCK_H
20
21
#include "butil/build_config.h"
22
23
#if defined(BUTIL_CXX11_ENABLED)
24
#include <mutex>                           // std::lock_guard
25
#endif
26
27
#include "butil/synchronization/lock.h"
28
#include "butil/macros.h"
29
#include "butil/logging.h"
30
#include "butil/errno.h"
31
32
#if !defined(BUTIL_CXX11_ENABLED)
33
#define BAIDU_SCOPED_LOCK(ref_of_lock)                                  \
34
    std::lock_guard<BAIDU_TYPEOF(ref_of_lock)>                          \
35
    BAIDU_CONCAT(scoped_locker_dummy_at_line_, __LINE__)(ref_of_lock)
36
#else
37
38
// NOTE(gejun): c++11 deduces additional reference to the type.
39
namespace butil {
40
namespace detail {
41
template <typename T>
42
std::lock_guard<typename std::remove_reference<T>::type> get_lock_guard();
43
}  // namespace detail
44
}  // namespace butil
45
46
#define BAIDU_SCOPED_LOCK(ref_of_lock)                                  \
47
25
    decltype(::butil::detail::get_lock_guard<decltype(ref_of_lock)>()) \
48
25
    BAIDU_CONCAT(scoped_locker_dummy_at_line_, __LINE__)(ref_of_lock)
49
#endif
50
51
namespace std {
52
53
#if !defined(BUTIL_CXX11_ENABLED)
54
55
// Do not acquire ownership of the mutex
56
struct defer_lock_t {};
57
static const defer_lock_t defer_lock = {};
58
59
// Try to acquire ownership of the mutex without blocking 
60
struct try_to_lock_t {};
61
static const try_to_lock_t try_to_lock = {};
62
63
// Assume the calling thread already has ownership of the mutex 
64
struct adopt_lock_t {};
65
static const adopt_lock_t adopt_lock = {};
66
67
template <typename Mutex> class lock_guard {
68
public:
69
    explicit lock_guard(Mutex & mutex) : _pmutex(&mutex) { _pmutex->lock(); }
70
    ~lock_guard() { _pmutex->unlock(); }
71
private:
72
    DISALLOW_COPY_AND_ASSIGN(lock_guard);
73
    Mutex* _pmutex;
74
};
75
76
template <typename Mutex> class unique_lock {
77
    DISALLOW_COPY_AND_ASSIGN(unique_lock);
78
public:
79
    typedef Mutex mutex_type;
80
    unique_lock() : _mutex(NULL), _owns_lock(false) {}
81
    explicit unique_lock(mutex_type& mutex)
82
        : _mutex(&mutex), _owns_lock(true) {
83
        mutex.lock();
84
    }
85
    unique_lock(mutex_type& mutex, defer_lock_t)
86
        : _mutex(&mutex), _owns_lock(false)
87
    {}
88
    unique_lock(mutex_type& mutex, try_to_lock_t) 
89
        : _mutex(&mutex), _owns_lock(mutex.try_lock())
90
    {}
91
    unique_lock(mutex_type& mutex, adopt_lock_t) 
92
        : _mutex(&mutex), _owns_lock(true)
93
    {}
94
95
    ~unique_lock() {
96
        if (_owns_lock) {
97
            _mutex->unlock();
98
        }
99
    }
100
101
    void lock() {
102
        if (_owns_lock) {
103
            CHECK(false) << "Detected deadlock issue";     
104
            return;
105
        }
106
        _owns_lock = true;
107
        _mutex->lock();
108
    }
109
110
    bool try_lock() {
111
        if (_owns_lock) {
112
            CHECK(false) << "Detected deadlock issue";     
113
            return false;
114
        }
115
        _owns_lock = _mutex->try_lock();
116
        return _owns_lock;
117
    }
118
119
    void unlock() {
120
        if (!_owns_lock) {
121
            CHECK(false) << "Invalid operation";
122
            return;
123
        }
124
        _mutex->unlock();
125
        _owns_lock = false;
126
    }
127
128
    void swap(unique_lock& rhs) {
129
        std::swap(_mutex, rhs._mutex);
130
        std::swap(_owns_lock, rhs._owns_lock);
131
    }
132
133
    mutex_type* release() {
134
        mutex_type* saved_mutex = _mutex;
135
        _mutex = NULL;
136
        _owns_lock = false;
137
        return saved_mutex;
138
    }
139
140
    mutex_type* mutex() { return _mutex; }
141
    bool owns_lock() const { return _owns_lock; }
142
    operator bool() const { return owns_lock(); }
143
144
private:
145
    mutex_type*                     _mutex;
146
    bool                            _owns_lock;
147
};
148
149
#endif // !defined(BUTIL_CXX11_ENABLED)
150
151
#if defined(OS_POSIX)
152
153
template<> class lock_guard<pthread_mutex_t> {
154
public:
155
25
    explicit lock_guard(pthread_mutex_t & mutex) : _pmutex(&mutex) {
156
25
#if !defined(NDEBUG)
157
25
        const int rc = pthread_mutex_lock(_pmutex);
158
25
        if (rc) {
159
0
            LOG(FATAL) << "Fail to lock pthread_mutex_t=" << _pmutex << ", " << berror(rc);
160
0
            _pmutex = NULL;
161
0
        }
162
#else
163
        pthread_mutex_lock(_pmutex);
164
#endif  // NDEBUG
165
25
    }
166
    
167
25
    ~lock_guard() {
168
25
#ifndef NDEBUG
169
25
        if (_pmutex) {
170
25
            pthread_mutex_unlock(_pmutex);
171
25
        }
172
#else
173
        pthread_mutex_unlock(_pmutex);
174
#endif
175
25
    }
176
    
177
private:
178
    DISALLOW_COPY_AND_ASSIGN(lock_guard);
179
    pthread_mutex_t* _pmutex;
180
};
181
182
template<> class lock_guard<pthread_spinlock_t> {
183
public:
184
0
    explicit lock_guard(pthread_spinlock_t & spin) : _pspin(&spin) {
185
0
#if !defined(NDEBUG)
186
0
        const int rc = pthread_spin_lock(_pspin);
187
0
        if (rc) {
188
0
            LOG(FATAL) << "Fail to lock pthread_spinlock_t=" << _pspin << ", " << berror(rc);
189
0
            _pspin = NULL;
190
0
        }
191
#else
192
        pthread_spin_lock(_pspin);
193
#endif  // NDEBUG
194
0
    }
195
    
196
0
    ~lock_guard() {
197
0
#ifndef NDEBUG
198
0
        if (_pspin) {
199
0
            pthread_spin_unlock(_pspin);
200
0
        }
201
#else
202
        pthread_spin_unlock(_pspin);
203
#endif
204
0
    }
205
    
206
private:
207
    DISALLOW_COPY_AND_ASSIGN(lock_guard);
208
    pthread_spinlock_t* _pspin;
209
};
210
211
template<> class unique_lock<pthread_mutex_t> {
212
    DISALLOW_COPY_AND_ASSIGN(unique_lock);
213
public:
214
    typedef pthread_mutex_t         mutex_type;
215
0
    unique_lock() : _mutex(NULL), _owns_lock(false) {}
216
    explicit unique_lock(mutex_type& mutex)
217
0
        : _mutex(&mutex), _owns_lock(true) {
218
0
        pthread_mutex_lock(_mutex);
219
0
    }
220
    unique_lock(mutex_type& mutex, defer_lock_t)
221
        : _mutex(&mutex), _owns_lock(false)
222
0
    {}
223
    unique_lock(mutex_type& mutex, try_to_lock_t) 
224
        : _mutex(&mutex), _owns_lock(pthread_mutex_trylock(&mutex) == 0)
225
0
    {}
226
    unique_lock(mutex_type& mutex, adopt_lock_t) 
227
        : _mutex(&mutex), _owns_lock(true)
228
0
    {}
229
230
0
    ~unique_lock() {
231
0
        if (_owns_lock) {
232
0
            pthread_mutex_unlock(_mutex);
233
0
        }
234
0
    }
235
236
0
    void lock() {
237
0
        if (_owns_lock) {
238
0
            CHECK(false) << "Detected deadlock issue";     
239
0
            return;
240
0
        }
241
0
#if !defined(NDEBUG)
242
0
        const int rc = pthread_mutex_lock(_mutex);
243
0
        if (rc) {
244
0
            LOG(FATAL) << "Fail to lock pthread_mutex=" << _mutex << ", " << berror(rc);
245
0
            return;
246
0
        }
247
0
        _owns_lock = true;
248
#else
249
        _owns_lock = true;
250
        pthread_mutex_lock(_mutex);
251
#endif  // NDEBUG
252
0
    }
253
254
0
    bool try_lock() {
255
0
        if (_owns_lock) {
256
0
            CHECK(false) << "Detected deadlock issue";     
257
0
            return false;
258
0
        }
259
0
        _owns_lock = !pthread_mutex_trylock(_mutex);
260
0
        return _owns_lock;
261
0
    }
262
263
0
    void unlock() {
264
0
        if (!_owns_lock) {
265
0
            CHECK(false) << "Invalid operation";
266
0
            return;
267
0
        }
268
0
        pthread_mutex_unlock(_mutex);
269
0
        _owns_lock = false;
270
0
    }
271
272
0
    void swap(unique_lock& rhs) {
273
0
        std::swap(_mutex, rhs._mutex);
274
0
        std::swap(_owns_lock, rhs._owns_lock);
275
0
    }
276
277
0
    mutex_type* release() {
278
0
        mutex_type* saved_mutex = _mutex;
279
0
        _mutex = NULL;
280
0
        _owns_lock = false;
281
0
        return saved_mutex;
282
0
    }
283
284
0
    mutex_type* mutex() { return _mutex; }
285
0
    bool owns_lock() const { return _owns_lock; }
286
0
    operator bool() const { return owns_lock(); }
287
288
private:
289
    mutex_type*                     _mutex;
290
    bool                            _owns_lock;
291
};
292
293
template<> class unique_lock<pthread_spinlock_t> {
294
    DISALLOW_COPY_AND_ASSIGN(unique_lock);
295
public:
296
    typedef pthread_spinlock_t  mutex_type;
297
0
    unique_lock() : _mutex(NULL), _owns_lock(false) {}
298
    explicit unique_lock(mutex_type& mutex)
299
0
        : _mutex(&mutex), _owns_lock(true) {
300
0
        pthread_spin_lock(_mutex);
301
0
    }
302
303
0
    ~unique_lock() {
304
0
        if (_owns_lock) {
305
0
            pthread_spin_unlock(_mutex);
306
0
        }
307
0
    }
308
    unique_lock(mutex_type& mutex, defer_lock_t)
309
        : _mutex(&mutex), _owns_lock(false)
310
0
    {}
311
    unique_lock(mutex_type& mutex, try_to_lock_t) 
312
        : _mutex(&mutex), _owns_lock(pthread_spin_trylock(&mutex) == 0)
313
0
    {}
314
    unique_lock(mutex_type& mutex, adopt_lock_t) 
315
        : _mutex(&mutex), _owns_lock(true)
316
0
    {}
317
318
0
    void lock() {
319
0
        if (_owns_lock) {
320
0
            CHECK(false) << "Detected deadlock issue";     
321
0
            return;
322
0
        }
323
0
#if !defined(NDEBUG)
324
0
        const int rc = pthread_spin_lock(_mutex);
325
0
        if (rc) {
326
0
            LOG(FATAL) << "Fail to lock pthread_spinlock=" << _mutex << ", " << berror(rc);
327
0
            return;
328
0
        }
329
0
        _owns_lock = true;
330
0
#else
331
0
        _owns_lock = true;
332
0
        pthread_spin_lock(_mutex);
333
0
#endif  // NDEBUG
334
0
    }
335
336
0
    bool try_lock() {
337
0
        if (_owns_lock) {
338
0
            CHECK(false) << "Detected deadlock issue";     
339
0
            return false;
340
0
        }
341
0
        _owns_lock = !pthread_spin_trylock(_mutex);
342
0
        return _owns_lock;
343
0
    }
344
345
0
    void unlock() {
346
0
        if (!_owns_lock) {
347
0
            CHECK(false) << "Invalid operation";
348
0
            return;
349
0
        }
350
0
        pthread_spin_unlock(_mutex);
351
0
        _owns_lock = false;
352
0
    }
353
354
0
    void swap(unique_lock& rhs) {
355
0
        std::swap(_mutex, rhs._mutex);
356
0
        std::swap(_owns_lock, rhs._owns_lock);
357
0
    }
358
359
0
    mutex_type* release() {
360
0
        mutex_type* saved_mutex = _mutex;
361
0
        _mutex = NULL;
362
0
        _owns_lock = false;
363
0
        return saved_mutex;
364
0
    }
365
366
0
    mutex_type* mutex() { return _mutex; }
367
0
    bool owns_lock() const { return _owns_lock; }
368
0
    operator bool() const { return owns_lock(); }
369
370
private:
371
    mutex_type*                     _mutex;
372
    bool                            _owns_lock;
373
};
374
375
#endif  // defined(OS_POSIX)
376
377
}  // namespace std
378
379
namespace butil {
380
381
// Lock both lck1 and lck2 without the deadlock issue.
382
template <typename Mutex1, typename Mutex2>
383
0
void double_lock(std::unique_lock<Mutex1> &lck1, std::unique_lock<Mutex2> &lck2) {
384
0
    DCHECK(!lck1.owns_lock());
385
0
    DCHECK(!lck2.owns_lock());
386
0
    volatile void* const ptr1 = lck1.mutex();
387
0
    volatile void* const ptr2 = lck2.mutex();
388
0
    DCHECK_NE(ptr1, ptr2);
389
0
    if (ptr1 < ptr2) {
390
0
        lck1.lock();
391
0
        lck2.lock();
392
0
    } else {
393
0
        lck2.lock();
394
0
        lck1.lock();
395
0
    }
396
0
}
397
398
};
399
400
#endif  // BUTIL_BAIDU_SCOPED_LOCK_H