Ansariel 2021-11-16 13:04:22 +01:00
commit 3322eadea0
16 changed files with 158 additions and 73 deletions

View File

@ -228,8 +228,15 @@ Ansariel Hiller
SL-13364
SL-13858
SL-13697
SL-14939
SL-14940
SL-14941
SL-13395
SL-3136
SL-15200
SL-15226
SL-15227
SL-15398
Aralara Rajal
Arare Chantilly
CHUIBUG-191

View File

@ -103,7 +103,8 @@ BOOL LLApp::sDisableCrashlogger = FALSE;
BOOL LLApp::sLogInSignal = FALSE;
// static
LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status
// Keeps track of application status
LLScalarCond<LLApp::EAppStatus> LLApp::sStatus{LLApp::APP_STATUS_STOPPED};
LLAppErrorHandler LLApp::sErrorHandler = NULL;
BOOL LLApp::sErrorThreadRunning = FALSE;
@ -449,7 +450,8 @@ static std::map<LLApp::EAppStatus, const char*> statusDesc
// static
void LLApp::setStatus(EAppStatus status)
{
sStatus = status;
// notify everyone waiting on sStatus any time its value changes
sStatus.set_all(status);
// This can also happen very late in the application lifecycle -- don't
// resurrect a deleted LLSingleton
@ -479,6 +481,12 @@ void LLApp::setError()
setStatus(APP_STATUS_ERROR);
}
// static
bool LLApp::sleep(F32Milliseconds duration)
{
return ! sStatus.wait_for_unequal(duration, APP_STATUS_RUNNING);
}
void LLApp::setDebugFileNames(const std::string &path)
{
mStaticDebugFileName = path + "static_debug_info.log";
@ -511,28 +519,28 @@ void LLApp::setStopped()
// static
bool LLApp::isStopped()
{
return (APP_STATUS_STOPPED == sStatus);
return (APP_STATUS_STOPPED == sStatus.get());
}
// static
bool LLApp::isRunning()
{
return (APP_STATUS_RUNNING == sStatus);
return (APP_STATUS_RUNNING == sStatus.get());
}
// static
bool LLApp::isError()
{
return (APP_STATUS_ERROR == sStatus);
return (APP_STATUS_ERROR == sStatus.get());
}
// static
bool LLApp::isQuitting()
{
return (APP_STATUS_QUITTING == sStatus);
return (APP_STATUS_QUITTING == sStatus.get());
}
// static

View File

@ -28,9 +28,11 @@
#define LL_LLAPP_H
#include <map>
#include "llcond.h"
#include "llrun.h"
#include "llsd.h"
#include <atomic>
#include <chrono>
// Forward declarations
class LLErrorThread;
class LLLiveFile;
@ -207,6 +209,36 @@ public:
static bool isExiting(); // Either quitting or error (app is exiting, cleanly or not)
static int getPid();
//
// Sleep for specified time while still running
//
// For use by a coroutine or thread that performs some maintenance on a
// periodic basis. (See also LLEventTimer.) This method supports the
// pattern of an "infinite" loop that sleeps for some time, performs some
// action, then sleeps again. The trouble with literally sleeping a worker
// thread is that it could potentially sleep right through attempted
// application shutdown. This method avoids that by returning false as
// soon as the application status changes away from APP_STATUS_RUNNING
// (isRunning()).
//
// sleep() returns true if it sleeps undisturbed for the entire specified
// duration. The idea is that you can code 'while sleep(duration) ...',
// which will break the loop once shutdown begins.
//
// Since any time-based LLUnit should be implicitly convertible to
// F32Milliseconds, accept that specific type as a proxy.
static bool sleep(F32Milliseconds duration);
// Allow any duration defined in terms of <chrono>.
// One can imagine a wonderfully general bidirectional conversion system
// between any type derived from LLUnits::LLUnit<T, LLUnits::Seconds> and
// any std::chrono::duration -- but that doesn't yet exist.
template <typename Rep, typename Period>
bool sleep(const std::chrono::duration<Rep, Period>& duration)
{
// wait_for_unequal() has the opposite bool return convention
return ! sStatus.wait_for_unequal(duration, APP_STATUS_RUNNING);
}
/** @name Error handling methods */
//@{
/**
@ -236,8 +268,8 @@ public:
// Return the Google Breakpad minidump filename after a crash.
char *getMiniDumpFilename() { return mMinidumpPath; }
std::string* getStaticDebugFile() { return &mStaticDebugFileName; }
std::string* getDynamicDebugFile() { return &mDynamicDebugFileName; }
std::string* getStaticDebugFile() { return &mStaticDebugFileName; }
std::string* getDynamicDebugFile() { return &mDynamicDebugFileName; }
// Write out a Google Breakpad minidump file.
void writeMiniDump();
@ -265,7 +297,7 @@ public:
protected:
static void setStatus(EAppStatus status); // Use this to change the application status.
static EAppStatus sStatus; // Reflects current application status
static LLScalarCond<EAppStatus> sStatus; // Reflects current application status
static BOOL sErrorThreadRunning; // Set while the error thread is running
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
std::wstring mCrashReportPipeStr; //Name of pipe to use for crash reporting.

View File

@ -27,7 +27,6 @@
#ifndef LL_LLTHREAD_H
#define LL_LLTHREAD_H
#include "llapp.h"
#include "llapr.h"
#include "boost/intrusive_ptr.hpp"
#include "llrefcount.h"

View File

@ -33,6 +33,7 @@
#include <iphlpapi.h>
#endif
#include "llapp.h"
#include "lldefs.h"
#include "llerror.h"

View File

@ -726,7 +726,7 @@ bool LLCrashLogger::init()
#if LL_WINDOWS
Sleep(1000);
#else
sleep(1);
::sleep(1);
#endif
locked = mKeyMaster.checkMaster();
}

View File

@ -31,6 +31,7 @@
*/
#include "linden_common.h"
#include "llapp.h"
#include "llassettype.h"
#include "lldir.h"
#include <boost/filesystem.hpp>
@ -43,8 +44,6 @@
static const char* subdirs = "0123456789abcdef";
LLDiskCache::LLDiskCache(const std::string cache_dir,
// <FS:Ansariel> Fix integer overflow
//const int max_size_bytes,
const uintmax_t max_size_bytes,
const bool enable_cache_debug_info) :
mCacheDir(cache_dir),
@ -69,6 +68,34 @@ LLDiskCache::LLDiskCache(const std::string cache_dir,
// </FS:Beq>
}
// WARNING: purge() is called by LLPurgeDiskCacheThread. As such it must
// NOT touch any LLDiskCache data without introducing and locking a mutex!
// Interaction through the filesystem itself should be safe. Lets say thread
// A is accessing the cache file for reading/writing and thread B is trimming
// the cache. Lets also assume using llifstream to open a file and
// boost::filesystem::remove are not atomic (which will be pretty much the
// case).
// Now, A is trying to open the file using llifstream ctor. It does some
// checks if the file exists and whatever else it might be doing, but has not
// issued the call to the OS to actually open the file yet. Now B tries to
// delete the file: If the file has been already marked as in use by the OS,
// deleting the file will fail and B will continue with the next file. A can
// safely continue opening the file. If the file has not yet been marked as in
// use, B will delete the file. Now A actually wants to open it, operation
// will fail, subsequent check via llifstream.is_open will fail, asset will
// have to be re-requested. (Assuming here the viewer will actually handle
// this situation properly, that can also happen if there is a file containing
// garbage.)
// Other situation: B is trimming the cache and A wants to read a file that is
// about to get deleted. boost::filesystem::remove does whatever it is doing
// before actually deleting the file. If A opens the file before the file is
// actually gone, the OS call from B to delete the file will fail since the OS
// will prevent this. B continues with the next file. If the file is already
// gone before A finally gets to open it, this operation will fail and the
// asset will have to be re-requested.
void LLDiskCache::purge()
{
if (mEnableCacheDebugInfo)
@ -151,15 +178,12 @@ void LLDiskCache::purge()
{
del++; // Extra accounting to track the retention of static assets
// </FS:Beq>
// <FS:Ansariel> Do not crash if we cannot delete the file for some reason
//boost::filesystem::remove(entry.second.second);
boost::filesystem::remove(entry.second.second, ec);
if (ec.failed())
{
LL_WARNS() << "Failed to delete cache file " << entry.second.second << ": " << ec.message() << LL_ENDL;
}
}
// </FS:Ansariel>
}
else
{
@ -431,8 +455,6 @@ void LLDiskCache::clearCache()
{
if (remove_entry.string().find(mCacheFilenamePrefix) != std::string::npos)
{
// <FS:Ansariel> Do not crash if we cannot delete the file for some reason
//boost::filesystem::remove(entry);
const boost::filesystem::path remove_path = remove_entry;
++entry;
boost::filesystem::remove(remove_path, ec);
@ -440,7 +462,6 @@ void LLDiskCache::clearCache()
{
LL_WARNS() << "Failed to delete cache file " << remove_path.string() << ": " << ec.message() << LL_ENDL;
}
// </FS:Ansariel>
}
else
{
@ -502,26 +523,17 @@ uintmax_t LLDiskCache::dirFileSize(const std::string dir)
return total_file_size;
}
// <FS:Ansariel> Regular disk cache cleanup
FSPurgeDiskCacheThread::FSPurgeDiskCacheThread() :
LLPurgeDiskCacheThread::LLPurgeDiskCacheThread() :
LLThread("PurgeDiskCacheThread", nullptr)
{
}
void FSPurgeDiskCacheThread::run()
void LLPurgeDiskCacheThread::run()
{
constexpr F64 CHECK_INTERVAL = 60;
mTimer.setTimerExpirySec(CHECK_INTERVAL);
mTimer.start();
constexpr std::chrono::seconds CHECK_INTERVAL{60};
do
while (LLApp::instance()->sleep(CHECK_INTERVAL))
{
if (mTimer.checkExpirationAndReset(CHECK_INTERVAL))
{
LLDiskCache::instance().purge();
}
ms_sleep(100);
} while (!isQuitting());
LLDiskCache::instance().purge();
}
}
// </FS:Ansariel>

View File

@ -86,8 +86,6 @@ class LLDiskCache :
* The maximum size of the cache in bytes - Based on the
* setting at 'CacheSize' and 'DiskCachePercentOfTotal'
*/
// <FS:Ansariel> Fix integer overflow
//const int max_size_bytes,
const uintmax_t max_size_bytes,
/**
* A flag that enables extra cache debugging so that
@ -128,6 +126,13 @@ class LLDiskCache :
/**
* Purge the oldest items in the cache so that the combined size of all files
* is no bigger than mMaxSizeBytes.
*
* WARNING: purge() is called by LLPurgeDiskCacheThread. As such it must
* NOT touch any LLDiskCache data without introducing and locking a mutex!
*
* Purging the disk cache involves nontrivial work on the viewer's
* filesystem. If called on the main thread, this causes a noticeable
* freeze.
*/
void purge();
@ -200,17 +205,12 @@ class LLDiskCache :
std::vector<std::string> mSkipList; // <FS:Beq/> Vector of "static" untouchable assets that should never be purged
};
// <FS:Ansariel> Regular disk cache cleanup
class FSPurgeDiskCacheThread : public LLThread
class LLPurgeDiskCacheThread : public LLThread
{
public:
FSPurgeDiskCacheThread();
LLPurgeDiskCacheThread();
protected:
void run() override;
private:
LLTimer mTimer;
};
// </FS:Ansariel>
#endif // _LLDISKCACHE

View File

@ -48,6 +48,28 @@ LLFileSystem::LLFileSystem(const LLUUID& file_id, const LLAssetType::EType file_
mPosition = 0;
mBytesRead = 0;
mMode = mode;
// This block of code was originally called in the read() method but after comments here:
// https://bitbucket.org/lindenlab/viewer/commits/e28c1b46e9944f0215a13cab8ee7dded88d7fc90#comment-10537114
// we decided to follow Henri's suggestion and move the code to update the last access time here.
if (mode == LLFileSystem::READ)
{
// build the filename (TODO: we do this in a few places - perhaps we should factor into a single function)
std::string id;
mFileID.toString(id);
const std::string extra_info = "";
const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id, mFileType, extra_info);
// update the last access time for the file if it exists - this is required
// even though we are reading and not writing because this is the
// way the cache works - it relies on a valid "last accessed time" for
// each file so it knows how to remove the oldest, unused files
bool exists = gDirUtilp->fileExists(filename);
if (exists)
{
LLDiskCache::getInstance()->updateFileAccessTime(filename);
}
}
}
LLFileSystem::~LLFileSystem()
@ -153,8 +175,6 @@ S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType fi
BOOL LLFileSystem::read(U8* buffer, S32 bytes)
{
FSZoneC(tracy::Color::Gold); // <FS:Beq> measure cache performance
// <FS:Ansariel> Cache fixes
//BOOL success = TRUE;
BOOL success = FALSE;
std::string id;
@ -182,9 +202,9 @@ BOOL LLFileSystem::read(U8* buffer, S32 bytes)
// file.close();
// mPosition += mBytesRead;
// if (!mBytesRead)
// if (mBytesRead)
// {
// success = FALSE;
// success = TRUE;
// }
//}
LLFILE* file = LLFile::fopen(filename, "rb");
@ -206,12 +226,6 @@ BOOL LLFileSystem::read(U8* buffer, S32 bytes)
}
// </FS:Ansariel>
// update the last access time for the file - this is required
// even though we are reading and not writing because this is the
// way the cache works - it relies on a valid "last accessed time" for
// each file so it knows how to remove the oldest, unused files
LLDiskCache::getInstance()->updateFileAccessTime(filename);
return success;
}

