NCBI C++ ToolKit
cache_with_lock.hpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 #ifndef CORELIB__CACHE_WITH_LOCK__HPP
2 #define CORELIB__CACHE_WITH_LOCK__HPP
3 /* $Id: cache_with_lock.hpp 100558 2023-08-10 21:32:49Z vasilche $
4  * ===========================================================================
5  *
6  * PUBLIC DOMAIN NOTICE
7  * National Center for Biotechnology Information
8  *
9  * This software/database is a "United States Government Work" under the
10  * terms of the United States Copyright Act. It was written as part of
11  * the author's official duties as a United States Government employee and
12  * thus cannot be copyrighted. This software/database is freely available
13  * to the public for use. The National Library of Medicine and the U.S.
14  * Government have not placed any restriction on its use or reproduction.
15  *
16  * Although all reasonable efforts have been taken to ensure the accuracy
17  * and reliability of the software and data, the NLM and the U.S.
18  * Government do not and cannot warrant the performance or results that
19  * may be obtained by using this software or data. The NLM and the U.S.
20  * Government disclaim all warranties, express or implied, including
21  * warranties of performance, merchantability or fitness for any particular
22  * purpose.
23  *
24  * Please cite the author in any work or product based on this material.
25  *
26  * ===========================================================================
27  *
28  * Authors: Eugene Vasilchenko
29  *
30  * File Description:
31  * Cache for loaded objects with lock to hold their destruction
32  *
33  */
34 
35 #include <corelib/ncbistd.hpp>
36 #include <util/range.hpp>
37 
39 
40 template<class Key, class Value, class Less = less<Key> >
41 class CCacheWithLock : public CObject
42 {
43 public:
44  typedef Key key_type;
45  typedef Value mapped_type;
46 
47 protected:
48  class CSlot;
49  typedef Less TLess;
51  typedef typename TMap::iterator TMapIterator;
53  typedef list<TMapIterator> TRemoveList;
54  typedef typename TRemoveList::iterator TRemoveListIterator;
55 
56  class CSlot : public CObject {
57  public:
58  CSlot() {
59  m_LockCounter.Set(1);
60  }
66  };
67 
68  TMap m_Map;
69  size_t m_SizeLimit;
70  size_t m_RemoveSize;
73 
74 public:
75  class CLock {
76  protected:
79  friend class CCacheWithLock<key_type, mapped_type, TLess>;
80 
81  CLock(CCacheWithLock* cache, CSlot* slot)
82  : m_Cache(cache),
83  m_Slot(slot)
84  {
85  _ASSERT(cache);
86  _ASSERT(slot->m_LockCounter.Get() > 0);
87  }
88 
89  public:
90  CLock() {
91  }
92  ~CLock() {
93  Reset();
94  }
95  CLock(const CLock& lock)
96  : m_Cache(lock.m_Cache),
97  m_Slot(lock.m_Slot)
98  {
99  if ( m_Slot ) {
100  m_Slot->m_LockCounter.Add(1);
101  }
102  }
103  CLock& operator=(const CLock& lock)
104  {
105  if ( m_Slot != lock.m_Slot ) {
106  if ( m_Slot ) {
108  }
109  m_Cache = lock.m_Cache;
110  m_Slot = lock.m_Slot;
111  if ( m_Slot ) {
112  m_Slot->m_LockCounter.Add(1);
113  }
114  }
115  return *this;
116  }
117  CLock(CLock&& lock)
118  : m_Cache(std::move(lock.m_Cache)),
119  m_Slot(std::move(lock.m_Slot))
120  {
121  }
123  {
124  if ( m_Slot != lock.m_Slot ) {
125  Reset();
126  m_Cache.Swap(lock.m_Cache);
127  m_Slot.Swap(lock.m_Slot);
128  }
129  return *this;
130  }
131 
133 
134  void Reset() {
135  if ( m_Slot ) {
137  m_Slot = null;
138  m_Cache = null;
139  }
140  }
141 
142  CFastMutex& GetValueMutex() { return m_Slot.GetNCObject().m_ValueMutex; }
143 
144  mapped_type& operator*() const { return m_Slot.GetNCObject().m_Value; }
145  mapped_type* operator->() const { return &m_Slot.GetNCObject().m_Value; }
146 
147  bool operator==(CLock a) const {
148  return m_Slot == a.m_Slot;
149  }
150  bool operator!=(CLock a) const {
151  return !(*this == a);
152  }
153  };
154 
155  CCacheWithLock(size_t size_limit = 0)
156  : m_SizeLimit(size_limit),
157  m_RemoveSize(0)
158  {
159  }
160 
162  CMutexGuard guard(m_Mutex);
164  if ( iter == m_Map.end() || m_Map.key_comp()(key, iter->first) ) {
165  // insert
166  typedef typename TMap::value_type TValue;
167  iter = m_Map.insert(iter, TValue(key, Ref(new CSlot())));
168  iter->second->m_MapIter = iter;
169  }
170  else if ( iter->second->m_LockCounter.Add(1) == 1 ) {
171  // first lock from remove list
172  _ASSERT(m_RemoveSize > 0);
173  _ASSERT(m_RemoveSize == m_RemoveList.size());
174  m_RemoveList.erase(iter->second->m_RemoveListIter);
175  --m_RemoveSize;
176  }
177  return CLock(this, iter->second);
178  }
179 
180  size_t get_size_limit(void) const {
181  return m_SizeLimit;
182  }
183  void set_size_limit(size_t size_limit) {
184  if ( size_limit != m_SizeLimit ) {
185  CMutexGuard guard(m_Mutex);
186  x_GC(m_SizeLimit = size_limit);
187  }
188  }
189 
190  void clear() {
191  CMutexGuard guard(m_Mutex);
192  x_GC(0);
193  }
194 
195 protected:
196  void Unlock(CSlot* slot) {
197  CMutexGuard guard(m_Mutex);
198  _ASSERT(slot);
199  _ASSERT(slot->m_MapIter->second == slot);
200  if ( slot->m_LockCounter.Add(-1) == 0 ) {
201  // last lock removed
202  slot->m_RemoveListIter =
203  m_RemoveList.insert(m_RemoveList.end(), slot->m_MapIter);
204  ++m_RemoveSize;
205  x_GC(m_SizeLimit);
206  }
207  }
208 
209  void x_GC(size_t size) {
210  while ( m_RemoveSize > size ) {
211  m_Map.erase(m_RemoveList.front());
212  m_RemoveList.pop_front();
213  --m_RemoveSize;
214  }
215  }
216 
217 public:
218 };
219 
220 
221 // cache for ranged slots with variable 2^n sizes
222 // slot allocation size is a hint, it may be reduced to avoid overlap
223 template<class Position, class Value>
225 {
226 public:
227  typedef Position position_type;
230  struct PLess {
231  bool operator()(const range_type& r1, const range_type& r2) const {
232  return r1.GetToOpen() < r2.GetToOpen();
233  }
234  };
235 
236 protected:
237  class CSlot;
239  typedef typename TMap::iterator TMapIterator;
241  typedef list<TMapIterator> TRemoveList;
242  typedef typename TRemoveList::iterator TRemoveListIterator;
243 
244  class CSlot : public CObject {
245  public:
246  CSlot() {
247  m_LockCounter.Set(1);
248  }
254  };
255 
257  size_t m_SizeLimit;
258  size_t m_RemoveSize;
261 
262 public:
263  class CLock {
264  protected:
269 
271  : m_Cache(cache),
272  m_Range(iter->first),
273  m_Slot(iter->second)
274  {
275  _ASSERT(cache);
276  _ASSERT(m_Slot->m_LockCounter.Get() > 0);
277  }
278 
279  public:
280  CLock() {
281  }
282  ~CLock() {
283  Reset();
284  }
285  CLock(const CLock& lock)
286  : m_Cache(lock.m_Cache),
287  m_Slot(lock.m_Slot)
288  {
289  if ( m_Slot ) {
290  m_Slot->m_LockCounter.Add(1);
291  }
292  }
293  CLock& operator=(const CLock& lock)
294  {
295  if ( m_Slot != lock.m_Slot ) {
296  if ( m_Slot ) {
298  }
299  m_Cache = lock.m_Cache;
300  m_Slot = lock.m_Slot;
301  if ( m_Slot ) {
302  m_Slot->m_LockCounter.Add(1);
303  }
304  }
305  return *this;
306  }
307  CLock(CLock&& lock)
308  : m_Cache(std::move(lock.m_Cache)),
309  m_Slot(std::move(lock.m_Slot))
310  {
311  }
313  {
314  if ( m_Slot != lock.m_Slot ) {
315  Reset();
316  m_Cache.Swap(lock.m_Cache);
317  m_Slot.Swap(lock.m_Slot);
318  }
319  return *this;
320  }
321 
323 
324  void Reset() {
325  if ( m_Slot ) {
327  m_Slot = null;
328  m_Cache = null;
329  }
330  }
331 
332  CFastMutex& GetValueMutex() { return m_Slot.GetNCObject().m_ValueMutex; }
333 
334  const range_type& get_range() const { return m_Range; }
335  mapped_type& operator*() const { return m_Slot.GetNCObject().m_Value; }
336  mapped_type* operator->() const { return &m_Slot.GetNCObject().m_Value; }
337 
338  bool operator==(CLock a) const {
339  return m_Slot == a.m_Slot;
340  }
341  bool operator!=(CLock a) const {
342  return !(*this == a);
343  }
344  };
345 
346  // size of deletion queue of unused slots
347  CBinaryRangeCacheWithLock(size_t size_limit = 0)
348  : m_SizeLimit(size_limit),
349  m_RemoveSize(0)
350  {
351  }
352 
353  // size_pow2 is hint for the size of new slot
354  // actual range may be smaller to avoid overlap with other range
355  CLock get_lock(const position_type& pos, position_type size_pow2) {
356  CMutexGuard guard(m_Mutex);
357  TMapIterator iter = m_Map.upper_bound(range_type(pos, pos)); // iter->to_open > pos
358  if ( iter != m_Map.end() && iter->first.GetFrom() <= pos ) {
359  // existing slot contains pos
360  if ( iter->second->m_LockCounter.Add(1) == 1 ) {
361  // first lock from remove list
362  _ASSERT(m_RemoveSize > 0);
363  _ASSERT(m_RemoveSize == m_RemoveList.size());
364  m_RemoveList.erase(iter->second->m_RemoveListIter);
365  --m_RemoveSize;
366  }
367  }
368  else {
369  TMapIterator iter_before = iter;
370  if ( iter_before != m_Map.begin() ) {
371  --iter_before;
372  }
373  _ASSERT(iter_before == iter /* no range before */ ||
374  pos >= iter_before->first.GetToOpen());
375  _ASSERT(iter == m_Map.end() /* no range after */ ||
376  pos < iter->first.GetFrom());
377  // adjust size of new range
378  while ( s_Overlaps(pos, size_pow2, iter_before, iter) ) {
379  _ASSERT(size_pow2 > 0);
380  --size_pow2;
381  }
382  range_type range = s_MakeRange(pos, size_pow2);
383  // insert
384  typedef typename TMap::value_type TValue;
385  iter = m_Map.insert(iter, TValue(range, Ref(new CSlot())));
386  iter->second->m_MapIter = iter;
387  }
388  return CLock(this, iter);
389  }
390 
391  size_t get_size_limit(void) const {
392  return m_SizeLimit;
393  }
394  void set_size_limit(size_t size_limit) {
395  if ( size_limit != m_SizeLimit ) {
396  CMutexGuard guard(m_Mutex);
397  x_GC(m_SizeLimit = size_limit);
398  }
399  }
400 
401  void clear() {
402  CMutexGuard guard(m_Mutex);
403  x_GC(0);
404  }
405 
406 protected:
407  void Unlock(CSlot* slot) {
408  CMutexGuard guard(m_Mutex);
409  _ASSERT(slot);
410  _ASSERT(slot->m_MapIter->second == slot);
411  if ( slot->m_LockCounter.Add(-1) == 0 ) {
412  // last lock removed
413  slot->m_RemoveListIter =
414  m_RemoveList.insert(m_RemoveList.end(), slot->m_MapIter);
415  ++m_RemoveSize;
416  x_GC(m_SizeLimit);
417  }
418  }
419 
421  position_type size = position_type(1)<<size_pow2;
422  position_type mask = size-1;
423  pos &= ~mask;
424  return range_type(pos, pos+size);
425  }
427  TMapIterator iter_before, TMapIterator iter_after) const {
428  range_type range = s_MakeRange(pos, size_pow2);
429  if ( iter_before != iter_after &&
430  iter_before->first.GetToOpen() > range.GetFrom() ) {
431  // overlap with range before
432  return true;
433  }
434  if ( iter_after != m_Map.end() &&
435  iter_after->first.GetFrom() < range.GetToOpen() ) {
436  // overlap with range after
437  return true;
438  }
439  return false;
440  }
441 
442  void x_GC(size_t size) {
443  while ( m_RemoveSize > size ) {
444  m_Map.erase(m_RemoveList.front());
445  m_RemoveList.pop_front();
446  --m_RemoveSize;
447  }
448  }
449 
450 public:
451 };
452 
453 
455 
456 #endif // CORELIB__CACHE_WITH_LOCK__HPP
ncbi::TMaskedQueryRegions mask
CAtomicCounter –.
Definition: ncbicntr.hpp:71
CLock & operator=(const CLock &lock)
CRef< CBinaryRangeCacheWithLock > m_Cache
const range_type & get_range() const
CLock(CBinaryRangeCacheWithLock *cache, const TMapIterator &iter)
map< range_type, CRef< CSlot >, PLess > TMap
TRemoveList::iterator TRemoveListIterator
bool s_Overlaps(position_type pos, position_type size_pow2, TMapIterator iter_before, TMapIterator iter_after) const
void set_size_limit(size_t size_limit)
list< TMapIterator > TRemoveList
CLock get_lock(const position_type &pos, position_type size_pow2)
static range_type s_MakeRange(position_type pos, position_type size_pow2)
CBinaryRangeCacheWithLock(size_t size_limit=0)
TMap::const_iterator TMapConstIterator
COpenRange< position_type > range_type
size_t get_size_limit(void) const
CLock(const CLock &lock)
CFastMutex & GetValueMutex()
bool operator!=(CLock a) const
DECLARE_OPERATOR_BOOL_REF(m_Slot)
CRef< CCacheWithLock > m_Cache
mapped_type * operator->() const
CLock & operator=(CLock &&lock)
mapped_type & operator*() const
bool operator==(CLock a) const
CLock & operator=(const CLock &lock)
CLock(CCacheWithLock *cache, CSlot *slot)
TRemoveListIterator m_RemoveListIter
CAtomicCounter m_LockCounter
size_t get_size_limit(void) const
list< TMapIterator > TRemoveList
void set_size_limit(size_t size_limit)
void x_GC(size_t size)
CLock get_lock(const key_type &key)
TMap::iterator TMapIterator
map< key_type, CRef< CSlot >, TLess > TMap
CCacheWithLock(size_t size_limit=0)
TMap::const_iterator TMapConstIterator
TRemoveList::iterator TRemoveListIterator
TRemoveList m_RemoveList
void Unlock(CSlot *slot)
CFastMutex –.
Definition: ncbimtx.hpp:667
CMutex –.
Definition: ncbimtx.hpp:749
CObject –.
Definition: ncbiobj.hpp:180
void erase(iterator pos)
Definition: map.hpp:167
container_type::const_iterator const_iterator
Definition: map.hpp:53
const_iterator begin() const
Definition: map.hpp:151
const_iterator end() const
Definition: map.hpp:152
const_iterator lower_bound(const key_type &key) const
Definition: map.hpp:154
iterator_bool insert(const value_type &val)
Definition: map.hpp:165
const_iterator upper_bound(const key_type &key) const
Definition: map.hpp:155
Definition: map.hpp:338
Include a standard set of the NCBI C++ Toolkit most basic headers.
static DLIST_TYPE *DLIST_NAME() first(DLIST_LIST_TYPE *list)
Definition: dlist.tmpl.h:46
void Set(TValue new_value) THROWS_NONE
Set atomic counter value.
Definition: ncbicntr.hpp:185
TValue Add(int delta) THROWS_NONE
Atomically add value (=delta), and return new counter value.
Definition: ncbicntr.hpp:278
TValue Get(void) const THROWS_NONE
Get atomic counter value.
Definition: ncbicntr.hpp:168
CRef< C > Ref(C *object)
Helper functions to get CRef<> and CConstRef<> objects.
Definition: ncbiobj.hpp:2015
void Swap(TThisType &ref)
Swaps the pointer with another reference.
Definition: ncbiobj.hpp:754
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
range(_Ty, _Ty) -> range< _Ty >
const struct ncbi::grid::netcache::search::fields::SIZE size
const struct ncbi::grid::netcache::search::fields::KEY key
GenericValue< UTF8<> > Value
GenericValue with UTF8 encoding.
Definition: document.h:2107
unsigned int a
Definition: ncbi_localip.c:102
static const sljit_gpr r1
static const sljit_gpr r2
bool operator()(const range_type &r1, const range_type &r2) const
#define _ASSERT
Modified on Fri Sep 20 14:57:25 2024 by modify_doxy.py rev. 669887