1 | // LAF Base Library |
2 | // Copyright (C) 2020-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2016 David Capello |
4 | // |
5 | // This file is released under the terms of the MIT license. |
6 | // Read LICENSE.txt for more information. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "base/rw_lock.h" |
13 | |
14 | // Uncomment this line in case that you want TRACE() lock/unlock |
15 | // operations. |
16 | //#define DEBUG_OBJECT_LOCKS |
17 | |
18 | #include "base/debug.h" |
19 | #include "base/thread.h" |
20 | |
21 | #include <algorithm> |
22 | |
23 | namespace base { |
24 | |
25 | RWLock::RWLock() |
26 | : m_write_lock(false) |
27 | , m_read_locks(0) |
28 | , m_weak_lock(nullptr) |
29 | { |
30 | } |
31 | |
32 | RWLock::~RWLock() |
33 | { |
34 | ASSERT(!m_write_lock); |
35 | ASSERT(m_read_locks == 0); |
36 | ASSERT(m_weak_lock == nullptr); |
37 | } |
38 | |
39 | bool RWLock::canWriteLockFromRead() const |
40 | { |
41 | std::lock_guard lock(m_mutex); |
42 | |
43 | // If only we are reading (one lock) and nobody is writting, we can |
44 | // lock for writting.. |
45 | return (m_read_locks == 1 && !m_write_lock); |
46 | } |
47 | |
48 | bool RWLock::lock(LockType lockType, int timeout) |
49 | { |
50 | while (timeout >= 0) { |
51 | { |
52 | std::lock_guard lock(m_mutex); |
53 | |
54 | switch (lockType) { |
55 | |
56 | case ReadLock: |
57 | // If no body is writting the object... |
58 | if (!m_write_lock) { |
59 | // We can read it |
60 | ++m_read_locks; |
61 | return true; |
62 | } |
63 | break; |
64 | |
65 | case WriteLock: |
66 | // Check that there is no weak lock |
67 | if (m_weak_lock) { |
68 | if (*m_weak_lock == WeakLocked) |
69 | *m_weak_lock = WeakUnlocking; |
70 | |
71 | if (*m_weak_lock == WeakUnlocking) |
72 | goto go_wait; |
73 | |
74 | ASSERT(*m_weak_lock == WeakUnlocked); |
75 | } |
76 | |
77 | // If no body is reading and writting... |
78 | if (m_read_locks == 0 && !m_write_lock) { |
79 | // We can start writting the object... |
80 | m_write_lock = true; |
81 | |
82 | #ifdef DEBUG_OBJECT_LOCKS |
83 | TRACE("LCK: lock: Locked <%p> to write\n" , this); |
84 | #endif |
85 | return true; |
86 | } |
87 | break; |
88 | |
89 | } |
90 | |
91 | go_wait:; |
92 | } |
93 | |
94 | if (timeout > 0) { |
95 | int delay = std::min(100, timeout); |
96 | timeout -= delay; |
97 | |
98 | #ifdef DEBUG_OBJECT_LOCKS |
99 | TRACE("LCK: lock: wait 100 msecs for <%p>\n" , this); |
100 | #endif |
101 | |
102 | base::this_thread::sleep_for(double(delay) / 1000.0); |
103 | } |
104 | else |
105 | break; |
106 | } |
107 | |
108 | #ifdef DEBUG_OBJECT_LOCKS |
109 | TRACE("LCK: lock: Cannot lock <%p> to %s (has %d read locks and %d write locks)\n" , |
110 | this, (lockType == ReadLock ? "read" : "write" ), m_read_locks, m_write_lock); |
111 | #endif |
112 | |
113 | return false; |
114 | } |
115 | |
116 | void RWLock::downgradeToRead() |
117 | { |
118 | std::lock_guard lock(m_mutex); |
119 | |
120 | ASSERT(m_read_locks == 0); |
121 | ASSERT(m_write_lock); |
122 | |
123 | m_write_lock = false; |
124 | m_read_locks = 1; |
125 | } |
126 | |
127 | void RWLock::unlock() |
128 | { |
129 | std::lock_guard lock(m_mutex); |
130 | |
131 | if (m_write_lock) { |
132 | m_write_lock = false; |
133 | } |
134 | else if (m_read_locks > 0) { |
135 | --m_read_locks; |
136 | } |
137 | else { |
138 | ASSERT(false); |
139 | } |
140 | } |
141 | |
142 | bool RWLock::weakLock(std::atomic<WeakLock>* weak_lock_flag) |
143 | { |
144 | std::lock_guard lock(m_mutex); |
145 | |
146 | if (m_weak_lock || |
147 | m_write_lock) |
148 | return false; |
149 | |
150 | m_weak_lock = weak_lock_flag; |
151 | *m_weak_lock = WeakLocked; |
152 | return true; |
153 | } |
154 | |
155 | void RWLock::weakUnlock() |
156 | { |
157 | std::lock_guard lock(m_mutex); |
158 | |
159 | ASSERT(m_weak_lock); |
160 | ASSERT(*m_weak_lock != WeakLock::WeakUnlocked); |
161 | ASSERT(!m_write_lock); |
162 | |
163 | if (m_weak_lock) { |
164 | *m_weak_lock = WeakLock::WeakUnlocked; |
165 | m_weak_lock = nullptr; |
166 | } |
167 | } |
168 | |
169 | bool RWLock::upgradeToWrite(int timeout) |
170 | { |
171 | while (timeout >= 0) { |
172 | { |
173 | std::lock_guard lock(m_mutex); |
174 | |
175 | // Check that there is no weak lock |
176 | if (m_weak_lock) { |
177 | if (*m_weak_lock == WeakLocked) |
178 | *m_weak_lock = WeakUnlocking; |
179 | |
180 | // Wait some time |
181 | if (*m_weak_lock == WeakUnlocking) |
182 | goto go_wait; |
183 | |
184 | ASSERT(*m_weak_lock == WeakUnlocked); |
185 | } |
186 | |
187 | // this only is possible if there are just one reader |
188 | if (m_read_locks == 1) { |
189 | ASSERT(!m_write_lock); |
190 | m_read_locks = 0; |
191 | m_write_lock = true; |
192 | |
193 | #ifdef DEBUG_OBJECT_LOCKS |
194 | TRACE("LCK: upgradeToWrite: Locked <%p> to write\n" , this); |
195 | #endif |
196 | |
197 | return true; |
198 | } |
199 | |
200 | go_wait:; |
201 | } |
202 | |
203 | if (timeout > 0) { |
204 | int delay = std::min(100, timeout); |
205 | timeout -= delay; |
206 | |
207 | #ifdef DEBUG_OBJECT_LOCKS |
208 | TRACE("LCK: upgradeToWrite: wait 100 msecs for <%p>\n" , this); |
209 | #endif |
210 | |
211 | base::this_thread::sleep_for(double(delay) / 1000.0); |
212 | } |
213 | else |
214 | break; |
215 | } |
216 | |
217 | #ifdef DEBUG_OBJECT_LOCKS |
218 | TRACE("LCK: upgradeToWrite: Cannot lock <%p> to write (has %d read locks and %d write locks)\n" , |
219 | this, m_read_locks, m_write_lock); |
220 | #endif |
221 | |
222 | return false; |
223 | } |
224 | |
225 | } // namespace base |
226 | |