View File

@ -46,6 +46,7 @@
#include "apr_poll.h"
// linden library headers
#include "llapp.h"
#include "indra_constants.h"
#include "lldir.h"
#include "llerror.h"

View File

@ -28,6 +28,7 @@
#include "linden_common.h"
#include "llapp.h"
#include "llpluginprocessparent.h"
#include "llpluginmessagepipe.h"
#include "llpluginmessageclasses.h"

View File

@ -738,7 +738,7 @@ LLAppViewer* LLAppViewer::sInstance = NULL;
LLTextureCache* LLAppViewer::sTextureCache = NULL;
LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL;
LLTextureFetch* LLAppViewer::sTextureFetch = NULL;
FSPurgeDiskCacheThread* LLAppViewer::sPurgeDiskCacheThread = NULL; // <FS:Ansariel> Regular disk cache cleanup
LLPurgeDiskCacheThread* LLAppViewer::sPurgeDiskCacheThread = NULL;
std::string getRuntime()
{
@ -2478,7 +2478,7 @@ bool LLAppViewer::cleanup()
sTextureFetch->shutdown();
sTextureCache->shutdown();
sImageDecodeThread->shutdown();
sPurgeDiskCacheThread->shutdown(); // <FS:Ansariel> Regular disk cache cleanup
sPurgeDiskCacheThread->shutdown();
sTextureFetch->shutDownTextureCacheThread() ;
sTextureFetch->shutDownImageDecodeThread() ;
@ -2501,10 +2501,8 @@ bool LLAppViewer::cleanup()
sImageDecodeThread = NULL;
delete mFastTimerLogThread;
mFastTimerLogThread = NULL;
// <FS:Ansariel> Regular disk cache cleanup
delete sPurgeDiskCacheThread;
sPurgeDiskCacheThread = NULL;
// </FS:Ansariel>
if (LLFastTimerView::sAnalyzePerformance)
{
@ -2607,7 +2605,7 @@ bool LLAppViewer::initThreads()
sImageDecodeThread,
enable_threads && true,
app_metrics_qa_mode);
LLAppViewer::sPurgeDiskCacheThread = new FSPurgeDiskCacheThread(); // <FS:Ansariel> Regular disk cache cleanup
LLAppViewer::sPurgeDiskCacheThread = new LLPurgeDiskCacheThread();
if (LLTrace::BlockTimer::sLog || LLTrace::BlockTimer::sMetricLog)
{
@ -5005,8 +5003,11 @@ void LLAppViewer::migrateCacheDirectory()
//static
U32 LLAppViewer::getTextureCacheVersion()
{
//viewer texture cache version, change if the texture cache format changes.
const U32 TEXTURE_CACHE_VERSION = 8;
// Viewer texture cache version, change if the texture cache format changes.
// 2021-03-10 Bumping up by one to help obviate texture cache issues with
// Simple Cache Viewer - see SL-14985 for more information
//const U32 TEXTURE_CACHE_VERSION = 8;
const U32 TEXTURE_CACHE_VERSION = 9;
return TEXTURE_CACHE_VERSION ;
}
@ -5039,8 +5040,6 @@ bool LLAppViewer::initCache()
//const unsigned int disk_cache_mb = cache_total_size_mb * disk_cache_percent / 100;
const unsigned int disk_cache_mb = gSavedSettings.getU32("FSDiskCacheSize");
// </FS:Ansariel>
// <FS:Ansariel> Fix integer overflow
//const unsigned int disk_cache_bytes = disk_cache_mb * 1024 * 1024;
const uintmax_t disk_cache_bytes = disk_cache_mb * 1024ULL * 1024ULL;
const bool enable_cache_debug_info = gSavedSettings.getBOOL("EnableDiskCacheDebugInfo");
@ -5134,7 +5133,6 @@ bool LLAppViewer::initCache()
LLDiskCache::getInstance()->purge();
}
}
// <FS:Ansariel> Regular disk cache cleanup
LLAppViewer::getPurgeDiskCacheThread()->start();
// <FS:Ansariel> FIRE-13066

