1 #ifndef EXCLUDE_BLOB_CACHE__HPP
2 #define EXCLUDE_BLOB_CACHE__HPP
3 
4 /*  $Id: exclude_blob_cache.hpp 629837 2021-04-22 12:47:49Z ivanov $
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: Sergey Satskiy
30  *
31  * File Description:
32  *
33  */
34 
35 
36 #include <string>
37 #include <set>
38 #include <list>
39 #include <chrono>
40 #include <atomic>
41 #include <map>
42 #include <vector>
43 using namespace std;
44 
45 
46 enum EPSGS_CacheAddResult {
47     ePSGS_AlreadyInCache,
48     ePSGS_Added
49 };
50 
51 
52 struct SExcludeBlobId
53 {
54     int     m_Sat;
55     int     m_SatKey;
56     bool    m_Completed;
57 
SExcludeBlobIdSExcludeBlobId58     SExcludeBlobId(int  sat, int  sat_key):
59         m_Sat(sat), m_SatKey(sat_key), m_Completed(false)
60     {}
61 
operator <SExcludeBlobId62     bool operator < (const SExcludeBlobId &  other) const
63     {
64         if (m_Sat == other.m_Sat)
65             return m_SatKey < other.m_SatKey;
66         return m_Sat < other.m_Sat;
67     }
68 
operator ==SExcludeBlobId69     bool operator == (const SExcludeBlobId &  other) const
70     {
71         return m_Sat == other.m_Sat && m_SatKey == other.m_SatKey;
72     }
73 };
74 
75 
76 
77 class CUserExcludeBlobs
78 {
79     public:
CUserExcludeBlobs()80         CUserExcludeBlobs() :
81             m_Lock(false), m_LastTouch(std::chrono::steady_clock::now())
82         {}
83 
~CUserExcludeBlobs()84         ~CUserExcludeBlobs()
85         {}
86 
87     public:
88         // The 'completed' value is filled only if the blob is in the cache
89         bool IsInCache(int  sat, int  sat_key, bool &  completed);
90         EPSGS_CacheAddResult AddBlobId(int  sat, int  sat_key, bool &  completed);
91 
92         // Return true if the required blob id was found
93         bool SetCompleted(int  sat, int  sat_key, bool  new_val);
94         bool Remove(int  sat, int  sat_key);
95 
96         void Purge(size_t  purged_size);
97         void Clear(void);
98 
99     public:
100         // The lock is exposed so that the upper level can grab it before
101         // the upper lock is released. An alternative would be to store the
102         // lock in the upper level but it seems better to store it here because
103         // it goes into the cathegory of the user associated data
104         atomic<bool>                                        m_Lock;
105         std::chrono::time_point<std::chrono::steady_clock>  m_LastTouch;
106 
107     private:
108         set<SExcludeBlobId>                                 m_ExcludeBlobs;
109         list<SExcludeBlobId>                                m_LRU;
110 };
111 
112 
113 class CUserExcludeBlobsPool
114 {
115     public:
CUserExcludeBlobsPool()116         CUserExcludeBlobsPool() :
117             m_Head(nullptr), m_Lock(false)
118         {}
119 
~CUserExcludeBlobsPool()120         ~CUserExcludeBlobsPool()
121         {
122             SNode *     current = m_Head.load();
123             while (current != nullptr) {
124                 delete current->m_Data;
125                 auto next_item = current->m_Next;
126                 delete current;
127                 current = next_item;
128             }
129         }
130 
131         // Non copyable
132         CUserExcludeBlobsPool(CUserExcludeBlobsPool const &) = delete;
133         CUserExcludeBlobsPool(CUserExcludeBlobsPool &&) = delete;
134         const CUserExcludeBlobsPool & operator=(const CUserExcludeBlobsPool &) = delete;
135 
136     public:
Get(void)137         CUserExcludeBlobs *  Get(void)
138         {
139             SNode *     item = m_Head.load();
140             if (item == nullptr)
141                 return new CUserExcludeBlobs();
142 
143             while (m_Lock.exchange(true)) {}    // acquire lock
144 
145             item = m_Head.load();
146             auto object = item->m_Data;
147             m_Head = item->m_Next;
148 
149             m_Lock = false;                     // release lock
150 
151             delete item;
152             return object;
153         }
154 
Return(CUserExcludeBlobs * user_exclude_blobs)155         void Return(CUserExcludeBlobs *  user_exclude_blobs)
156         {
157             SNode *     returned_node = new SNode(user_exclude_blobs);
158 
159             while (m_Lock.exchange(true)) {}    // acquire lock
160 
161             returned_node->m_Next = m_Head.load();
162             m_Head = returned_node;
163 
164             m_Lock = false;                     // release lock
165         }
166 
167     private:
168         struct SNode {
169             SNode *                 m_Next;
170             CUserExcludeBlobs *     m_Data;
171 
SNodeCUserExcludeBlobsPool::SNode172             SNode(CUserExcludeBlobs *  exclude_blobs) :
173                 m_Next(nullptr), m_Data(exclude_blobs)
174             {}
175         };
176 
177         atomic<SNode *>     m_Head;
178         atomic<bool>        m_Lock;
179 };
180 
181 
182 
183 class CExcludeBlobCache
184 {
185     public:
CExcludeBlobCache(size_t inactivity_timeout,size_t max_cache_size,size_t purged_size)186         CExcludeBlobCache(size_t  inactivity_timeout,
187                           size_t  max_cache_size, size_t  purged_size) :
188             m_Lock(false), m_InactivityTimeout(inactivity_timeout),
189             m_MaxCacheSize(max_cache_size), m_PurgedSize(purged_size)
190         {
191             m_ToPurge.reserve(128);     // arbitrary
192             m_ToDiscard.reserve(128);   // arbitrary
193         }
194 
~CExcludeBlobCache()195         ~CExcludeBlobCache()
196         {
197             for (auto &  item : m_UserBlobs)
198                 delete item.second;
199         }
200 
201     public:
202         // The 'completed' value is filled only if the blob is in the cache
203         EPSGS_CacheAddResult AddBlobId(const string &  user,
204                                        int  sat, int  sat_key, bool &  completed);
205         bool IsInCache(const string &  user,
206                        int  sat, int  sat_key, bool &  completed);
207 
208         // Return true if the required blob id was found
209         bool SetCompleted(const string &  user,
210                           int  sat, int  sat_key, bool  new_val);
211         bool Remove(const string &  user,
212                     int  sat, int  sat_key);
213 
214         void Purge(void);
215 
Size(void)216         size_t Size(void) {
217             size_t size = 0;
218             while (m_Lock.exchange(true)) {}    // acquire top level lock
219             size = m_UserBlobs.size();
220             m_Lock = false;                     // release top level lock
221             return size;
222         }
223 
224     private:
225         map<string, CUserExcludeBlobs *>    m_UserBlobs;
226         atomic<bool>                        m_Lock;
227         CUserExcludeBlobsPool               m_Pool;
228 
229         std::chrono::seconds                m_InactivityTimeout;
230         size_t                              m_MaxCacheSize;
231         size_t                              m_PurgedSize;
232 
233         vector<CUserExcludeBlobs *>         m_ToPurge;
234         vector<CUserExcludeBlobs *>         m_ToDiscard;
235 };
236 
237 
238 #endif
239