yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
f65d756bf
master
1#include "slang-persistent-cache.h" 2 3#include "../core/slang-blob.h" 4#include "../core/slang-io.h" 5#include "../core/slang-stream.h" 6#include "../core/slang-string-util.h" 7 8namespace Slang 9{ 10 11PersistentCache ::PersistentCache (const Desc & desc ) 12{ 13m_cacheDirectory = Path ::simplify (desc .directory ); 14Path ::createDirectory (m_cacheDirectory ); 15 16m_lockFileName = Path ::simplify (m_cacheDirectory + "/lock" ); 17m_indexFileName = Path ::simplify (m_cacheDirectory + "/index" ); 18 19m_lockFile .open (m_lockFileName ); 20 21m_maxEntryCount = desc .maxEntryCount ; 22 23resetStats (); 24 25initialize (); 26} 27 28PersistentCache ::~PersistentCache () {} 29 30SlangResult PersistentCache ::clear () 31{ 32if (!m_lockFile .isOpen ()) 33 { 34return SLANG_E_CANNOT_OPEN ; 35 } 36 37// Acquire the exclusive lock. 38 std::lock_guard < std::mutex > mutexLock (m_mutex ); 39LockFileGuard fileLock (m_lockFile ); 40 41struct Visitor :Path ::Visitor 42 { 43const String & directory ; 44const String & lockFileName ; 45 46Visitor (const String & directory ,const String & lockFileName ) 47 :directory (directory ),lockFileName (lockFileName ) 48 { 49 } 50 51void accept (Path ::Type type ,const UnownedStringSlice & fileName )SLANG_OVERRIDE 52 { 53String fullPath = Path ::simplify (directory + "/" + fileName ); 54 ; 55if (type == Path ::Type ::File && lockFileName != fullPath ) 56 { 57Path ::remove (fullPath ); 58 } 59 } 60 }; 61 62Visitor visitor (m_cacheDirectory ,m_lockFileName ); 63Path ::find (m_cacheDirectory ,nullptr ,& visitor ); 64 65m_stats .entryCount = 0 ; 66 67return SLANG_OK ; 68} 69 70void PersistentCache ::resetStats () 71{ 72m_stats .entryCount = 0 ; 73m_stats .hitCount = 0 ; 74m_stats .missCount = 0 ; 75} 76 77SlangResult PersistentCache ::readEntry (const Key & key ,ISlangBlob ** outData ) 78{ 79// Be pessimistic and assume we have a cache miss. 80++ m_stats .missCount ; 81 82if (!m_lockFile .isOpen ()) 83 { 84return SLANG_E_CANNOT_OPEN ; 85 } 86 87// Acquire the exclusive lock. 88 std::lock_guard < std::mutex > mutexLock (m_mutex ); 89LockFileGuard fileLock (m_lockFile ); 90 91// Return if index does not exist. 92if (!File ::exists (m_indexFileName )) 93 { 94return SLANG_E_NOT_FOUND ; 95 } 96 97// Read the cache index. 98CacheIndex cacheIndex ; 99SLANG_RETURN_ON_FAIL (readIndex (m_indexFileName ,cacheIndex )); 100 101// Increase the age of all entries in the cache. 102for (auto & entry :cacheIndex ) 103 { 104++ entry .age ; 105 } 106 107// Find the entry. 108Index entryIndex = 109cacheIndex .findFirstIndex ([& key ](const CacheEntry & entry ) {return entry .key == key ; }); 110if (entryIndex == -1 ) 111 { 112return SLANG_E_NOT_FOUND ; 113 } 114 115// Read the entry. 116String entryFileName = getEntryFileName (key ); 117ScopedAllocation data ; 118SlangResult result = File ::readAllBytes (entryFileName ,data ); 119if (result == SLANG_OK ) 120 { 121-- m_stats .missCount ; 122++ m_stats .hitCount ; 123cacheIndex [entryIndex ].age = 0 ; 124auto blob = RawBlob ::moveCreate (data ); 125* outData = blob .detach (); 126 } 127else 128 { 129cacheIndex .removeAt (entryIndex ); 130 } 131 132// Write the cache index. 133SLANG_RETURN_ON_FAIL (writeIndex (m_indexFileName ,cacheIndex )); 134m_stats .entryCount = (Count )cacheIndex .getCount (); 135 136return result ; 137} 138 139SlangResult PersistentCache ::writeEntry (const Key & key ,ISlangBlob * data ) 140{ 141SLANG_ASSERT (data ); 142 143if (!m_lockFile .isOpen ()) 144 { 145return SLANG_E_CANNOT_OPEN ; 146 } 147 148// Acquire the exclusive lock. 149 std::lock_guard < std::mutex > mutexLock (m_mutex ); 150LockFileGuard fileLock (m_lockFile ); 151 152// Read the cache index. 153// We ignore any errors when reading the index and just write a new one. 154CacheIndex cacheIndex ; 155readIndex (m_indexFileName ,cacheIndex ); 156 157// Increase the age of all entries in the cache and get the index of 158// the oldest entry. 159Index oldestEntryIndex = -1 ; 160uint32_t oldestEntryAge = 0 ; 161for (Index entryIndex = 0 ;entryIndex < cacheIndex .getCount ();++ entryIndex ) 162 { 163auto & entry = cacheIndex [entryIndex ]; 164++ entry .age ; 165if (entry .age > oldestEntryAge ) 166 { 167oldestEntryIndex = entryIndex ; 168oldestEntryAge = entry .age ; 169 } 170 } 171 172// Write the cache entry. 173String entryFileName = getEntryFileName (key ); 174SLANG_RETURN_ON_FAIL ( 175File ::writeAllBytes (entryFileName ,data -> getBufferPointer (),data -> getBufferSize ())); 176 177// Update the index. 178if (m_maxEntryCount > 0 && cacheIndex .getCount () >=m_maxEntryCount ) 179 { 180// Replace oldest entry. 181SLANG_ASSERT (oldestEntryIndex >=0 ); 182File ::remove (getEntryFileName (cacheIndex [oldestEntryIndex ].key )); 183cacheIndex [oldestEntryIndex ]= CacheEntry {key ,0 }; 184 } 185else 186 { 187// Add new entry. 188cacheIndex .add (CacheEntry {key ,0 }); 189 } 190 191// Write the cache index. 192SlangResult result = writeIndex (m_indexFileName ,cacheIndex ); 193if (result == SLANG_OK ) 194 { 195m_stats .entryCount = (Count )cacheIndex .getCount (); 196 } 197else 198 { 199// If writing the index failed, remove the entry file to avoid growing the cache. 200Path ::remove (entryFileName ); 201 } 202 203return result ; 204} 205 206SlangResult PersistentCache ::initialize () 207{ 208if (!m_lockFile .isOpen ()) 209 { 210return SLANG_E_CANNOT_OPEN ; 211 } 212 213// Acquire the exclusive lock. 214 std::lock_guard < std::mutex > mutexLock (m_mutex ); 215LockFileGuard fileLock (m_lockFile ); 216 217CacheIndex cacheIndex ; 218if (SLANG_SUCCEEDED (readIndex (m_indexFileName ,cacheIndex ))) 219 { 220m_stats .entryCount = (Count )cacheIndex .getCount (); 221 } 222 223return SLANG_OK ; 224} 225 226String PersistentCache ::getEntryFileName (const Key & key ) 227{ 228StringBuilder str ; 229str <<m_cacheDirectory <<"/" <<key .toString (); 230return str ; 231} 232 233struct CacheIndexHeader 234{ 235char magic [4 ]; 236uint32_t version ; 237uint32_t count ; 238uint32_t reserved ; 239}; 240 241static const char * kMagic = "SLS$" ; 242static const uint32_t kVersion = 1 ; 243 244SlangResult PersistentCache ::readIndex (const String & fileName ,CacheIndex & outIndex ) 245{ 246FileStream fs ; 247SLANG_RETURN_ON_FAIL (fs .init (fileName ,FileMode ::Open )); 248 249// Get file size. 250SLANG_RETURN_ON_FAIL (fs .seek (SeekOrigin ::End ,0 )); 251size_t fileSize = (size_t )fs .getPosition (); 252SLANG_RETURN_ON_FAIL (fs .seek (SeekOrigin ::Start ,0 )); 253 254CacheIndexHeader header ; 255SLANG_RETURN_ON_FAIL (fs .readExactly (& header ,sizeof (header ))); 256if (::memcmp (header .magic ,kMagic ,4 )!= 0 || header .version != kVersion ) 257 { 258return SLANG_E_INTERNAL_FAIL ; 259 } 260 261// Return if payload does not have the right size. 262if (header .count * sizeof (CacheEntry )!= fileSize - sizeof (header )) 263 { 264return SLANG_E_INTERNAL_FAIL ; 265 } 266 267outIndex .setCount (header .count ); 268SLANG_RETURN_ON_FAIL (fs .readExactly (outIndex .getBuffer (),header .count * sizeof (CacheEntry ))); 269 270return SLANG_OK ; 271} 272 273SlangResult PersistentCache ::writeIndex (const String & fileName ,const CacheIndex & index ) 274{ 275FileStream fs ; 276SLANG_RETURN_ON_FAIL (fs .init (fileName ,FileMode ::Create )); 277 278CacheIndexHeader header ; 279 ::memcpy (header .magic ,kMagic ,4 ); 280header .version = kVersion ; 281header .count = (uint32_t )index .getCount (); 282header .reserved = 0 ; 283SLANG_RETURN_ON_FAIL (fs .write (& header ,sizeof (header ))); 284 285SLANG_RETURN_ON_FAIL (fs .write (index .getBuffer (),index .getCount ()* sizeof (CacheEntry ))); 286 287return SLANG_OK ; 288} 289 290}// namespace Slang