lldiskcache.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. /**
  2. * @file lldiskcache.cpp
  3. * @brief Implementation of the disk cache.
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewergpl$
  6. *
  7. * Copyright (c) 2020, Linden Research, Inc. (c) 2021 Henri Beauchamp.
  8. *
  9. * Modifications by Henri Beauchamp:
  10. * - Pointless per-asset-type file naming removed.
  11. * - Use of LLFile faster operations and of the extended LLDiriterator where
  12. * possible.
  13. * - Cache structure changed to speed up file opening and reduce the risk of
  14. * hitting file-systems limitations (such as the max number of files per
  15. * directory).
  16. * - Proper cache validation and shutdown.
  17. * - Proper catching of throw()s and boost::filesystem errors.
  18. * - Track cache files size in real time (lock-less: just via an atomic
  19. * variable).
  20. * - Proper and threaded auto-purging of the cache when it exceeds 150% of
  21. * its nominal size.
  22. * - Multiple threads and multiple viewer instances deconfliction.
  23. *
  24. * Second Life Viewer Source Code
  25. * The source code in this file ("Source Code") is provided by Linden Lab
  26. * to you under the terms of the GNU General Public License, version 2.0
  27. * ("GPL"), unless you have obtained a separate licensing agreement
  28. * ("Other License"), formally executed by you and Linden Lab. Terms of
  29. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  30. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  31. *
  32. * There are special exceptions to the terms and conditions of the GPL as
  33. * it is applied to this Source Code. View the full text of the exception
  34. * in the file doc/FLOSS-exception.txt in this software distribution, or
  35. * online at
  36. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  37. *
  38. * By copying, modifying or distributing this software, you acknowledge
  39. * that you have read and understood your obligations described above,
  40. * and agree to abide by those obligations.
  41. *
  42. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  43. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  44. * COMPLETENESS OR PERFORMANCE.
  45. * $/LicenseInfo$
  46. */
  47. #include "linden_common.h"
  48. #include "boost/filesystem.hpp"
  49. #include "lldiskcache.h"
  50. #include "llcallbacklist.h"
  51. #include "lldir.h"
  52. #include "lldiriterator.h"
  53. #include "llrand.h"
  54. #include "llthread.h"
  55. #include "lltimer.h"
  56. #include "hbtracy.h"
  57. using namespace boost::filesystem;
  58. // Threshold in time_t units that is used to decide if the last access time of
  59. // the file is updated or not. Added as a precaution for the concern outlined
  60. // in SL-14582 about frequent writes on SSDs reducing their lifespan. Let's
  61. // start with half an hour in time_t units and see how that unfolds.
  62. constexpr time_t TIME_THRESHOLD = 1800;
  63. // ... reduced to only one minute when we are currently purging the cache. HB
  64. constexpr time_t TIME_THRESHOLD_PURGE = 60;
  65. // Interval of time between consecutive checks for the stopping of the purging
  66. // thread (1 second).
  67. constexpr F32 INTERVAL_BETWEEN_CHECKS = 1.f;
  68. // Static variable members
  69. LLCachePurgeThread* LLDiskCache::sPurgeThread = NULL;
  70. std::string LLDiskCache::sCacheDir;
  71. U64 LLDiskCache::sNominalSizeBytes = 0;
  72. U64 LLDiskCache::sMaxSizeBytes = 0;
  73. LLAtomicU64 LLDiskCache::sCurrentSizeBytes(0);
  74. LLAtomicBool LLDiskCache::sPurging(false);
  75. bool LLDiskCache::sCacheValid = false;
  76. // Subdirectory names 0...9a...f, concatenated in a string
  77. static std::string sDigits = "0123456789abcdef";
  78. ///////////////////////////////////////////////////////////////////////////////
  79. // LLCachePurgeThread class
  80. ///////////////////////////////////////////////////////////////////////////////
  81. class LLCachePurgeThread final : public LLThread
  82. {
  83. protected:
  84. LOG_CLASS(LLCachePurgeThread);
  85. public:
  86. LL_INLINE LLCachePurgeThread()
  87. : LLThread("Disk cache purging thread")
  88. {
  89. start();
  90. }
  91. LL_INLINE void run() override
  92. {
  93. LLDiskCache::purge();
  94. }
  95. };
  96. ///////////////////////////////////////////////////////////////////////////////
  97. // LLDiskCache class
  98. ///////////////////////////////////////////////////////////////////////////////
  99. //static
  100. void LLDiskCache::init(U64 nominal_size_bytes, bool second_instance)
  101. {
  102. llinfos << "Initializing cache..." << llendl;
  103. sNominalSizeBytes = nominal_size_bytes;
  104. sMaxSizeBytes = 15UL * sNominalSizeBytes / 10UL;
  105. if (second_instance)
  106. {
  107. // Add 50 to 150 Mb (in random steps of 5Mb) to the maximum size for
  108. // the second and further instances, so that the various instances do
  109. // not attempt to purge the cache at the same time (even though, since
  110. // they only account each for their own cache file writes, they will
  111. // not see the same apparent cache size at the same time)... HB
  112. sMaxSizeBytes += (50UL + 5UL * U64(ll_frand(20.f))) * 1048576UL;
  113. }
  114. // We enforce the storage of our files in an "assets" sub-directory, which
  115. // save us from worrying about deleting files that do not belong to our
  116. // cache (no need to test for a file prefix or extension, meaning faster
  117. // operations when purging, clearing, or calculating the cache size). HB
  118. sCacheDir = gDirUtil.getFullPath(LL_PATH_CACHE, "assets");
  119. sCacheValid = LLFile::mkdir(sCacheDir);
  120. if (sCacheValid)
  121. {
  122. sCacheDir += LL_DIR_DELIM_CHR;
  123. // We use sub-directories to lower the number of file entries per
  124. // directory (which can easily count in hundred of thousands when
  125. // using a large cache in a single directory). This avoids hitting
  126. // any file-system limitation, and helps speeding up the opening of
  127. // cache files. HB
  128. for (U32 i = 0; i < 16; ++i)
  129. {
  130. sCacheValid &= LLFile::mkdir(sCacheDir + sDigits[i]);
  131. }
  132. }
  133. if (sCacheValid)
  134. {
  135. #if LL_WINDOWS
  136. if (!second_instance)
  137. {
  138. // Do not call cacheDirSize() on startup from the main thread under
  139. // Windows when the cache directory has not already been scanned
  140. // (i.e. after boot, from the first viewer instance): it causes
  141. // minutes-long delays for large caches on hard disks (obviously a
  142. // problem with "SuperFetch", but even after disabling it, scanning
  143. // the cache can take a couple dozens seconds, when the same cache
  144. // takes at most a few seconds to get scanned under Linux) !
  145. // LLDiskCache::threadedPurge() will instead set sCurrentSizeBytes
  146. // for us, and in a non-blocking thread... HB
  147. llinfos << "Nominal cache size: " << sNominalSizeBytes
  148. << " bytes. Maximal cache size: " << sMaxSizeBytes
  149. << " bytes. Cache directory: " << sCacheDir << llendl;
  150. return;
  151. }
  152. #endif
  153. sCurrentSizeBytes = cacheDirSize();
  154. llinfos << "Nominal cache size: " << sNominalSizeBytes
  155. << " bytes. Maximal cache size: " << sMaxSizeBytes
  156. << " bytes. Current cache size: " << sCurrentSizeBytes
  157. << " bytes. Cache directory: " << sCacheDir << llendl;
  158. }
  159. else
  160. {
  161. llwarns << "Cache path is invalid: " << sCacheDir << llendl;
  162. }
  163. }
  164. //static
  165. void LLDiskCache::shutdown()
  166. {
  167. // Stop changing the cache now !
  168. sCacheValid = false;
  169. if (sPurgeThread)
  170. {
  171. U32 loops = 0;
  172. while (loops++ < 100 && !sPurgeThread->isStopped())
  173. {
  174. ms_sleep(10); // Give it some more time...
  175. }
  176. if (sPurgeThread->isStopped())
  177. {
  178. llinfos << "Cache purging thread stopped." << llendl;
  179. }
  180. else
  181. {
  182. llwarns << "Timeout waiting for the cache purging thread to stop. Force-removing it."
  183. << llendl;
  184. }
  185. delete sPurgeThread;
  186. sPurgeThread = NULL;
  187. sPurging = false;
  188. }
  189. }
  190. //static
  191. U64 LLDiskCache::cacheDirSize()
  192. {
  193. U64 total_file_size = 0;
  194. std::string subdir, filename;
  195. for (U32 i = 0; i < 16; ++i)
  196. {
  197. subdir = sCacheDir + sDigits[i];
  198. if (LLFile::isdir(subdir))
  199. {
  200. LLDirIterator iter(subdir, NULL, DI_SIZE);
  201. while (iter.next(filename))
  202. {
  203. total_file_size += iter.getSize();
  204. }
  205. }
  206. }
  207. return total_file_size;
  208. }
  209. //static
  210. void LLDiskCache::clear()
  211. {
  212. if (LLFile::isdir(sCacheDir))
  213. {
  214. std::string subdir;
  215. for (U32 i = 0; i < 16; ++i)
  216. {
  217. subdir = sCacheDir + sDigits[i];
  218. if (LLFile::isdir(subdir))
  219. {
  220. LLDirIterator::deleteFilesInDir(subdir);
  221. }
  222. }
  223. }
  224. else
  225. {
  226. llinfos << "No cache directory: nothing to clear." << llendl;
  227. }
  228. sCurrentSizeBytes = 0;
  229. }
  230. //static
  231. void LLDiskCache::purge()
  232. {
  233. if (!LLFile::isdir(sCacheDir))
  234. {
  235. llinfos << "No cache directory: nothing to purge." << llendl;
  236. return;
  237. }
  238. sPurging = true;
  239. typedef std::pair<time_t, std::pair<U64, std::string> > file_info_t;
  240. std::vector<file_info_t> file_info;
  241. LLTimer purge_timer;
  242. purge_timer.reset();
  243. std::string subdir, filename;
  244. for (U32 i = 0; i < 16; ++i)
  245. {
  246. subdir = sCacheDir + sDigits[i];
  247. if (!LLFile::isdir(subdir))
  248. {
  249. llwarns << "Missing cache sub-directory: " << subdir << llendl;
  250. continue;
  251. }
  252. LLDirIterator iter(subdir, NULL, DI_ISFILE | DI_SIZE | DI_TIMESTAMP);
  253. while (iter.next(filename))
  254. {
  255. if (iter.isFile())
  256. {
  257. file_info.emplace_back(iter.getTimeStamp(),
  258. std::make_pair(iter.getSize(),
  259. iter.getPath() +
  260. filename));
  261. }
  262. }
  263. }
  264. std::sort(file_info.begin(), file_info.end(),
  265. [](file_info_t& x, file_info_t& y)
  266. {
  267. return x.first > y.first;
  268. });
  269. U32 count = file_info.size();
  270. llinfos << count
  271. << " files found in cache. Checking the total size and possibly purging old files..."
  272. << llendl;
  273. U64 files_size_total = 0;
  274. U64 removed_bytes = 0;
  275. U32 purged_files = 0;
  276. for (U32 i = 0; i < count; ++i)
  277. {
  278. const file_info_t& entry = file_info[i];
  279. files_size_total += entry.second.first;
  280. bool removed = files_size_total > sNominalSizeBytes;
  281. if (removed)
  282. {
  283. try
  284. {
  285. // Verify that the file did not get touched by another thread
  286. // or viewer instance since we last checked its time stamp !
  287. if (last_write_time(entry.second.second) <= entry.first)
  288. {
  289. remove(entry.second.second);
  290. ++purged_files;
  291. removed_bytes += entry.second.first;
  292. }
  293. else
  294. {
  295. LL_DEBUGS("DiskCache") << "Skipped updated file: "
  296. << entry.second.second << LL_ENDL;
  297. removed = false;
  298. }
  299. }
  300. catch (const filesystem_error& e)
  301. {
  302. removed = false;
  303. llwarns << "Failure to remove \"" << entry.second.second
  304. << "\". Reason: " << e.what() << llendl;
  305. }
  306. }
  307. LL_DEBUGS("DiskCache") << (removed ? " Removed " : "Kept ")
  308. << entry.second.second << LL_ENDL;
  309. }
  310. sPurging = false;
  311. #if 0 // This would be more accurate for a single running viewer instance (no
  312. // risk of a race condition with another thread writing into the cache
  313. // while we counted the files size in it), but with multiple running
  314. // instances of the viewer, sCurrentSizeBytes does not account for
  315. // files written by those instances, so we must reset the actual cache
  316. // size (as we just counted it) to account for them... HB
  317. // Plus, for Windoze, we cannot do that (sCurrentSizeBytes not being
  318. // initialized to the actual cache size on login: see the comment in
  319. // init()).
  320. sCurrentSizeBytes -= removed_bytes;
  321. #else
  322. sCurrentSizeBytes = files_size_total - removed_bytes;
  323. #endif
  324. U32 ms = (U32)(purge_timer.getElapsedTimeF32() * 1000.f);
  325. if (purged_files)
  326. {
  327. llinfos << "Cache purge took " << ms << "ms to execute. "
  328. << purged_files << " purged files and " << removed_bytes
  329. << " bytes removed. " << sCurrentSizeBytes
  330. << " bytes now in cache." << llendl;
  331. }
  332. else
  333. {
  334. llinfos << "Cache check took " << ms << "ms to execute. Cache size: "
  335. << sCurrentSizeBytes << " bytes." << llendl;
  336. }
  337. LL_DEBUGS("DiskCache") << "Current cache size: " << cacheDirSize()
  338. << " bytes." << LL_ENDL;
  339. }
  340. // Must be called from the main thread only !
  341. //static
  342. void LLDiskCache::threadedPurge()
  343. {
  344. if (!sCacheValid)
  345. {
  346. return;
  347. }
  348. if (sPurgeThread) // Called via doAfterInterval()
  349. {
  350. if (sPurgeThread->isStopped())
  351. {
  352. LL_DEBUGS("DiskCache") << "Purge thread stopped, deleting it."
  353. << LL_ENDL;
  354. delete sPurgeThread;
  355. sPurgeThread = NULL;
  356. }
  357. else
  358. {
  359. LL_DEBUGS("DiskCache") << "Purge thread still running..."
  360. << LL_ENDL;
  361. // Check again later to see if the thread has stopped
  362. doAfterInterval(threadedPurge, INTERVAL_BETWEEN_CHECKS);
  363. }
  364. }
  365. else // Called by addBytesWritten() or from llappviewer.cpp
  366. {
  367. // Start a new thread.
  368. LL_DEBUGS("DiskCache") << "Starting a new purge thread..." << LL_ENDL;
  369. sPurgeThread = new LLCachePurgeThread;
  370. // Check again later to see if the thread has stopped
  371. doAfterInterval(threadedPurge, INTERVAL_BETWEEN_CHECKS);
  372. }
  373. }
  374. //static
  375. std::string LLDiskCache::getFilePath(const LLUUID& id, const char* extra_info)
  376. {
  377. std::string filename = id.asString();
  378. if (extra_info && *extra_info)
  379. {
  380. filename += '_';
  381. filename += extra_info;
  382. }
  383. filename += ".asset";
  384. return ((sCacheDir + filename[0]) + LL_DIR_DELIM_STR) + filename;
  385. }
  386. //static
  387. void LLDiskCache::addBytesWritten(S32 bytes)
  388. {
  389. LL_TRACY_TIMER(TRC_DISKCACHE_UPDSIZE);
  390. sCurrentSizeBytes += bytes;
  391. // If not called by the main thread, or a threaded purging is in progress,
  392. // bail out now.
  393. if (!is_main_thread() || sPurgeThread)
  394. {
  395. return;
  396. }
  397. LL_DEBUGS("DiskCache") << "Cache size: " << sCurrentSizeBytes << " bytes."
  398. << LL_ENDL;
  399. // Start purging the cache if needed.
  400. if (sCurrentSizeBytes > sMaxSizeBytes)
  401. {
  402. threadedPurge();
  403. }
  404. }
  405. //static
  406. void LLDiskCache::updateFileAccessTime(const std::string& filename)
  407. {
  408. LL_TRACY_TIMER(TRC_DISKCACHE_ACCESSTIME);
  409. // Current time
  410. const time_t cur_time = computer_time();
  411. // Last write time
  412. time_t last_write = LLFile::lastModidied(filename);
  413. // We only write the new value if 'threshold' has elapsed since the last
  414. // write.
  415. time_t threshold = sPurging ? TIME_THRESHOLD_PURGE : TIME_THRESHOLD;
  416. if (cur_time - last_write > threshold)
  417. {
  418. boost::system::error_code ec;
  419. #if LL_WINDOWS
  420. last_write_time(ll_convert_string_to_wide(filename), cur_time, ec);
  421. #else
  422. last_write_time(filename, cur_time, ec);
  423. #endif
  424. if (ec.failed())
  425. {
  426. llwarns << "Failure to touch \"" << filename
  427. << "\". Reason: " << ec.message() << llendl;
  428. }
  429. }
  430. }