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
29namespace 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 */
38class 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 */
82class 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 */
117class 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 */
205class 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 */
239class 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