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