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