/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 |