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
23namespace base {
24
25RWLock::RWLock()
26 : m_write_lock(false)
27 , m_read_locks(0)
28 , m_weak_lock(nullptr)
29{
30}
31
32RWLock::~RWLock()
33{
34 ASSERT(!m_write_lock);
35 ASSERT(m_read_locks == 0);
36 ASSERT(m_weak_lock == nullptr);
37}
38
39bool 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
48bool 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
116void 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
127void 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
142bool 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
155void 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
169bool 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