1 | /** |
2 | * Copyright 2021 Alibaba, Inc. and its affiliates. All Rights Reserved. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | |
16 | * \author Hechong.xyf |
17 | * \date Jan 2018 |
18 | * \brief Interface of AiLego Utility Lock |
19 | */ |
20 | |
21 | #ifndef __AILEGO_PARALLEL_LOCK_H__ |
22 | #define __AILEGO_PARALLEL_LOCK_H__ |
23 | |
24 | #include <atomic> |
25 | #include <condition_variable> |
26 | #include <mutex> |
27 | #include <ailego/internal/platform.h> |
28 | |
29 | namespace ailego { |
30 | |
31 | // Test if atomic_bool is always lock free. |
32 | // Arm may be always lock free using some compiler flags, |
33 | // see https://stackoverflow.com/a/64253858/486350. |
34 | #if ATOMIC_BOOL_LOCK_FREE == 2 |
35 | |
36 | /*! Spin Mutex (The atomic type is always lock-free) |
37 | */ |
38 | class SpinMutex { |
39 | public: |
40 | //! Constructor |
41 | SpinMutex(void) {} |
42 | |
43 | //! Locking |
44 | void lock(void) { |
45 | bool expected = false; |
46 | while (!flag_.compare_exchange_weak( |
47 | expected, true, std::memory_order_acquire, std::memory_order_relaxed)) { |
48 | expected = false; |
49 | // Provide a hint to the processor that the code sequence is a spin-wait |
50 | // loop. This can help improve the performance and power consumption of |
51 | // spin-wait loops. |
52 | ailego_yield(); |
53 | } |
54 | } |
55 | |
56 | //! Try locking |
57 | bool try_lock(void) { |
58 | bool expected = false; |
59 | return flag_.compare_exchange_strong( |
60 | expected, true, std::memory_order_acquire, std::memory_order_relaxed); |
61 | } |
62 | |
63 | //! Unlocking |
64 | void unlock(void) { |
65 | flag_.store(false, std::memory_order_release); |
66 | } |
67 | |
68 | private: |
69 | //! Disable them |
70 | SpinMutex(const SpinMutex &) = delete; |
71 | SpinMutex(SpinMutex &&) = delete; |
72 | SpinMutex &operator=(const SpinMutex &) = delete; |
73 | SpinMutex &operator=(SpinMutex &&) = delete; |
74 | |
75 | //! Members |
76 | std::atomic_bool flag_{false}; |
77 | }; |
78 | #else |
79 | |
80 | /*! Spin Mutex (General) |
81 | */ |
82 | class SpinMutex { |
83 | public: |
84 | //! Constructor |
85 | SpinMutex(void) {} |
86 | |
87 | //! Locking |
88 | void lock(void) { |
89 | while (flag_.test_and_set(std::memory_order_acquire)) |
90 | ; |
91 | } |
92 | |
93 | //! Try locking |
94 | bool try_lock(void) { |
95 | return (!flag_.test_and_set(std::memory_order_acquire)); |
96 | } |
97 | |
98 | //! Unlocking |
99 | void unlock(void) { |
100 | flag_.clear(std::memory_order_release); |
101 | } |
102 | |
103 | private: |
104 | //! Disable them |
105 | SpinMutex(const SpinMutex &) = delete; |
106 | SpinMutex(SpinMutex &&) = delete; |
107 | SpinMutex &operator=(const SpinMutex &) = delete; |
108 | SpinMutex &operator=(SpinMutex &&) = delete; |
109 | |
110 | //! Members |
111 | std::atomic_flag flag_{}; |
112 | }; |
113 | #endif // ATOMIC_BOOL_LOCK_FREE == 2 |
114 | |
115 | /*! Shared Mutex |
116 | */ |
117 | class SharedMutex { |
118 | public: |
119 | //! Constructor |
120 | SharedMutex(void) {} |
121 | |
122 | //! Locking |
123 | void lock(void) { |
124 | std::unique_lock<std::mutex> q(mutex_); |
125 | ++write_count_; |
126 | write_cond_.wait(q, [this]() { return (pending_count_ == 0); }); |
127 | --write_count_; |
128 | --pending_count_; |
129 | } |
130 | |
131 | //! Try locking |
132 | bool try_lock(void) { |
133 | std::unique_lock<std::mutex> q(mutex_, std::defer_lock); |
134 | if (q.try_lock()) { |
135 | if (pending_count_ == 0) { |
136 | --pending_count_; |
137 | return true; |
138 | } |
139 | } |
140 | return false; |
141 | } |
142 | |
143 | //! Unlocking |
144 | void unlock(void) { |
145 | std::lock_guard<std::mutex> q(mutex_); |
146 | ++pending_count_; |
147 | |
148 | if (write_count_ != 0) { |
149 | write_cond_.notify_one(); |
150 | } else { |
151 | read_cond_.notify_all(); |
152 | } |
153 | } |
154 | |
155 | //! Locking (shared) |
156 | void lock_shared(void) { |
157 | std::unique_lock<std::mutex> q(mutex_); |
158 | ++read_count_; |
159 | read_cond_.wait( |
160 | q, [this]() { return (write_count_ == 0 && pending_count_ >= 0); }); |
161 | --read_count_; |
162 | ++pending_count_; |
163 | } |
164 | |
165 | //! Try locking (shared) |
166 | bool try_lock_shared(void) { |
167 | std::lock_guard<std::mutex> q(mutex_); |
168 | if (write_count_ == 0 && pending_count_ >= 0) { |
169 | ++pending_count_; |
170 | return true; |
171 | } |
172 | return false; |
173 | } |
174 | |
175 | //! Unlocking (shared) |
176 | void unlock_shared(void) { |
177 | std::lock_guard<std::mutex> q(mutex_); |
178 | --pending_count_; |
179 | |
180 | if (write_count_ != 0 && pending_count_ == 0) { |
181 | write_cond_.notify_one(); |
182 | } else { |
183 | read_cond_.notify_all(); |
184 | } |
185 | } |
186 | |
187 | private: |
188 | //! Disable them |
189 | SharedMutex(const SharedMutex &) = delete; |
190 | SharedMutex(SharedMutex &&) = delete; |
191 | SharedMutex &operator=(const SharedMutex &) = delete; |
192 | SharedMutex &operator=(SharedMutex &&) = delete; |
193 | |
194 | //! Members |
195 | int32_t pending_count_{0}; |
196 | int32_t read_count_{0}; |
197 | int32_t write_count_{0}; |
198 | std::mutex mutex_{}; |
199 | std::condition_variable read_cond_{}; |
200 | std::condition_variable write_cond_{}; |
201 | }; |
202 | |
203 | /*! Write Lock |
204 | */ |
205 | class WriteLock { |
206 | public: |
207 | //! Constructor |
208 | WriteLock(SharedMutex &mutex) : mutex_(mutex) {} |
209 | |
210 | //! Locking |
211 | void lock(void) { |
212 | mutex_.lock(); |
213 | } |
214 | |
215 | //! Try locking |
216 | bool try_lock(void) { |
217 | return mutex_.try_lock(); |
218 | } |
219 | |
220 | //! Unlocking |
221 | void unlock(void) { |
222 | mutex_.unlock(); |
223 | } |
224 | |
225 | private: |
226 | //! Disable them |
227 | WriteLock(void) = delete; |
228 | WriteLock(const WriteLock &) = delete; |
229 | WriteLock(WriteLock &&) = delete; |
230 | WriteLock &operator=(const WriteLock &) = delete; |
231 | WriteLock &operator=(WriteLock &&) = delete; |
232 | |
233 | //! Members |
234 | SharedMutex &mutex_; |
235 | }; |
236 | |
237 | /*! Read Lock |
238 | */ |
239 | class ReadLock { |
240 | public: |
241 | //! Constructor |
242 | ReadLock(SharedMutex &mutex) : mutex_(mutex) {} |
243 | |
244 | //! Locking |
245 | void lock(void) { |
246 | mutex_.lock_shared(); |
247 | } |
248 | |
249 | //! Try locking |
250 | bool try_lock(void) { |
251 | return mutex_.try_lock_shared(); |
252 | } |
253 | |
254 | //! Unlocking |
255 | void unlock(void) { |
256 | mutex_.unlock_shared(); |
257 | } |
258 | |
259 | private: |
260 | //! Disable them |
261 | ReadLock(void) = delete; |
262 | ReadLock(const ReadLock &) = delete; |
263 | ReadLock(ReadLock &&) = delete; |
264 | ReadLock &operator=(const ReadLock &) = delete; |
265 | ReadLock &operator=(ReadLock &&) = delete; |
266 | |
267 | //! Members |
268 | SharedMutex &mutex_; |
269 | }; |
270 | |
271 | } // namespace ailego |
272 | |
273 | #endif // __AILEGO_PARALLEL_LOCK_H__ |
274 | |