View File

@ -58,8 +58,8 @@ class LLImageDecodeThread;
class LLTextureFetch;
class LLWatchdogTimeout;
class LLViewerJoystick;
class LLPurgeDiskCacheThread;
class LLViewerRegion;
class FSPurgeDiskCacheThread; // <FS:Ansariel> Regular disk cache cleanup
extern LLTrace::BlockTimerStatHandle FTM_FRAME;
@ -118,7 +118,7 @@ public:
static LLTextureCache* getTextureCache() { return sTextureCache; }
static LLImageDecodeThread* getImageDecodeThread() { return sImageDecodeThread; }
static LLTextureFetch* getTextureFetch() { return sTextureFetch; }
static FSPurgeDiskCacheThread* getPurgeDiskCacheThread() { return sPurgeDiskCacheThread; } // <FS:Ansariel> Regular disk cache cleanup
static LLPurgeDiskCacheThread* getPurgeDiskCacheThread() { return sPurgeDiskCacheThread; }
static U32 getTextureCacheVersion() ;
static U32 getObjectCacheVersion() ;
@ -306,7 +306,7 @@ private:
static LLTextureCache* sTextureCache;
static LLImageDecodeThread* sImageDecodeThread;
static LLTextureFetch* sTextureFetch;
static FSPurgeDiskCacheThread* sPurgeDiskCacheThread; // <FS:Ansariel> Regular disk cache cleanup
static LLPurgeDiskCacheThread* sPurgeDiskCacheThread;
S32 mNumSessions;

