Skip to content

Commit 0ee528c

Browse files
committed
Mitigate asset fetch thread stalls from LLDiskCache mutex contention and trivial cleanup
Move LLDiskCache::updateFileAccessTime to LLFilesystem as it's the only user of that function. Change mCacheDir and LLDiskCache::metaDataToFilepath to statics.
1 parent 6dbf1ca commit 0ee528c

File tree

4 files changed

+108
-132
lines changed

4 files changed

+108
-132
lines changed

indra/llfilesystem/lldiskcache.cpp

Lines changed: 25 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,25 @@
3939

4040
#include "lldiskcache.h"
4141

42+
/**
43+
* The prefix inserted at the start of a cache file filename to
44+
* help identify it as a cache file. It's probably not required
45+
* (just the presence in the cache folder is enough) but I am
46+
* paranoid about the cache folder being set to something bad
47+
* like the users' OS system dir by mistake or maliciously and
48+
* this will help to offset any damage if that happens.
49+
*/
50+
static const std::string CACHE_FILENAME_PREFIX("sl_cache");
51+
52+
std::string LLDiskCache::sCacheDir;
53+
4254
LLDiskCache::LLDiskCache(const std::string cache_dir,
4355
const uintmax_t max_size_bytes,
4456
const bool enable_cache_debug_info) :
45-
mCacheDir(cache_dir),
4657
mMaxSizeBytes(max_size_bytes),
4758
mEnableCacheDebugInfo(enable_cache_debug_info)
4859
{
49-
mCacheFilenamePrefix = "sl_cache";
50-
60+
sCacheDir = cache_dir;
5161
LLFile::mkdir(cache_dir);
5262
}
5363

