diff --git a/doc/contributions.txt b/doc/contributions.txt index 0909bb52bc..7d01ed7dcb 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -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 diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 4970fa6385..c00df59756 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -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::sStatus{LLApp::APP_STATUS_STOPPED}; LLAppErrorHandler LLApp::sErrorHandler = NULL; BOOL LLApp::sErrorThreadRunning = FALSE; @@ -449,7 +450,8 @@ static std::map 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 diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index 83f3bf3f93..c65fe21c9c 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -28,9 +28,11 @@ #define LL_LLAPP_H #include +#include "llcond.h" #include "llrun.h" #include "llsd.h" #include +#include // 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 . + // One can imagine a wonderfully general bidirectional conversion system + // between any type derived from LLUnits::LLUnit and + // any std::chrono::duration -- but that doesn't yet exist. + template + bool sleep(const std::chrono::duration& 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 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. diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 118c60d7a6..613431ee6a 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -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" diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp index 191f8512e0..94991373a8 100644 --- a/indra/llcommon/lluuid.cpp +++ b/indra/llcommon/lluuid.cpp @@ -33,6 +33,7 @@ #include #endif +#include "llapp.h" #include "lldefs.h" #include "llerror.h" diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index b7c6cae08a..b18f6341cc 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -726,7 +726,7 @@ bool LLCrashLogger::init() #if LL_WINDOWS Sleep(1000); #else - sleep(1); + ::sleep(1); #endif locked = mKeyMaster.checkMaster(); } diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index 589213733a..1ef3b0410c 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -31,6 +31,7 @@ */ #include "linden_common.h" +#include "llapp.h" #include "llassettype.h" #include "lldir.h" #include @@ -43,8 +44,6 @@ static const char* subdirs = "0123456789abcdef"; LLDiskCache::LLDiskCache(const std::string cache_dir, - // 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, // } +// 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. Let’s say thread +// A is accessing the cache file for reading/writing and thread B is trimming +// the cache. Let’s 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 // - // 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; } } - // } else { @@ -431,8 +455,6 @@ void LLDiskCache::clearCache() { if (remove_entry.string().find(mCacheFilenamePrefix) != std::string::npos) { - // 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; } - // } else { @@ -502,26 +523,17 @@ uintmax_t LLDiskCache::dirFileSize(const std::string dir) return total_file_size; } -// 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(); + } } -// diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/lldiskcache.h index 1164b428e0..0624c95700 100644 --- a/indra/llfilesystem/lldiskcache.h +++ b/indra/llfilesystem/lldiskcache.h @@ -86,8 +86,6 @@ class LLDiskCache : * The maximum size of the cache in bytes - Based on the * setting at 'CacheSize' and 'DiskCachePercentOfTotal' */ - // 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 mSkipList; // Vector of "static" untouchable assets that should never be purged }; -// Regular disk cache cleanup -class FSPurgeDiskCacheThread : public LLThread +class LLPurgeDiskCacheThread : public LLThread { public: - FSPurgeDiskCacheThread(); + LLPurgeDiskCacheThread(); protected: void run() override; - -private: - LLTimer mTimer; }; -// #endif // _LLDISKCACHE diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index a64070eb9d..61cc7756de 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -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); // measure cache performance - // 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) } // - // 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; } diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 9b5050af41..a9d03c425c 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -46,6 +46,7 @@ #include "apr_poll.h" // linden library headers +#include "llapp.h" #include "indra_constants.h" #include "lldir.h" #include "llerror.h" diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index 6c6f819415..7088cf4d4b 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -28,6 +28,7 @@ #include "linden_common.h" +#include "llapp.h" #include "llpluginprocessparent.h" #include "llpluginmessagepipe.h" #include "llpluginmessageclasses.h" diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index c8bbf0d7eb..4e02073b0a 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -738,7 +738,7 @@ LLAppViewer* LLAppViewer::sInstance = NULL; LLTextureCache* LLAppViewer::sTextureCache = NULL; LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL; LLTextureFetch* LLAppViewer::sTextureFetch = NULL; -FSPurgeDiskCacheThread* LLAppViewer::sPurgeDiskCacheThread = NULL; // 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(); // Regular disk cache cleanup + sPurgeDiskCacheThread->shutdown(); sTextureFetch->shutDownTextureCacheThread() ; sTextureFetch->shutDownImageDecodeThread() ; @@ -2501,10 +2501,8 @@ bool LLAppViewer::cleanup() sImageDecodeThread = NULL; delete mFastTimerLogThread; mFastTimerLogThread = NULL; - // Regular disk cache cleanup delete sPurgeDiskCacheThread; sPurgeDiskCacheThread = NULL; - // if (LLFastTimerView::sAnalyzePerformance) { @@ -2607,7 +2605,7 @@ bool LLAppViewer::initThreads() sImageDecodeThread, enable_threads && true, app_metrics_qa_mode); - LLAppViewer::sPurgeDiskCacheThread = new FSPurgeDiskCacheThread(); // 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"); // - // 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(); } } - // Regular disk cache cleanup LLAppViewer::getPurgeDiskCacheThread()->start(); // FIRE-13066 diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 08710e8cbb..a32f7a743f 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -58,8 +58,8 @@ class LLImageDecodeThread; class LLTextureFetch; class LLWatchdogTimeout; class LLViewerJoystick; +class LLPurgeDiskCacheThread; class LLViewerRegion; -class FSPurgeDiskCacheThread; // 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; } // 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; // Regular disk cache cleanup + static LLPurgeDiskCacheThread* sPurgeDiskCacheThread; S32 mNumSessions; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 0c5cc892ae..45916f7262 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -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); // [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); // [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)); // [UDP Assets] //LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 59184af9b5..25a43f094b 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -303,10 +303,7 @@ void LLViewerAssetStorage::storeAssetData( legacy->mUpCallback = callback; legacy->mUserData = user_data; - // Fix broken asset upload - //LLFileSystem file(asset_id, asset_type, LLFileSystem::WRITE); LLFileSystem file(asset_id, asset_type, LLFileSystem::APPEND); - // const S32 buf_size = 65536; U8 copy_buf[buf_size]; diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index 24c89b1be7..8dec9193c0 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -498,10 +498,7 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() infile.open(filename, LL_APR_RB, NULL, &file_size); if (infile.getFileHandle()) { - // Fix broken asset upload - //LLFileSystem file(getAssetId(), assetType, LLFileSystem::WRITE); LLFileSystem file(getAssetId(), assetType, LLFileSystem::APPEND); - // const S32 buf_size = 65536; U8 copy_buf[buf_size];