View File

@ -1738,6 +1738,10 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c
file.read(buffer, bytes);
if (headerReceived(mesh_params, buffer, bytes) == MESH_OK)
{
std::string mid;
mesh_params.getSculptID().toString(mid);
LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mid << " - was retrieved from the cache." << LL_ENDL;
// Found mesh in cache
return true;
}
@ -1753,8 +1757,13 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c
constructUrl(mesh_params.getSculptID(), &http_url, &legacy_cap_version);
// </FS:Ansariel> [UDP Assets]
if (!http_url.empty())
{
std::string mid;
mesh_params.getSculptID().toString(mid);
LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mid << " - was retrieved from the simulator." << LL_ENDL;
//grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits
//within the first 4KB
//NOTE -- this will break of headers ever exceed 4KB
@ -1838,6 +1847,11 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK)
{
delete[] buffer;
std::string mid;
mesh_id.toString(mid);
LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mid << " - was retrieved from the cache." << LL_ENDL;
return true;
}
}
@ -1852,9 +1866,13 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
int legacy_cap_version(0);
constructUrl(mesh_id, &http_url, &legacy_cap_version);
// </FS:Ansariel> [UDP Assets]
if (!http_url.empty())
{
std::string mid;
mesh_id.toString(mid);
LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mid << " - was retrieved from the simulator." << LL_ENDL;
LLMeshHandlerBase::ptr_t handler(new LLMeshLODHandler(mesh_params, lod, offset, size));
// <FS:Ansariel> [UDP Assets]
//LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);

View File

@ -303,10 +303,7 @@ void LLViewerAssetStorage::storeAssetData(
legacy->mUpCallback = callback;
legacy->mUserData = user_data;
// <FS:Ansariel> Fix broken asset upload
//LLFileSystem file(asset_id, asset_type, LLFileSystem::WRITE);
LLFileSystem file(asset_id, asset_type, LLFileSystem::APPEND);
// </FS:Ansariel>
const S32 buf_size = 65536;
U8 copy_buf[buf_size];

View File

@ -498,10 +498,7 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile()
infile.open(filename, LL_APR_RB, NULL, &file_size);
if (infile.getFileHandle())
{
// <FS:Ansariel> Fix broken asset upload
//LLFileSystem file(getAssetId(), assetType, LLFileSystem::WRITE);
LLFileSystem file(getAssetId(), assetType, LLFileSystem::APPEND);
// </FS:Ansariel>
const S32 buf_size = 65536;
U8 copy_buf[buf_size];