/src/brpc/src/bthread/mutex.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 | | // bthread - An M:N threading library to make applications more concurrent. |
19 | | |
20 | | // Date: 2015/12/14 18:17:04 |
21 | | |
22 | | #ifndef BTHREAD_MUTEX_H |
23 | | #define BTHREAD_MUTEX_H |
24 | | |
25 | | #include "bthread/types.h" |
26 | | #include "butil/scoped_lock.h" |
27 | | #include "bvar/utils/lock_timer.h" |
28 | | |
29 | | __BEGIN_DECLS |
30 | | extern int bthread_mutex_init(bthread_mutex_t* __restrict mutex, |
31 | | const bthread_mutexattr_t* __restrict attr); |
32 | | extern int bthread_mutex_destroy(bthread_mutex_t* mutex); |
33 | | extern int bthread_mutex_trylock(bthread_mutex_t* mutex); |
34 | | extern int bthread_mutex_lock(bthread_mutex_t* mutex); |
35 | | extern int bthread_mutex_timedlock(bthread_mutex_t* __restrict mutex, |
36 | | const struct timespec* __restrict abstime); |
37 | | extern int bthread_mutex_unlock(bthread_mutex_t* mutex); |
38 | | extern bthread_t bthread_self(void); |
39 | | __END_DECLS |
40 | | |
41 | | namespace bthread { |
42 | | |
43 | | // The C++ Wrapper of bthread_mutex |
44 | | |
45 | | // NOTE: Not aligned to cacheline as the container of Mutex is practically aligned |
46 | | class Mutex { |
47 | | public: |
48 | | typedef bthread_mutex_t* native_handler_type; |
49 | 0 | Mutex() { |
50 | 0 | int ec = bthread_mutex_init(&_mutex, NULL); |
51 | 0 | if (ec != 0) { |
52 | 0 | throw std::system_error(std::error_code(ec, std::system_category()), |
53 | 0 | "Mutex constructor failed"); |
54 | 0 | } |
55 | 0 | } |
56 | 0 | ~Mutex() { CHECK_EQ(0, bthread_mutex_destroy(&_mutex)); } |
57 | 0 | native_handler_type native_handler() { return &_mutex; } |
58 | 0 | void lock() { |
59 | 0 | int ec = bthread_mutex_lock(&_mutex); |
60 | 0 | if (ec != 0) { |
61 | 0 | throw std::system_error(std::error_code(ec, std::system_category()), |
62 | 0 | "Mutex lock failed"); |
63 | 0 | } |
64 | 0 | } |
65 | 0 | void unlock() { bthread_mutex_unlock(&_mutex); } |
66 | 0 | bool try_lock() { return 0 == bthread_mutex_trylock(&_mutex); } |
67 | 0 | bool timed_lock(const struct timespec* abstime) { |
68 | 0 | return !bthread_mutex_timedlock(&_mutex, abstime); |
69 | 0 | } |
70 | | // TODO(chenzhangyi01): Complement interfaces for C++11 |
71 | | private: |
72 | | DISALLOW_COPY_AND_ASSIGN(Mutex); |
73 | | bthread_mutex_t _mutex; |
74 | | }; |
75 | | |
76 | | namespace internal { |
77 | | #ifdef BTHREAD_USE_FAST_PTHREAD_MUTEX |
78 | | class FastPthreadMutex { |
79 | | public: |
80 | | FastPthreadMutex(); |
81 | | void lock(); |
82 | | void unlock(); |
83 | | bool try_lock(); |
84 | | bool timed_lock(const struct timespec* abstime); |
85 | | private: |
86 | | DISALLOW_COPY_AND_ASSIGN(FastPthreadMutex); |
87 | | int lock_contended(const struct timespec* abstime); |
88 | | |
89 | | unsigned _futex; |
90 | | // Note: Owner detection of the mutex comes with average execution |
91 | | // slowdown of about 50%., so it is only used for debugging and is |
92 | | // only available when the macro `BRPC_DEBUG_LOCK' = 1. |
93 | | mutex_owner_t _owner; |
94 | | }; |
95 | | #else |
96 | | typedef butil::Mutex FastPthreadMutex; |
97 | | #endif |
98 | | } |
99 | | |
100 | | class FastPthreadMutex { |
101 | | public: |
102 | 0 | FastPthreadMutex() = default; |
103 | | ~FastPthreadMutex() = default; |
104 | | DISALLOW_COPY_AND_ASSIGN(FastPthreadMutex); |
105 | | |
106 | | void lock(); |
107 | | void unlock(); |
108 | 0 | bool try_lock() { return _mutex.try_lock(); } |
109 | | #if defined(BTHREAD_USE_FAST_PTHREAD_MUTEX) || HAS_PTHREAD_MUTEX_TIMEDLOCK |
110 | | bool timed_lock(const struct timespec* abstime); |
111 | | #endif // BTHREAD_USE_FAST_PTHREAD_MUTEX HAS_PTHREAD_MUTEX_TIMEDLOCK |
112 | | |
113 | | private: |
114 | | internal::FastPthreadMutex _mutex; |
115 | | }; |
116 | | |
117 | | } // namespace bthread |
118 | | |
119 | | // Specialize std::lock_guard and std::unique_lock for bthread_mutex_t |
120 | | |
121 | | namespace std { |
122 | | |
123 | | template <> class lock_guard<bthread_mutex_t> { |
124 | | public: |
125 | 0 | explicit lock_guard(bthread_mutex_t& mutex) : _pmutex(&mutex) { |
126 | 0 | #if !defined(NDEBUG) |
127 | 0 | const int rc = bthread_mutex_lock(_pmutex); |
128 | 0 | if (rc) { |
129 | 0 | LOG(FATAL) << "Fail to lock bthread_mutex_t=" << _pmutex << ", " << berror(rc); |
130 | 0 | _pmutex = NULL; |
131 | 0 | } |
132 | | #else |
133 | | bthread_mutex_lock(_pmutex); |
134 | | #endif // NDEBUG |
135 | 0 | } |
136 | | |
137 | 0 | ~lock_guard() { |
138 | 0 | #ifndef NDEBUG |
139 | 0 | if (_pmutex) { |
140 | 0 | bthread_mutex_unlock(_pmutex); |
141 | 0 | } |
142 | | #else |
143 | | bthread_mutex_unlock(_pmutex); |
144 | | #endif |
145 | 0 | } |
146 | | |
147 | | private: |
148 | | DISALLOW_COPY_AND_ASSIGN(lock_guard); |
149 | | bthread_mutex_t* _pmutex; |
150 | | }; |
151 | | |
152 | | template <> class unique_lock<bthread_mutex_t> { |
153 | | DISALLOW_COPY_AND_ASSIGN(unique_lock); |
154 | | public: |
155 | | typedef bthread_mutex_t mutex_type; |
156 | 0 | unique_lock() : _mutex(NULL), _owns_lock(false) {} |
157 | | explicit unique_lock(mutex_type& mutex) |
158 | 0 | : _mutex(&mutex), _owns_lock(false) { |
159 | 0 | lock(); |
160 | 0 | } |
161 | | unique_lock(mutex_type& mutex, defer_lock_t) |
162 | | : _mutex(&mutex), _owns_lock(false) |
163 | 0 | {} |
164 | | unique_lock(mutex_type& mutex, try_to_lock_t) |
165 | | : _mutex(&mutex), _owns_lock(bthread_mutex_trylock(&mutex) == 0) |
166 | 0 | {} |
167 | | unique_lock(mutex_type& mutex, adopt_lock_t) |
168 | | : _mutex(&mutex), _owns_lock(true) |
169 | 0 | {} |
170 | | |
171 | 0 | ~unique_lock() { |
172 | 0 | if (_owns_lock) { |
173 | 0 | unlock(); |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | 0 | void lock() { |
178 | 0 | if (!_mutex) { |
179 | 0 | CHECK(false) << "Invalid operation"; |
180 | 0 | return; |
181 | 0 | } |
182 | 0 | if (_owns_lock) { |
183 | 0 | CHECK(false) << "Detected deadlock issue"; |
184 | 0 | return; |
185 | 0 | } |
186 | 0 | bthread_mutex_lock(_mutex); |
187 | 0 | _owns_lock = true; |
188 | 0 | } |
189 | | |
190 | 0 | bool try_lock() { |
191 | 0 | if (!_mutex) { |
192 | 0 | CHECK(false) << "Invalid operation"; |
193 | 0 | return false; |
194 | 0 | } |
195 | 0 | if (_owns_lock) { |
196 | 0 | CHECK(false) << "Detected deadlock issue"; |
197 | 0 | return false; |
198 | 0 | } |
199 | 0 | _owns_lock = !bthread_mutex_trylock(_mutex); |
200 | 0 | return _owns_lock; |
201 | 0 | } |
202 | | |
203 | 0 | void unlock() { |
204 | 0 | if (!_owns_lock) { |
205 | 0 | CHECK(false) << "Invalid operation"; |
206 | 0 | return; |
207 | 0 | } |
208 | 0 | if (_mutex) { |
209 | 0 | bthread_mutex_unlock(_mutex); |
210 | 0 | _owns_lock = false; |
211 | 0 | } |
212 | 0 | } |
213 | | |
214 | 0 | void swap(unique_lock& rhs) { |
215 | 0 | std::swap(_mutex, rhs._mutex); |
216 | 0 | std::swap(_owns_lock, rhs._owns_lock); |
217 | 0 | } |
218 | | |
219 | 0 | mutex_type* release() { |
220 | 0 | mutex_type* saved_mutex = _mutex; |
221 | 0 | _mutex = NULL; |
222 | 0 | _owns_lock = false; |
223 | 0 | return saved_mutex; |
224 | 0 | } |
225 | | |
226 | 0 | mutex_type* mutex() { return _mutex; } |
227 | 0 | bool owns_lock() const { return _owns_lock; } |
228 | 0 | operator bool() const { return owns_lock(); } |
229 | | |
230 | | private: |
231 | | mutex_type* _mutex; |
232 | | bool _owns_lock; |
233 | | }; |
234 | | |
235 | | } // namespace std |
236 | | |
237 | | namespace bvar { |
238 | | |
239 | | template <> |
240 | | struct MutexConstructor<bthread_mutex_t> { |
241 | 0 | bool operator()(bthread_mutex_t* mutex) const { |
242 | 0 | return bthread_mutex_init(mutex, NULL) == 0; |
243 | 0 | } |
244 | | }; |
245 | | |
246 | | template <> |
247 | | struct MutexDestructor<bthread_mutex_t> { |
248 | 0 | bool operator()(bthread_mutex_t* mutex) const { |
249 | 0 | return bthread_mutex_destroy(mutex) == 0; |
250 | 0 | } |
251 | | }; |
252 | | |
253 | | } // namespace bvar |
254 | | |
255 | | #endif //BTHREAD_MUTEX_H |