@@ -83,7 +93,7 @@ void LLDiskCache::purge()
8393
{
8494
if (mEnableCacheDebugInfo)
8595
{
86-
LL_INFOS() << "Total dir size before purge is " << dirFileSize(mCacheDir) << LL_ENDL;
96+
LL_INFOS() << "Total dir size before purge is " << dirFileSize(sCacheDir) << LL_ENDL;
8797
}
8898

8999
boost::system::error_code ec;
@@ -93,9 +103,9 @@ void LLDiskCache::purge()
93103
std::vector<file_info_t> file_info;
94104

95105
#if LL_WINDOWS
96-
std::wstring cache_path(utf8str_to_utf16str(mCacheDir));
106+
std::wstring cache_path(utf8str_to_utf16str(sCacheDir));
97107
#else
98-
std::string cache_path(mCacheDir);
108+
std::string cache_path(sCacheDir);
99109
#endif
100110
if (boost::filesystem::is_directory(cache_path, ec) && !ec.failed())
101111
{
@@ -104,7 +114,7 @@ void LLDiskCache::purge()
104114
{
105115
if (boost::filesystem::is_regular_file(*iter, ec) && !ec.failed())
106116
{
107-
if ((*iter).path().string().find(mCacheFilenamePrefix) != std::string::npos)
117+
if ((*iter).path().string().find(CACHE_FILENAME_PREFIX) != std::string::npos)
108118
{
109119
uintmax_t file_size = boost::filesystem::file_size(*iter, ec);
110120
if (ec.failed())
@@ -181,7 +191,7 @@ void LLDiskCache::purge()
181191
LL_INFOS() << line.str() << LL_ENDL;
182192
}
183193

184-
LL_INFOS() << "Total dir size after purge is " << dirFileSize(mCacheDir) << LL_ENDL;
194+
LL_INFOS() << "Total dir size after purge is " << dirFileSize(sCacheDir) << LL_ENDL;
185195
LL_INFOS() << "Cache purge took " << execute_time << " ms to execute for " << file_info.size() << " files" << LL_ENDL;
186196
}
187197
}
@@ -236,97 +246,17 @@ const std::string LLDiskCache::assetTypeToString(LLAssetType::EType at)
236246
return std::string("UNKNOWN");
237247
}
238248

239-
const std::string LLDiskCache::metaDataToFilepath(const std::string id,
240-
LLAssetType::EType at,
241-
const std::string extra_info)
249+
const std::string LLDiskCache::metaDataToFilepath(const std::string& id, LLAssetType::EType at)
242250
{
243-
std::ostringstream file_path;
244-
245-
file_path << mCacheDir;
246-
file_path << gDirUtilp->getDirDelimiter();
247-
file_path << mCacheFilenamePrefix;
248-
file_path << "_";
249-
file_path << id;
250-
file_path << "_";
251-
file_path << (extra_info.empty() ? "0" : extra_info);
252-
//file_path << "_";
253-
//file_path << assetTypeToString(at); // see SL-14210 Prune descriptive tag from new cache filenames
254-
// for details of why it was removed. Note that if you put it
255-
// back or change the format of the filename, the cache files
256-
// files will be invalidated (and perhaps, more importantly,
257-
// never deleted unless you delete them manually).
258-
file_path << ".asset";
259-
260-
return file_path.str();
261-
}
262-
263-
void LLDiskCache::updateFileAccessTime(const std::string file_path)
264-
{
265-
/**
266-
* Threshold in time_t units that is used to decide if the last access time
267-
* time of the file is updated or not. Added as a precaution for the concern
268-
* outlined in SL-14582 about frequent writes on older SSDs reducing their
269-
* lifespan. I think this is the right place for the threshold value - rather
270-
* than it being a pref - do comment on that Jira if you disagree...
271-
*
272-
* Let's start with 1 hour in time_t units and see how that unfolds
273-
*/
274-
const std::time_t time_threshold = 1 * 60 * 60;
275-
276-
// current time
277-
const std::time_t cur_time = std::time(nullptr);
278-
279-
boost::system::error_code ec;
280-
#if LL_WINDOWS
281-
// file last write time
282-
const std::time_t last_write_time = boost::filesystem::last_write_time(utf8str_to_utf16str(file_path), ec);
283-
if (ec.failed())
284-
{
285-
LL_WARNS() << "Failed to read last write time for cache file " << file_path << ": " << ec.message() << LL_ENDL;
286-
return;
287-
}
288-
289-
// delta between cur time and last time the file was written
290-
const std::time_t delta_time = cur_time - last_write_time;
291-
292-
// we only write the new value if the time in time_threshold has elapsed
293-
// before the last one
294-
if (delta_time > time_threshold)
295-
{
296-
boost::filesystem::last_write_time(utf8str_to_utf16str(file_path), cur_time, ec);
297-
}
298-
#else
299-
// file last write time
300-
const std::time_t last_write_time = boost::filesystem::last_write_time(file_path, ec);
301-
if (ec.failed())
302-
{
303-
LL_WARNS() << "Failed to read last write time for cache file " << file_path << ": " << ec.message() << LL_ENDL;
304-
return;
305-
}
306-
307-
// delta between cur time and last time the file was written
308-
const std::time_t delta_time = cur_time - last_write_time;
309-
310-
// we only write the new value if the time in time_threshold has elapsed
311-
// before the last one
312-
if (delta_time > time_threshold)
313-
{
314-
boost::filesystem::last_write_time(file_path, cur_time, ec);
315-
}
316-
#endif
317-
318-
if (ec.failed())
319-
{
320-
LL_WARNS() << "Failed to update last write time for cache file " << file_path << ": " << ec.message() << LL_ENDL;
321-
}
251+
return llformat("%s%s%s_%s_0.asset", sCacheDir.c_str(), gDirUtilp->getDirDelimiter().c_str(), CACHE_FILENAME_PREFIX.c_str(), id.c_str());
322252
}
323253

324254
const std::string LLDiskCache::getCacheInfo()
325255
{
326256
std::ostringstream cache_info;
327257

328258
F32 max_in_mb = (F32)mMaxSizeBytes / (1024.0f * 1024.0f);
329-
F32 percent_used = ((F32)dirFileSize(mCacheDir) / (F32)mMaxSizeBytes) * 100.0f;
259+
F32 percent_used = ((F32)dirFileSize(sCacheDir) / (F32)mMaxSizeBytes) * 100.0f;
330260

331261
cache_info << std::fixed;
332262
cache_info << std::setprecision(1);
@@ -346,9 +276,9 @@ void LLDiskCache::clearCache()
346276
*/
347277
boost::system::error_code ec;
348278
#if LL_WINDOWS
349-
std::wstring cache_path(utf8str_to_utf16str(mCacheDir));
279+
std::wstring cache_path(utf8str_to_utf16str(sCacheDir));
350280
#else
351-
std::string cache_path(mCacheDir);
281+
std::string cache_path(sCacheDir);
352282
#endif
353283
if (boost::filesystem::is_directory(cache_path, ec) && !ec.failed())
354284
{
@@ -357,7 +287,7 @@ void LLDiskCache::clearCache()
357287
{
358288
if (boost::filesystem::is_regular_file(*iter, ec) && !ec.failed())
359289
{
360-
if ((*iter).path().string().find(mCacheFilenamePrefix) != std::string::npos)
290+
if ((*iter).path().string().find(CACHE_FILENAME_PREFIX) != std::string::npos)
361291
{
362292
boost::filesystem::remove(*iter, ec);
363293
if (ec.failed())
@@ -431,7 +361,7 @@ uintmax_t LLDiskCache::dirFileSize(const std::string dir)
431361
{
432362
if (boost::filesystem::is_regular_file(*iter, ec) && !ec.failed())
433363
{
434-
if ((*iter).path().string().find(mCacheFilenamePrefix) != std::string::npos)
364+
if ((*iter).path().string().find(CACHE_FILENAME_PREFIX) != std::string::npos)
435365
{
436366
uintmax_t file_size = boost::filesystem::file_size(*iter, ec);
437367
if (!ec.failed())

indra/llfilesystem/lldiskcache.h

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,9 @@ class LLDiskCache :
104104
* so many things had to be pushed back there to accomodate it, that I
105105
* decided to move it here. Still not sure that's completely right.
106106
*/
107-
const std::string metaDataToFilepath(const std::string id,
108-
LLAssetType::EType at,
109-
const std::string extra_info);
107+
static const std::string metaDataToFilepath(const std::string& id,
108+
LLAssetType::EType at);
110109

111-
/**
112-
* Update the "last write time" of a file to "now". This must be called whenever a
113-
* file in the cache is read (not written) so that the last time the file was
114-
* accessed is up to date (This is used in the mechanism for purging the cache)
115-
*/
116-
void updateFileAccessTime(const std::string file_path);
117110

118111
/**
119112
* Purge the oldest items in the cache so that the combined size of all files
@@ -170,17 +163,7 @@ class LLDiskCache :
170163
* setting could potentially point it at a non-cache directory (for example,
171164
* the Windows System dir) with disastrous results.
172165
*/
173-
std::string mCacheDir;
174-
175-
/**
176-
* The prefix inserted at the start of a cache file filename to
177-
* help identify it as a cache file. It's probably not required
178-
* (just the presence in the cache folder is enough) but I am
179-
* paranoid about the cache folder being set to something bad
180-
* like the users' OS system dir by mistake or maliciously and
181-
* this will help to offset any damage if that happens.
182-
*/
183-
std::string mCacheFilenamePrefix;
166+
static std::string sCacheDir;
184167

185168
/**
186169
* When enabled, displays additional debugging information in

indra/llfilesystem/llfilesystem.cpp

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
#include "llfasttimer.h"
3535
#include "lldiskcache.h"
3636

37+
#include "boost/filesystem.hpp"
38+
3739
const S32 LLFileSystem::READ = 0x00000001;
3840
const S32 LLFileSystem::WRITE = 0x00000002;
3941
const S32 LLFileSystem::READ_WRITE = 0x00000003; // LLFileSystem::READ & LLFileSystem::WRITE
@@ -56,9 +58,8 @@ LLFileSystem::LLFileSystem(const LLUUID& file_id, const LLAssetType::EType file_
5658
{
5759
// build the filename (TODO: we do this in a few places - perhaps we should factor into a single function)
5860
std::string id;
59-
mFileID.toString(id);
60-
const std::string extra_info = "";
61-
const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id, mFileType, extra_info);
61+
mFileID.asString();
62+
const std::string filename = LLDiskCache::metaDataToFilepath(id, mFileType);
6263

6364
// update the last access time for the file if it exists - this is required
6465
// even though we are reading and not writing because this is the
@@ -67,7 +68,7 @@ LLFileSystem::LLFileSystem(const LLUUID& file_id, const LLAssetType::EType file_
6768
bool exists = gDirUtilp->fileExists(filename);
6869
if (exists)
6970
{
70-
LLDiskCache::getInstance()->updateFileAccessTime(filename);
71+
updateFileAccessTime(filename);
7172
}
7273
}
7374
}
@@ -82,8 +83,7 @@ bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType fil
8283
LL_PROFILE_ZONE_SCOPED;
8384
std::string id_str;
8485
file_id.toString(id_str);
85-
const std::string extra_info = "";
86-
const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info);
86+
const std::string filename = LLDiskCache::metaDataToFilepath(id_str, file_type);
8787

8888
llifstream file(filename, std::ios::binary);
8989
if (file.is_open())
@@ -99,8 +99,7 @@ bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType fi
9999
{
100100
std::string id_str;
101101
file_id.toString(id_str);
102-
const std::string extra_info = "";
103-
const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info);
102+
const std::string filename = LLDiskCache::metaDataToFilepath(id_str, file_type);
104103

105104
LLFile::remove(filename.c_str(), suppress_error);
106105

@@ -113,12 +112,11 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp
113112
{
114113
std::string old_id_str;
115114
old_file_id.toString(old_id_str);
116-
const std::string extra_info = "";
117-
const std::string old_filename = LLDiskCache::getInstance()->metaDataToFilepath(old_id_str, old_file_type, extra_info);
115+
const std::string old_filename = LLDiskCache::metaDataToFilepath(old_id_str, old_file_type);
118116

119117
std::string new_id_str;
120118
new_file_id.toString(new_id_str);
121-
const std::string new_filename = LLDiskCache::getInstance()->metaDataToFilepath(new_id_str, new_file_type, extra_info);
119+
const std::string new_filename = LLDiskCache::metaDataToFilepath(new_id_str, new_file_type);
122120

123121
// Rename needs the new file to not exist.
124122
LLFileSystem::removeFile(new_file_id, new_file_type, ENOENT);
@@ -140,8 +138,7 @@ S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType fi
140138
{
141139
std::string id_str;
142140
file_id.toString(id_str);
143-
const std::string extra_info = "";
144-
const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info);
141+
const std::string filename = LLDiskCache::metaDataToFilepath(id_str, file_type);
145142

146143
S32 file_size = 0;
147144
llifstream file(filename, std::ios::binary);
@@ -160,8 +157,7 @@ bool LLFileSystem::read(U8* buffer, S32 bytes)
160157

161158
std::string id;
162159
mFileID.toString(id);
163-
const std::string extra_info = "";
164-
const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id, mFileType, extra_info);
160+
const std::string filename = LLDiskCache::metaDataToFilepath(id, mFileType);
165161

166162
llifstream file(filename, std::ios::binary);
167163
if (file.is_open())
@@ -205,8 +201,7 @@ bool LLFileSystem::write(const U8* buffer, S32 bytes)
205201
{
206202
std::string id_str;
207203
mFileID.toString(id_str);
208-
const std::string extra_info = "";
209-
const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, mFileType, extra_info);
204+
const std::string filename = LLDiskCache::metaDataToFilepath(id_str, mFileType);
210205

211206
bool success = false;
212207

@@ -325,3 +320,64 @@ bool LLFileSystem::remove()
325320

326321
return true;
327322
}
323+
324+
void LLFileSystem::updateFileAccessTime(const std::string& file_path)
325+
{
326+
/**
327+
* Threshold in time_t units that is used to decide if the last access time
328+
* time of the file is updated or not. Added as a precaution for the concern
329+
* outlined in SL-14582 about frequent writes on older SSDs reducing their
330+
* lifespan. I think this is the right place for the threshold value - rather
331+
* than it being a pref - do comment on that Jira if you disagree...
332+
*
333+
* Let's start with 1 hour in time_t units and see how that unfolds
334+
*/
335+
const std::time_t time_threshold = 1 * 60 * 60;
336+
337+
// current time
338+
const std::time_t cur_time = std::time(nullptr);
339+
340+
boost::system::error_code ec;
341+
#if LL_WINDOWS
342+
// file last write time
343+
const std::time_t last_write_time = boost::filesystem::last_write_time(utf8str_to_utf16str(file_path), ec);
344+
if (ec.failed())
345+
{
346+
LL_WARNS() << "Failed to read last write time for cache file " << file_path << ": " << ec.message() << LL_ENDL;
347+
return;
348+
}
349+
350+
// delta between cur time and last time the file was written
351+
const std::time_t delta_time = cur_time - last_write_time;
352+
353+
// we only write the new value if the time in time_threshold has elapsed
354+
// before the last one
355+
if (delta_time > time_threshold)
356+
{
357+
boost::filesystem::last_write_time(utf8str_to_utf16str(file_path), cur_time, ec);
358+
}
359+
#else
360+
// file last write time
361+
const std::time_t last_write_time = boost::filesystem::last_write_time(file_path, ec);
362+
if (ec.failed())
363+
{
364+
LL_WARNS() << "Failed to read last write time for cache file " << file_path << ": " << ec.message() << LL_ENDL;
365+
return;
366+
}
367+
368+
// delta between cur time and last time the file was written
369+
const std::time_t delta_time = cur_time - last_write_time;
370+
371+
// we only write the new value if the time in time_threshold has elapsed
372+
// before the last one
373+
if (delta_time > time_threshold)
374+
{
375+
boost::filesystem::last_write_time(file_path, cur_time, ec);
376+
}
377+
#endif
378+
379+
if (ec.failed())
380+
{
381+
LL_WARNS() << "Failed to update last write time for cache file " << file_path << ": " << ec.message() << LL_ENDL;
382+
}
383+
}

0 commit comments

Comments
 (0)