1 #ifndef UTIL_LOCK_VECTOR__HPP
2 #define UTIL_LOCK_VECTOR__HPP
3
4 /* $Id: lock_vector.hpp 112045 2007-10-10 20:43:07Z ivanovp $
5 * ===========================================================================
6 *
7 * PUBLIC DOMAIN NOTICE
8 * National Center for Biotechnology Information
9 *
10 * This software/database is a "United States Government Work" under the
11 * terms of the United States Copyright Act. It was written as part of
12 * the author's official duties as a United States Government employee and
13 * thus cannot be copyrighted. This software/database is freely available
14 * to the public for use. The National Library of Medicine and the U.S.
15 * Government have not placed any restriction on its use or reproduction.
16 *
17 * Although all reasonable efforts have been taken to ensure the accuracy
18 * and reliability of the software and data, the NLM and the U.S.
19 * Government do not and cannot warrant the performance or results that
20 * may be obtained by using this software or data. The NLM and the U.S.
21 * Government disclaim all warranties, express or implied, including
22 * warranties of performance, merchantability or fitness for any particular
23 * purpose.
24 *
25 * Please cite the author in any work or product based on this material.
26 *
27 * ===========================================================================
28 *
29 * Authors: Anatoliy Kuznetsov
30 *
31 * File Description: Lock vector
32 *
33 */
34
35 #include <corelib/ncbimisc.hpp>
36 #include <corelib/ncbimtx.hpp>
37 #include <corelib/ncbi_system.hpp>
38
39 #include <util/bitset/ncbi_bitset.hpp>
40 #include <util/error_codes.hpp>
41
42
43 BEGIN_NCBI_SCOPE
44
45 /** @addtogroup Threads
46 *
47 * @{
48 */
49
50
51 /// Class implements bit-vector based lock storage registering millions
52 /// of int numbered objects.
53 ///
54 /// @sa CLockVectorGuard
55 ///
56 template <class BV>
57 class CLockVector
58 {
59 public:
60 typedef BV TBitVector;
61 public:
62 CLockVector();
63 ~CLockVector();
64
65 /// Try to acquire lock of specified id
66 ///
67 /// @return TRUE if lock was issued,
68 /// FALSE if object has already being locked.
69 ///
70 bool TryLock(unsigned id);
71
72 /// Unlock object.
73 /// @return TRUE if object unlocked successfully,
74 /// FALSE if it was not locked
75 ///
76 bool Unlock(unsigned id);
77
78 /// Check if id is locked or not
79 bool IsLocked(unsigned id) const;
80
81
82 /// Reclaim unused memory
83 void FreeUnusedMem();
84
85 private:
86 CLockVector(const CLockVector<BV>&);
87 CLockVector& operator=(const CLockVector<BV>&);
88 protected:
89 TBitVector m_IdVector; ///< vector of locked objs
90 mutable CFastMutex m_IdVector_Lock; ///< lock for m_LockVector
91 };
92
93 /// Lock guard for the CLockVector
94 ///
95 /// @sa CLockVector
96 ///
97 template<class TLockVect>
98 class CLockVectorGuard
99 {
100 public:
101 typedef TLockVect TLockVector;
102 public:
103
104 /// Construct without locking
105 CLockVectorGuard(TLockVector& lvect,
106 unsigned timeout_ms);
107
108 /// Construct and lock
109 ///
110 /// @param lvect
111 /// Lock vector storing all locks
112 /// @param id
113 /// Object Id we are locking
114 /// @param timeout_ms
115 /// Timeout in milliseconds for how long lock makes attempts to
116 /// acquire the id. If cannot lock it throws an exception.
117 ///
118 CLockVectorGuard(TLockVector& lvect,
119 unsigned id,
120 unsigned timeout_ms);
121
122
123 ~CLockVectorGuard();
124
125 /// Acquire lock
126 void Lock(unsigned id);
127
128 /// Unlocks the lock
129 void Unlock();
130
131 /// Forger lock
Release()132 void Release() { m_LockSet = false; }
133
134 /// Get BLOB id
GetId() const135 unsigned GetId() const { return m_Id; }
136
137 /// Assign Id (no locking)
SetId(unsigned id)138 void SetId(unsigned id)
139 {
140 _ASSERT(m_Id == 0);
141 _ASSERT(id);
142
143 m_Id = id;
144 }
145
146 /// Transfer lock ownership from another lock
147 /// @param lg
148 /// Old lock guard (lock src)
149 ///
150 void TakeFrom(CLockVectorGuard& lg);
151
152 /// Lock acquisition
153 void DoLock();
154
GetLockVector() const155 TLockVector& GetLockVector() const {return *m_LockVector; }
GetTimeout() const156 unsigned GetTimeout() const { return m_Timeout; }
157
158 private:
159 CLockVectorGuard(const CLockVectorGuard<TLockVect>&);
160 CLockVectorGuard<TLockVect>& operator=(const CLockVectorGuard<TLockVect>&);
161 private:
162 TLockVector* m_LockVector;
163 unsigned m_Id;
164 unsigned m_Timeout;
165 unsigned m_Spins;
166 bool m_LockSet;
167 };
168
169 template<class TLockVect>
CLockVectorGuard(TLockVector & lvect,unsigned timeout_ms)170 CLockVectorGuard<TLockVect>::CLockVectorGuard(TLockVector& lvect,
171 unsigned timeout_ms)
172 : m_LockVector(&lvect),
173 m_Id(0),
174 m_Timeout(timeout_ms),
175 m_Spins(200),
176 m_LockSet(false)
177 {
178 }
179
180
181 template<class TLockVect>
CLockVectorGuard(TLockVector & lvect,unsigned id,unsigned timeout_ms)182 CLockVectorGuard<TLockVect>::CLockVectorGuard(TLockVector& lvect,
183 unsigned id,
184 unsigned timeout_ms)
185 : m_LockVector(&lvect),
186 m_Id(id),
187 m_Timeout(timeout_ms),
188 m_Spins(200),
189 m_LockSet(false)
190 {
191 _ASSERT(id);
192 DoLock();
193 }
194
195 template<class TLockVect>
~CLockVectorGuard()196 CLockVectorGuard<TLockVect>::~CLockVectorGuard()
197 {
198 try {
199 Unlock();
200 }
201 catch (exception& ex)
202 {
203 ERR_POST_XX(Util_LVector, 1, ex.what());
204 }
205 }
206
207 template<class TLockVect>
DoLock()208 void CLockVectorGuard<TLockVect>::DoLock()
209 {
210 _ASSERT(m_LockSet == false);
211
212 // Strategy implemented here is spin-and-lock
213 // works fine if rate of contention is relatively low
214 // in the future needs to be changed so lock vector returns semaphor to
215 // wait until requested id is free
216 //
217 for (unsigned i = 0; i < m_Spins; ++i) {
218 m_LockSet = m_LockVector->TryLock(m_Id);
219 if (m_LockSet) {
220 return;
221 }
222 } // for
223
224 // plain spin lock did not work -- try to lock it gracefully
225 //
226 unsigned sleep_ms = 10;
227 unsigned time_spent = 0;
228 while (true) {
229 m_LockSet = m_LockVector->TryLock(m_Id);
230 if (m_LockSet) {
231 return;
232 }
233 SleepMilliSec(sleep_ms);
234 if (m_Timeout) {
235 time_spent += sleep_ms;
236 if (time_spent > m_Timeout) {
237 string msg = "Lock vector timeout error on object id="
238 + NStr::UIntToString(m_Id);
239 NCBI_THROW(CMutexException, eTryLock, msg);
240 }
241 }
242 } // while
243 }
244
245
246 template<class TLockVect>
Lock(unsigned id)247 void CLockVectorGuard<TLockVect>::Lock(unsigned id)
248 {
249 Unlock();
250 m_Id = id;
251 DoLock();
252 }
253
254 template<class TLockVect>
Unlock()255 void CLockVectorGuard<TLockVect>::Unlock()
256 {
257 if (!m_LockSet) {
258 return;
259 }
260 bool unlocked = m_LockVector->Unlock(m_Id);
261 _ASSERT(unlocked);
262 if (!unlocked) {
263 string msg =
264 "Double unlock on object id=" + NStr::UIntToString(m_Id);
265 NCBI_THROW(CMutexException, eTryLock, msg);
266 }
267 m_LockSet = false;
268 }
269
270 template<class BV>
TakeFrom(CLockVectorGuard & lg)271 void CLockVectorGuard<BV>::TakeFrom(CLockVectorGuard& lg)
272 {
273 Unlock();
274 m_LockVector = lg.m_LockVector; m_Id = lg.m_Id;
275 m_LockSet = true;
276 lg.Release();
277 }
278
279
280 template<class BV>
CLockVector()281 CLockVector<BV>::CLockVector()
282 : m_IdVector(bm::BM_GAP)
283 {
284 }
285
286 template<class BV>
~CLockVector()287 CLockVector<BV>::~CLockVector()
288 {
289 if (m_IdVector.any()) {
290 ERR_POST_XX(Util_LVector, 2,
291 "::~CLockVector() detected live locks on destruction.");
292 }
293 }
294
295 template<class BV>
TryLock(unsigned id)296 bool CLockVector<BV>::TryLock(unsigned id)
297 {
298 CFastMutexGuard guard(m_IdVector_Lock);
299 bool is_set = m_IdVector.set_bit_conditional(id, true, false);
300 return is_set;
301 }
302
303 template<class BV>
Unlock(unsigned id)304 bool CLockVector<BV>::Unlock(unsigned id)
305 {
306 CFastMutexGuard guard(m_IdVector_Lock);
307 bool is_set = m_IdVector.set_bit_conditional(id, false, true);
308 if (!is_set) {
309 _ASSERT(m_IdVector[id] == false);
310 }
311 return is_set;
312 }
313
314 template<class BV>
FreeUnusedMem()315 void CLockVector<BV>::FreeUnusedMem()
316 {
317 CFastMutexGuard guard(m_IdVector_Lock);
318 m_IdVector.optimize(0, TBitVector::opt_free_0);
319 }
320
321 template<class BV>
IsLocked(unsigned id) const322 bool CLockVector<BV>::IsLocked(unsigned id) const
323 {
324 CFastMutexGuard guard(m_IdVector_Lock);
325 return m_IdVector[id];
326 }
327
328
329 /* @} */
330
331 END_NCBI_SCOPE
332
333 #endif
334