From 5cca78e718f15522cc3db9aec76aa910dd696aa8 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 23 Jun 2014 14:23:33 -0400 Subject: First HTTP pipelining viewer. Enable pipelining for GetTexture and GetMesh2 at a pipeline depth of 5. Create global debug option, HttpPipelining, to enable and disable HTTP pipelining (defaults to true). Tweak texture and mesh low- and high-water request levels based on pipelining status and depth. Fixup texture console which was damaged in a recent release. Split logging of the no-request HTTP error case into two cases: one for missing URL in HTTP request, one for HTTP request not created. A refactor in llcorehttp is coming: I will be moving all libcurl- using code into libcurl-specific modules. --- indra/newview/lltexturefetch.cpp | 126 +++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 51 deletions(-) (limited to 'indra/newview/lltexturefetch.cpp') diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 548ed98f39..c33884e1e6 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -241,8 +241,10 @@ LLTrace::EventStatHandle LLTextureFetch::sCacheReadLatency("te // Tuning/Parameterization Constants -static const S32 HTTP_REQUESTS_IN_QUEUE_HIGH_WATER = 40; // Maximum requests to have active in HTTP -static const S32 HTTP_REQUESTS_IN_QUEUE_LOW_WATER = 20; // Active level at which to refill +static const S32 HTTP_PIPE_REQUESTS_HIGH_WATER = 100; // Maximum requests to have active in HTTP (pipelined) +static const S32 HTTP_PIPE_REQUESTS_LOW_WATER = 50; // Active level at which to refill +static const S32 HTTP_NONPIPE_REQUESTS_HIGH_WATER = 40; +static const S32 HTTP_NONPIPE_REQUESTS_LOW_WATER = 20; // BUG-3323/SH-4375 // *NOTE: This is a heuristic value. Texture fetches have a habit of using a @@ -608,16 +610,16 @@ private: LLCore::HttpHandle mHttpHandle; // Handle of any active request LLCore::BufferArray * mHttpBufferArray; // Refcounted pointer to response data - S32 mHttpPolicyClass; + S32 mHttpPolicyClass; bool mHttpActive; // Active request to http library - U32 mHttpReplySize, // Actual received data size - mHttpReplyOffset; // Actual received data offset + U32 mHttpReplySize, // Actual received data size + mHttpReplyOffset; // Actual received data offset bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore // State history - U32 mCacheReadCount, - mCacheWriteCount, - mResourceWaitCount; // Requests entering WAIT_HTTP_RESOURCE2 + U32 mCacheReadCount, + mCacheWriteCount, + mResourceWaitCount; // Requests entering WAIT_HTTP_RESOURCE2 }; ////////////////////////////////////////////////////////////////////////////// @@ -1525,36 +1527,49 @@ bool LLTextureFetchWorker::doWork(S32 param) mRequestedOffset -= 1; mRequestedSize += 1; } - mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; - if (!mUrl.empty()) - { - mRequestedTimer.reset(); - mLoaded = FALSE; - mGetStatus = LLCore::HttpStatus(); - mGetReason.clear(); - LL_DEBUGS(LOG_TXT) << "HTTP GET: " << mID << " Offset: " << mRequestedOffset - << " Bytes: " << mRequestedSize - << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth - << LL_ENDL; - // Will call callbackHttpGet when curl request completes - // Only server bake images use the returned headers currently, for getting retry-after field. - LLCore::HttpOptions *options = (mFTType == FTT_SERVER_BAKE) ? mFetcher->mHttpOptionsWithHeaders: mFetcher->mHttpOptions; - mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, - mWorkPriority, - mUrl, - mRequestedOffset, - (mRequestedOffset + mRequestedSize) > HTTP_REQUESTS_RANGE_END_MAX - ? 0 - : mRequestedSize, - options, - mFetcher->mHttpHeaders, - this); + if (mUrl.empty()) + { + // *FIXME: This should not be reachable except it has become + // so after some recent 'work'. Need to track this down + // and illuminate the unenlightened. + LL_WARNS(LOG_TXT) << "HTTP GET request failed for " << mID + << " on empty URL." << LL_ENDL; + resetFormattedData(); + releaseHttpSemaphore(); + return true; // failed } + + mRequestedTimer.reset(); + mLoaded = FALSE; + mGetStatus = LLCore::HttpStatus(); + mGetReason.clear(); + LL_DEBUGS(LOG_TXT) << "HTTP GET: " << mID << " Offset: " << mRequestedOffset + << " Bytes: " << mRequestedSize + << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth + << LL_ENDL; + + // Will call callbackHttpGet when curl request completes + // Only server bake images use the returned headers currently, for getting retry-after field. + LLCore::HttpOptions *options = (mFTType == FTT_SERVER_BAKE) ? mFetcher->mHttpOptionsWithHeaders: mFetcher->mHttpOptions; + mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, + mWorkPriority, + mUrl, + mRequestedOffset, + (mRequestedOffset + mRequestedSize) > HTTP_REQUESTS_RANGE_END_MAX + ? 0 + : mRequestedSize, + options, + mFetcher->mHttpHeaders, + this); if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) { - LL_WARNS(LOG_TXT) << "HTTP GET request failed for " << mID << LL_ENDL; + LLCore::HttpStatus status(mFetcher->mHttpRequest->getStatus()); + LL_WARNS(LOG_TXT) << "HTTP GET request failed for " << mID + << ", Status: " << status.toTerseString() + << " Reason: '" << status.toString() << "'" + << LL_ENDL; resetFormattedData(); releaseHttpSemaphore(); return true; // failed @@ -1610,10 +1625,6 @@ bool LLTextureFetchWorker::doWork(S32 param) else if (http_service_unavail == mGetStatus) { LL_INFOS_ONCE(LOG_TXT) << "Texture server busy (503): " << mUrl << LL_ENDL; - LL_INFOS(LOG_TXT) << "503: HTTP GET failed for: " << mUrl - << " Status: " << mGetStatus.toHex() - << " Reason: '" << mGetReason << "'" - << LL_ENDL; } else if (http_not_sat == mGetStatus) { @@ -2482,7 +2493,6 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpHeaders(NULL), mHttpMetricsHeaders(NULL), mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), - mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER), mTotalCacheReadCount(0U), mTotalCacheWriteCount(0U), mTotalResourceWaitCount(0U), @@ -2494,6 +2504,30 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), U32Bytes(gSavedSettings.getU32("TextureLoggingThreshold"))); + mHttpRequest = new LLCore::HttpRequest; + mHttpOptions = new LLCore::HttpOptions; + mHttpOptionsWithHeaders = new LLCore::HttpOptions; + mHttpOptionsWithHeaders->setWantHeaders(true); + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->append("Accept", "image/x-j2c"); + mHttpMetricsHeaders = new LLCore::HttpHeaders; + mHttpMetricsHeaders->append("Content-Type", "application/llsd+xml"); + LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); + mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_TEXTURE); + if (app_core_http.isPipelined(LLAppCoreHttp::AP_TEXTURE)) + { + mHttpHighWater = HTTP_PIPE_REQUESTS_HIGH_WATER; + mHttpLowWater = HTTP_PIPE_REQUESTS_LOW_WATER; + } + else + { + mHttpHighWater = HTTP_NONPIPE_REQUESTS_HIGH_WATER; + mHttpLowWater = HTTP_NONPIPE_REQUESTS_LOW_WATER; + } + mHttpSemaphore = mHttpHighWater; + + // Conditionally construct debugger object after 'this' is + // fully initialized. LLTextureFetchDebugger::sDebuggerEnabled = gSavedSettings.getBOOL("TextureFetchDebuggerEnabled"); if(LLTextureFetchDebugger::isEnabled()) { @@ -2506,16 +2540,6 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image } mOriginFetchSource = mFetchSource; } - - mHttpRequest = new LLCore::HttpRequest; - mHttpOptions = new LLCore::HttpOptions; - mHttpOptionsWithHeaders = new LLCore::HttpOptions; - mHttpOptionsWithHeaders->setWantHeaders(true); - mHttpHeaders = new LLCore::HttpHeaders; - mHttpHeaders->append("Accept", "image/x-j2c"); - mHttpMetricsHeaders = new LLCore::HttpHeaders; - mHttpMetricsHeaders->append("Content-Type", "application/llsd+xml"); - mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_TEXTURE); } LLTextureFetch::~LLTextureFetch() @@ -3645,7 +3669,7 @@ void LLTextureFetch::releaseHttpWaiters() { // Use mHttpSemaphore rather than mHTTPTextureQueue.size() // to avoid a lock. - if (mHttpSemaphore < (HTTP_REQUESTS_IN_QUEUE_HIGH_WATER - HTTP_REQUESTS_IN_QUEUE_LOW_WATER)) + if (mHttpSemaphore < (mHttpHighWater - mHttpLowWater)) return; // Quickly make a copy of all the LLUIDs. Get off the @@ -4538,7 +4562,7 @@ S32 LLTextureFetchDebugger::fillCurlQueue() mNbCurlCompleted = mFetchingHistory.size(); return 0; } - if (mNbCurlRequests > HTTP_REQUESTS_IN_QUEUE_LOW_WATER) + if (mNbCurlRequests > HTTP_NONPIPE_REQUESTS_LOW_WATER) { return mNbCurlRequests; } @@ -4571,7 +4595,7 @@ S32 LLTextureFetchDebugger::fillCurlQueue() mFetchingHistory[i].mHttpHandle = handle; mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS; mNbCurlRequests++; - if (mNbCurlRequests >= HTTP_REQUESTS_IN_QUEUE_HIGH_WATER) // emulate normal pipeline + if (mNbCurlRequests >= HTTP_NONPIPE_REQUESTS_HIGH_WATER) // emulate normal pipeline { break; } -- cgit v1.3 From 17da4cf57aadcf1987b48af298d8b2742089a35c Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 27 Jun 2014 17:25:39 -0400 Subject: Cleanup and tuning. Use a consistent index on some initialization data so their isn't an opportunity for gaps over overruns (init_data). Start some preliminary tweaking of policy class numbers. It looks like I can easily drop the default connection count to '4' and still hit the throttles. Did some experiments running pipeline deeper which was mostly fine for textures but tended to slow meshes. Reason uncertain but a depth of '5' seems generally healthy for mesh. I had one run of 52.6S with a theoretical minimum of 51.2S. That's as good as I've ever seen. --- indra/newview/llappcorehttp.cpp | 41 +++++++++++++++++++++----------------- indra/newview/llmeshrepository.cpp | 7 ++++++- indra/newview/lltexturefetch.cpp | 2 ++ 3 files changed, 31 insertions(+), 19 deletions(-) (limited to 'indra/newview/lltexturefetch.cpp') diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index deda0ccb41..d097f18d61 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -42,9 +42,9 @@ const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0); const long LLAppCoreHttp::PIPELINING_DEPTH(5L); +// Default and dynamic values for classes static const struct { - LLAppCoreHttp::EAppPolicy mPolicy; U32 mDefault; U32 mMin; U32 mMax; @@ -52,40 +52,40 @@ static const struct bool mPipelined; std::string mKey; const char * mUsage; -} init_data[] = // Default and dynamic values for classes +} init_data[LLAppCoreHttp::AP_COUNT] = { - { - LLAppCoreHttp::AP_DEFAULT, 8, 8, 8, 0, false, + { // AP_DEFAULT + 8, 8, 8, 0, false, "", "other" }, - { - LLAppCoreHttp::AP_TEXTURE, 8, 1, 12, 0, true, + { // AP_TEXTURE + 4, 1, 12, 0, true, "TextureFetchConcurrency", "texture fetch" }, - { - LLAppCoreHttp::AP_MESH1, 32, 1, 128, 100, false, + { // AP_MESH1 + 32, 1, 128, 100, false, "MeshMaxConcurrentRequests", "mesh fetch" }, - { - LLAppCoreHttp::AP_MESH2, 8, 1, 32, 100, true, + { // AP_MESH2 + 4, 1, 32, 100, true, "Mesh2MaxConcurrentRequests", "mesh2 fetch" }, - { - LLAppCoreHttp::AP_LARGE_MESH, 2, 1, 8, 0, false, + { // AP_LARGE_MESH + 2, 1, 8, 0, false, "", "large mesh fetch" }, - { - LLAppCoreHttp::AP_UPLOADS, 2, 1, 8, 0, false, + { // AP_UPLOADS + 2, 1, 8, 0, false, "", "asset upload" }, - { - LLAppCoreHttp::AP_LONG_POLL, 32, 32, 32, 0, false, + { // AP_LONG_POLL + 32, 32, 32, 0, false, "", "long poll" } @@ -173,9 +173,10 @@ void LLAppCoreHttp::init() mHttpClasses[AP_DEFAULT].mPolicy = LLCore::HttpRequest::DEFAULT_POLICY_ID; // Setup additional policies based on table and some special rules + llassert(LL_ARRAY_SIZE(init_data) == AP_COUNT); for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) { - const EAppPolicy app_policy(init_data[i].mPolicy); + const EAppPolicy app_policy(static_cast(i)); if (AP_DEFAULT == app_policy) { @@ -301,7 +302,7 @@ void LLAppCoreHttp::refreshSettings(bool initial) for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) { - const EAppPolicy app_policy(init_data[i].mPolicy); + const EAppPolicy app_policy(static_cast(i)); if (initial) { @@ -326,6 +327,10 @@ void LLAppCoreHttp::refreshSettings(bool initial) if (mPipelined && init_data[i].mPipelined) { // Pipelining election is currently static (init-time). + // Making it dynamic isn't too hard in the SL code but verifying + // that libcurl handles the on-to-off transition while holding + // outstanding requests is something that should be tested. + status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_PIPELINING_DEPTH, mHttpClasses[app_policy].mPolicy, PIPELINING_DEPTH, diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index c91ae975ea..74eb6015f2 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -338,14 +338,17 @@ static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch"); LLMeshRepository gMeshRepo; const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space + const S32 REQUEST_HIGH_WATER_MIN = 32; // Limits for GetMesh regions const S32 REQUEST_HIGH_WATER_MAX = 150; // Should remain under 2X throttle const S32 REQUEST_LOW_WATER_MIN = 16; const S32 REQUEST_LOW_WATER_MAX = 75; + const S32 REQUEST2_HIGH_WATER_MIN = 32; // Limits for GetMesh2 regions const S32 REQUEST2_HIGH_WATER_MAX = 100; const S32 REQUEST2_LOW_WATER_MIN = 16; const S32 REQUEST2_LOW_WATER_MAX = 50; + const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes to narrow/slow queue const long SMALL_MESH_XFER_TIMEOUT = 120L; // Seconds to complete xfer, small mesh downloads const long LARGE_MESH_XFER_TIMEOUT = 600L; // Seconds to complete xfer, large downloads @@ -3203,7 +3206,9 @@ void LLMeshRepository::notifyLoadedMeshes() // we'll increase this. See llappcorehttp and llcorehttp for // discussion on connection strategies. LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); - S32 scale(app_core_http.isPipelined(LLAppCoreHttp::AP_MESH2) ? 10 : 5); + S32 scale(app_core_http.isPipelined(LLAppCoreHttp::AP_MESH2) + ? (2 * LLAppCoreHttp::PIPELINING_DEPTH) + : 5); LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("Mesh2MaxConcurrentRequests"); LLMeshRepoThread::sRequestHighWater = llclamp(scale * S32(LLMeshRepoThread::sMaxConcurrentRequests), diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index ee11e7b47b..ecaf5f8d06 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2516,6 +2516,8 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_TEXTURE); if (app_core_http.isPipelined(LLAppCoreHttp::AP_TEXTURE)) { + // Init-time election that will have to change for + // support of dynamic changes to the pipelining enable flag. mHttpHighWater = HTTP_PIPE_REQUESTS_HIGH_WATER; mHttpLowWater = HTTP_PIPE_REQUESTS_LOW_WATER; } -- cgit v1.3 From 5dffe16aef7c6687a2ed47c5324f4365ff9d84df Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 11 Aug 2014 14:38:47 -0400 Subject: Add 'HttpRangeRequestsDisable' debug setting to inhibit use of 'Range:' header. Intended for users with bad networking gear or twitchy ISPs, if set to True, forces plain GET requests to asset servers for textures and meshes. This change kicked off a slight refactor in the mesh repository code which made it resilient against unexpected 200's and responses not covering the requested start range. There's still too much data copying in the Mesh code (always has been). Would love to fix that and get rid of the monolithic temp buffer. Cleaned up white space damage caused by unnamed linden who likes to drag his magical editor through code. --- indra/newview/app_settings/settings.xml | 11 + indra/newview/llmeshrepository.cpp | 364 ++++++++++++++++++-------------- indra/newview/lltexturefetch.cpp | 43 ++-- 3 files changed, 243 insertions(+), 175 deletions(-) (limited to 'indra/newview/lltexturefetch.cpp') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 410473c54f..50f56cf6ff 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4467,6 +4467,17 @@ Value 1 + HttpRangeRequestsDisable + + Comment + If true, viewer will not issued range GET requests for meshes and textures. + Persist + 1 + Type + Boolean + Value + 0 + IMShowTimestamps Comment diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index fc69ecfae9..6477389d4c 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1,4 +1,3 @@ - /** * @file llmeshrepository.cpp * @brief Mesh repository implementation. @@ -521,11 +520,13 @@ class LLMeshHandlerBase : public LLCore::HttpHandler { public: LOG_CLASS(LLMeshHandlerBase); - LLMeshHandlerBase() + LLMeshHandlerBase(U32 offset, U32 requested_bytes) : LLCore::HttpHandler(), mMeshParams(), mProcessed(false), - mHttpHandle(LLCORE_HTTP_HANDLE_INVALID) + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), + mOffset(offset), + mRequestedBytes(requested_bytes) {} virtual ~LLMeshHandlerBase() @@ -537,13 +538,15 @@ protected: public: virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); - virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size) = 0; + virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size) = 0; virtual void processFailure(LLCore::HttpStatus status) = 0; public: LLVolumeParams mMeshParams; bool mProcessed; - LLCore::HttpHandle mHttpHandle; + LLCore::HttpHandle mHttpHandle; + U32 mOffset; + U32 mRequestedBytes; }; @@ -554,8 +557,8 @@ class LLMeshHeaderHandler : public LLMeshHandlerBase { public: LOG_CLASS(LLMeshHeaderHandler); - LLMeshHeaderHandler(const LLVolumeParams & mesh_params) - : LLMeshHandlerBase() + LLMeshHeaderHandler(const LLVolumeParams & mesh_params, U32 offset, U32 requested_bytes) + : LLMeshHandlerBase(offset, requested_bytes) { mMeshParams = mesh_params; LLMeshRepoThread::incActiveHeaderRequests(); @@ -567,7 +570,7 @@ protected: void operator=(const LLMeshHeaderHandler &); // Not defined public: - virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); + virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); virtual void processFailure(LLCore::HttpStatus status); }; @@ -576,17 +579,16 @@ public: // // Thread: repo class LLMeshLODHandler : public LLMeshHandlerBase - { +{ public: + LOG_CLASS(LLMeshLODHandler); LLMeshLODHandler(const LLVolumeParams & mesh_params, S32 lod, U32 offset, U32 requested_bytes) - : LLMeshHandlerBase(), - mLOD(lod), - mRequestedBytes(requested_bytes), - mOffset(offset) + : LLMeshHandlerBase(offset, requested_bytes), + mLOD(lod) { - mMeshParams = mesh_params; - LLMeshRepoThread::incActiveLODRequests(); - } + mMeshParams = mesh_params; + LLMeshRepoThread::incActiveLODRequests(); + } virtual ~LLMeshLODHandler(); protected: @@ -594,13 +596,11 @@ protected: void operator=(const LLMeshLODHandler &); // Not defined public: - virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); + virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); virtual void processFailure(LLCore::HttpStatus status); public: S32 mLOD; - U32 mRequestedBytes; - U32 mOffset; }; @@ -608,14 +608,12 @@ public: // // Thread: repo class LLMeshSkinInfoHandler : public LLMeshHandlerBase - { +{ public: LOG_CLASS(LLMeshSkinInfoHandler); - LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 size) - : LLMeshHandlerBase(), - mMeshID(id), - mRequestedBytes(size), - mOffset(offset) + LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 requested_bytes) + : LLMeshHandlerBase(offset, requested_bytes), + mMeshID(id) {} virtual ~LLMeshSkinInfoHandler(); @@ -624,13 +622,11 @@ protected: void operator=(const LLMeshSkinInfoHandler &); // Not defined public: - virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); + virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); virtual void processFailure(LLCore::HttpStatus status); public: LLUUID mMeshID; - U32 mRequestedBytes; - U32 mOffset; }; @@ -638,14 +634,12 @@ public: // // Thread: repo class LLMeshDecompositionHandler : public LLMeshHandlerBase - { +{ public: LOG_CLASS(LLMeshDecompositionHandler); - LLMeshDecompositionHandler(const LLUUID& id, U32 offset, U32 size) - : LLMeshHandlerBase(), - mMeshID(id), - mRequestedBytes(size), - mOffset(offset) + LLMeshDecompositionHandler(const LLUUID& id, U32 offset, U32 requested_bytes) + : LLMeshHandlerBase(offset, requested_bytes), + mMeshID(id) {} virtual ~LLMeshDecompositionHandler(); @@ -654,13 +648,11 @@ protected: void operator=(const LLMeshDecompositionHandler &); // Not defined public: - virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); + virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); virtual void processFailure(LLCore::HttpStatus status); public: LLUUID mMeshID; - U32 mRequestedBytes; - U32 mOffset; }; @@ -668,14 +660,12 @@ public: // // Thread: repo class LLMeshPhysicsShapeHandler : public LLMeshHandlerBase - { +{ public: LOG_CLASS(LLMeshPhysicsShapeHandler); - LLMeshPhysicsShapeHandler(const LLUUID& id, U32 offset, U32 size) - : LLMeshHandlerBase(), - mMeshID(id), - mRequestedBytes(size), - mOffset(offset) + LLMeshPhysicsShapeHandler(const LLUUID& id, U32 offset, U32 requested_bytes) + : LLMeshHandlerBase(offset, requested_bytes), + mMeshID(id) {} virtual ~LLMeshPhysicsShapeHandler(); @@ -684,13 +674,11 @@ protected: void operator=(const LLMeshPhysicsShapeHandler &); // Not defined public: - virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); + virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); virtual void processFailure(LLCore::HttpStatus status); public: LLUUID mMeshID; - U32 mRequestedBytes; - U32 mOffset; }; @@ -716,8 +704,8 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content, LL_WARNS(LOG_MESH) << "error: " << err << LL_ENDL; LL_WARNS(LOG_MESH) << " mesh upload failed, stage '" << stage << "', error '" << err["error"].asString() - << "', message '" << err["message"].asString() - << "', id '" << err["identifier"].asString() + << "', message '" << err["message"].asString() + << "', id '" << err["identifier"].asString() << "'" << LL_ENDL; if (err.has("errors")) { @@ -779,7 +767,7 @@ LLMeshRepoThread::LLMeshRepoThread() LLMeshRepoThread::~LLMeshRepoThread() - { +{ LL_INFOS(LOG_MESH) << "Small GETs issued: " << LLMeshRepository::sHTTPRequestCount << ", Large GETs issued: " << LLMeshRepository::sHTTPLargeRequestCount << ", Max Lock Holdoffs: " << LLMeshRepository::sMaxLockHoldoffs @@ -790,23 +778,23 @@ LLMeshRepoThread::~LLMeshRepoThread() ++iter) { delete *iter; - } + } mHttpRequestSet.clear(); if (mHttpHeaders) - { + { mHttpHeaders->release(); mHttpHeaders = NULL; - } + } if (mHttpOptions) - { + { mHttpOptions->release(); mHttpOptions = NULL; - } + } if (mHttpLargeOptions) -{ + { mHttpLargeOptions->release(); mHttpLargeOptions = NULL; -} + } delete mHttpRequest; mHttpRequest = NULL; delete mMutex; @@ -1137,6 +1125,9 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c size_t offset, size_t len, LLCore::HttpHandler * handler) { + // Also used in lltexturefetch.cpp + static LLCachedControl disable_range_req(gSavedSettings, "HttpRangeRequestsDisable", false); + LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); if (len < LARGE_MESH_FETCH_THRESHOLD) @@ -1146,8 +1137,8 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c : mHttpLegacyPolicyClass), mHttpPriority, url, - offset, - len, + (disable_range_req ? size_t(0) : offset), + (disable_range_req ? size_t(0) : len), mHttpOptions, mHttpHeaders, handler); @@ -1161,8 +1152,8 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c handle = mHttpRequest->requestGetByteRange(mHttpLargePolicyClass, mHttpPriority, url, - offset, - len, + (disable_range_req ? size_t(0) : offset), + (disable_range_req ? size_t(0) : len), mHttpLargeOptions, mHttpHeaders, handler); @@ -1532,7 +1523,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) //within the first 4KB //NOTE -- this will break of headers ever exceed 4KB - LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params); + LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params, 0, MESH_HEADER_SIZE); LLCore::HttpHandle handle = getByteRange(http_url, cap_version, 0, MESH_HEADER_SIZE, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { @@ -2276,7 +2267,7 @@ void LLMeshUploadThread::doWholeModelUpload() mHttpRequest->update(0); while (! LLApp::isQuitting() && ! finished() && ! isDiscarded()) - { + { ms_sleep(sleep_time); sleep_time = llmin(250U, sleep_time + sleep_time); mHttpRequest->update(0); @@ -2292,7 +2283,7 @@ void LLMeshUploadThread::doWholeModelUpload() } } } - } +} void LLMeshUploadThread::requestWholeModelFee() { @@ -2323,7 +2314,7 @@ void LLMeshUploadThread::requestWholeModelFee() LL_WARNS(LOG_MESH) << "Couldn't issue request for model fee. Reason: " << mHttpStatus.toString() << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; - } + } else { U32 sleep_time(10); @@ -2340,7 +2331,7 @@ void LLMeshUploadThread::requestWholeModelFee() LL_DEBUGS(LOG_MESH) << "Mesh fee query operation discarded." << LL_ENDL; } } - } +} // Does completion duty for both fee queries and actual uploads. @@ -2393,12 +2384,12 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp { LLCore::BufferArrayStream bas(ba); LLSDSerialize::fromXML(body, bas); -} + } } dump_llsd_to_file(body, make_dump_name("whole_model_upload_response_", dump_num)); if (body["state"].asString() == "complete") -{ + { // requested "mesh" asset type isn't actually the type // of the resultant object, fix it up here. mModelData["asset_type"] = "object"; @@ -2451,18 +2442,18 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp body = llsd_from_file("fake_upload_error.xml"); } else - { + { LLCore::BufferArray * ba(response->getBody()); if (ba && ba->size()) - { + { LLCore::BufferArrayStream bas(ba); LLSDSerialize::fromXML(body, bas); - } - } + } + } dump_llsd_to_file(body, make_dump_name("whole_model_fee_response_", dump_num)); if (body["state"].asString() == "upload") - { + { mWholeModelUploadURL = body["uploader"].asString(); if (observer) @@ -2548,18 +2539,18 @@ void LLMeshRepoThread::notifyLoadedMeshes() skin_info_q.swap(mSkinInfoQ); } if (! mDecompositionQ.empty()) - { + { decomp_q.swap(mDecompositionQ); - } + } mMutex->unlock(); // Process the elements free of the lock while (! skin_info_q.empty()) - { + { gMeshRepo.notifySkinInfoReceived(skin_info_q.front()); skin_info_q.pop_front(); - } + } while (! decomp_q.empty()) { @@ -2681,35 +2672,78 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo // rather than partial) and 416 (request completely unsatisfyable). // Always been exposed to these but are less likely here where // speculative loads aren't done. - static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + LLCore::BufferArray * body(response->getBody()); + S32 body_offset(0); + U8 * data(NULL); + S32 data_size(body ? body->size() : 0); - if (par_status != status) + if (data_size > 0) { - LL_WARNS_ONCE(LOG_MESH) << "Non-206 successful status received for fetch: " - << status.toTerseString() << LL_ENDL; - } + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + + unsigned int offset(0), length(0), full_length(0); + + if (par_status == status) + { + // 216 case + response->getRange(&offset, &length, &full_length); + if (! offset && ! length) + { + // This is the case where we receive a 206 status but + // there wasn't a useful Content-Range header in the response. + // This could be because it was badly formatted but is more + // likely due to capabilities services which scrub headers + // from responses. Assume we got what we asked for...` + // length = data_size; + offset = mOffset; + } + } + else + { + // 200 case, typically + offset = 0; + } - LLCore::BufferArray * body(response->getBody()); - S32 data_size(body ? body->size() : 0); - U8 * data(NULL); + // *DEBUG: To test validation below + // offset += 1; - if (data_size > 0) - { + // Validate that what we think we received is consistent with + // what we've asked for. I.e. first byte we wanted lies somewhere + // in the response. + if (offset > mOffset + || (offset + data_size) <= mOffset + || (mOffset - offset) >= data_size) + { + // No overlap with requested range. Fail request with + // suitable error. Shouldn't happen unless server/cache/ISP + // is doing something awful. + LL_WARNS(LOG_MESH) << "Mesh response (bytes [" + << offset << ".." << (offset + length - 1) + << "]) didn't overlap with request's origin (bytes [" + << mOffset << ".." << (mOffset + mRequestedBytes - 1) + << "])." << LL_ENDL; + processFailure(LLCore::HttpStatus(LLCore::HttpStatus::LLCORE, LLCore::HE_INV_CONTENT_RANGE_HDR)); + ++LLMeshRepository::sHTTPErrorCount; + goto common_exit; + } + // *TODO: Try to get rid of data copying and add interfaces // that support BufferArray directly. Introduce a two-phase // handler, optional first that takes a body, fallback second // that requires a temporary allocation and data copy. - data = new U8[data_size]; - body->read(0, (char *) data, data_size); + body_offset = mOffset - offset; + data = new U8[data_size - body_offset]; + body->read(body_offset, (char *) data, data_size - body_offset); LLMeshRepository::sBytesReceived += data_size; } - processData(body, data, data_size); + processData(body, body_offset, data, data_size - body_offset); delete [] data; } // Release handler +common_exit: gMeshRepo.mThread->mHttpRequestSet.erase(this); delete this; // Must be last statement } @@ -2744,9 +2778,10 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) { gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i)); } - } +} -void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, + U8 * data, S32 data_size) { LLUUID mesh_id = mMeshParams.getSculptID(); bool success = (! MESH_HEADER_PROCESS_FAILED) && gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); @@ -2761,12 +2796,12 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 // Can't get the header so none of the LODs will be available LLMutexLock lock(gMeshRepo.mThread->mMutex); for (int i(0); i < 4; ++i) - { + { gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i)); - } } + } else if (data && data_size > 0) - { + { // header was successfully retrieved from sim, cache in vfs LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id]; @@ -2779,11 +2814,11 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 S32 lod_bytes = 0; for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i) - { + { // figure out how many bytes we'll need to reserve in the file const std::string & lod_name = header_lod[i]; lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger()); - } + } // just in case skin info or decomposition is at the end of the file (which it shouldn't be) lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger()); @@ -2799,7 +2834,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) - { + { LLMeshRepository::sCacheBytesWritten += data_size; ++LLMeshRepository::sCacheWrites; @@ -2810,19 +2845,19 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 memset(block, 0, sizeof(block)); while (bytes-file.tell() > sizeof(block)) - { + { file.write(block, sizeof(block)); - } + } S32 remaining = bytes-file.tell(); if (remaining > 0) - { + { file.write(block, remaining); } } } } - } +} LLMeshLODHandler::~LLMeshLODHandler() { @@ -2848,8 +2883,9 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status) gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); } -void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) - { +void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, + U8 * data, S32 data_size) +{ if ((! MESH_LOD_PROCESS_FAILED) && gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size)) { //good fetch from sim, write to VFS for caching @@ -2865,7 +2901,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 da LLMeshRepository::sCacheBytesWritten += size; ++LLMeshRepository::sCacheWrites; } - } + } else { LL_WARNS(LOG_MESH) << "Error during mesh LOD processing. ID: " << mMeshParams.getSculptID() @@ -2877,12 +2913,12 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 da } LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler() - { - llassert(mProcessed); - } +{ + llassert(mProcessed); +} void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) - { +{ LL_WARNS(LOG_MESH) << "Error during mesh skin info handling. ID: " << mMeshID << ", Reason: " << status.toString() << " (" << status.toTerseString() << "). Not retrying." @@ -2890,10 +2926,11 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) // *TODO: Mark mesh unavailable on error. For now, simply leave // request unfulfilled rather than retry forever. - } +} -void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) - { +void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, + U8 * data, S32 data_size) +{ if ((! MESH_SKIN_INFO_PROCESS_FAILED) && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) { //good fetch from sim, write to VFS for caching @@ -2921,20 +2958,21 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S LLMeshDecompositionHandler::~LLMeshDecompositionHandler() { - llassert(mProcessed); + llassert(mProcessed); } void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status) - { +{ LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling. ID: " << mMeshID << ", Reason: " << status.toString() << " (" << status.toTerseString() << "). Not retrying." << LL_ENDL; // *TODO: Mark mesh unavailable on error. For now, simply leave // request unfulfilled rather than retry forever. - } +} -void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, + U8 * data, S32 data_size) { if ((! MESH_DECOMP_PROCESS_FAILED) && gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) { @@ -2951,34 +2989,35 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * da file.seek(offset); file.write(data, size); } - } - else - { + } + else + { LL_WARNS(LOG_MESH) << "Error during mesh decomposition processing. ID: " << mMeshID << ", Unknown reason. Not retrying." << LL_ENDL; // *TODO: Mark mesh unavailable on error - } } +} LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler() - { - llassert(mProcessed); - } +{ + llassert(mProcessed); +} void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status) - { +{ LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling. ID: " << mMeshID << ", Reason: " << status.toString() << " (" << status.toTerseString() << "). Not retrying." << LL_ENDL; // *TODO: Mark mesh unavailable on error - } +} -void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) - { +void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, + U8 * data, S32 data_size) +{ if ((! MESH_PHYS_SHAPE_PROCESS_FAILED) && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size)) - { + { // good fetch from sim, write to VFS for caching LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); @@ -2986,13 +3025,13 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * dat S32 size = mRequestedBytes; if (file.getSize() >= offset+size) - { + { LLMeshRepository::sCacheBytesWritten += size; ++LLMeshRepository::sCacheWrites; file.seek(offset); file.write(data, size); - } } + } else { LL_WARNS(LOG_MESH) << "Error during mesh physics shape processing. ID: " << mMeshID @@ -3192,7 +3231,7 @@ void LLMeshRepository::notifyLoadedMeshes() if (1 == mGetMeshVersion) { // Legacy GetMesh operation with high connection concurrency - LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests"); + LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests"); LLMeshRepoThread::sRequestHighWater = llclamp(2 * S32(LLMeshRepoThread::sMaxConcurrentRequests), REQUEST_HIGH_WATER_MIN, REQUEST_HIGH_WATER_MAX); @@ -3311,18 +3350,18 @@ void LLMeshRepository::notifyLoadedMeshes() // If we can't get the locks, skip and pick this up later. ++hold_offs; sMaxLockHoldoffs = llmax(sMaxLockHoldoffs, hold_offs); - return; - } + return; + } hold_offs = 0; if (gAgent.getRegion()) { // Update capability urls - static std::string region_name("never name a region this"); + static std::string region_name("never name a region this"); - if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) - { - region_name = gAgent.getRegion()->getName(); + if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) + { + region_name = gAgent.getRegion()->getName(); const bool use_v1(gSavedSettings.getBOOL("MeshUseGetMesh1")); const std::string mesh1(gAgent.getRegion()->getCapability("GetMesh")); const std::string mesh2(gAgent.getRegion()->getCapability("GetMesh2")); @@ -3333,8 +3372,8 @@ void LLMeshRepository::notifyLoadedMeshes() << ", GetMesh: " << mesh1 << ", using version: " << mGetMeshVersion << LL_ENDL; + } } - } //popup queued error messages from background threads while (!mUploadErrorQ.empty()) @@ -3349,46 +3388,46 @@ void LLMeshRepository::notifyLoadedMeshes() S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count; if (mPendingRequests.size() > push_count) - { + { // More requests than the high-water limit allows so // sort and forward the most important. - //calculate "score" for pending requests + //calculate "score" for pending requests - //create score map - std::map score_map; + //create score map + std::map score_map; - for (U32 i = 0; i < 4; ++i) - { - for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter) + for (U32 i = 0; i < 4; ++i) { - F32 max_score = 0.f; - for (std::set::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) + for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter) { - LLViewerObject* object = gObjectList.findObject(*obj_iter); - - if (object) + F32 max_score = 0.f; + for (std::set::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) { - LLDrawable* drawable = object->mDrawable; - if (drawable) + LLViewerObject* object = gObjectList.findObject(*obj_iter); + + if (object) { - F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f); - max_score = llmax(max_score, cur_score); + LLDrawable* drawable = object->mDrawable; + if (drawable) + { + F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f); + max_score = llmax(max_score, cur_score); + } } } - } - score_map[iter->first.getSculptID()] = max_score; + score_map[iter->first.getSculptID()] = max_score; + } } - } - //set "score" for pending requests - for (std::vector::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) - { - iter->mScore = score_map[iter->mMeshParams.getSculptID()]; - } + //set "score" for pending requests + for (std::vector::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) + { + iter->mScore = score_map[iter->mMeshParams.getSculptID()]; + } - //sort by "score" + //sort by "score" std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count, mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater()); } @@ -3599,7 +3638,6 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id) } } } - } LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index ab7df02100..4008a6948d 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1327,7 +1327,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } } - static LLCachedControl use_http(gSavedSettings,"ImagePipelineUseHTTP", true); + static LLCachedControl use_http(gSavedSettings, "ImagePipelineUseHTTP", true); // if (mHost != LLHost::invalid) get_url = false; if ( use_http && mCanUseHTTP && mUrl.empty())//get http url. @@ -1472,6 +1472,9 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == SEND_HTTP_REQ) { + // Also used in llmeshrepository + static LLCachedControl disable_range_req(gSavedSettings, "HttpRangeRequestsDisable", false); + if (! mCanUseHTTP) { releaseHttpSemaphore(); @@ -1553,16 +1556,32 @@ bool LLTextureFetchWorker::doWork(S32 param) // Will call callbackHttpGet when curl request completes // Only server bake images use the returned headers currently, for getting retry-after field. LLCore::HttpOptions *options = (mFTType == FTT_SERVER_BAKE) ? mFetcher->mHttpOptionsWithHeaders: mFetcher->mHttpOptions; - mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, - mWorkPriority, - mUrl, - mRequestedOffset, - (mRequestedOffset + mRequestedSize) > HTTP_REQUESTS_RANGE_END_MAX - ? 0 - : mRequestedSize, - options, - mFetcher->mHttpHeaders, - this); + if (disable_range_req) + { + // 'Range:' requests may be disabled in which case all HTTP + // texture fetches result in full fetches. This can be used + // by people with questionable ISPs or networking gear that + // doesn't handle these well. + mHttpHandle = mFetcher->mHttpRequest->requestGet(mHttpPolicyClass, + mWorkPriority, + mUrl, + options, + mFetcher->mHttpHeaders, + this); + } + else + { + mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, + mWorkPriority, + mUrl, + mRequestedOffset, + (mRequestedOffset + mRequestedSize) > HTTP_REQUESTS_RANGE_END_MAX + ? 0 + : mRequestedSize, + options, + mFetcher->mHttpHeaders, + this); + } if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) { LLCore::HttpStatus status(mFetcher->mHttpRequest->getStatus()); @@ -1782,7 +1801,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DECODE_IMAGE) { - static LLCachedControl textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled", false); + static LLCachedControl textures_decode_disabled(gSavedSettings, "TextureDecodeDisabled", false); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it if (textures_decode_disabled) -- cgit v1.3 From e79a88c8ccfadcd260892000d4dec2ae921b26de Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 12 Aug 2014 18:21:26 -0400 Subject: Better support for dynamic option changes in llcorehttp. Libcurl has some problems disabling pipelining on a multi handle with outstanding requests so build a more conservative system that allows requests to drain before setting curl multi options. Would rather not have this but it is significantly safer. "HttpPipelining" debug setting is now fully dynamic. Connection limits can also be made dynamic in the near future. Upped the default connection count back to 8 for now but will revisit this in the tuning phase. It might be time to combine mesh and textures into a single asset class. For normal server operations that would be a clear path, but for server under load, the current scheme may be better. Minor cleanup in logging to elminate some redundant strings. Might add some more tracing to the stall logic 'just in case'. --- indra/llcorehttp/_httplibcurl.cpp | 198 ++++++++++++++++++++++---------- indra/llcorehttp/_httplibcurl.h | 12 +- indra/llcorehttp/_httpoperation.cpp | 28 +++-- indra/llcorehttp/_httpoprequest.cpp | 46 ++++---- indra/llcorehttp/_httppolicy.cpp | 83 ++++++++----- indra/llcorehttp/_httppolicy.h | 8 ++ indra/llcorehttp/_httpservice.cpp | 30 +++-- indra/newview/app_settings/settings.xml | 4 +- indra/newview/llappcorehttp.cpp | 174 ++++++++++++++++------------ indra/newview/llappcorehttp.h | 1 + indra/newview/lltexturefetch.cpp | 55 +++++---- indra/newview/lltexturefetch.h | 10 +- 12 files changed, 421 insertions(+), 228 deletions(-) (limited to 'indra/newview/lltexturefetch.cpp') diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index fb907f6318..b46833a1f3 100755 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -40,6 +40,8 @@ namespace void check_curl_multi_code(CURLMcode code); void check_curl_multi_code(CURLMcode code, int curl_setopt_option); +static const char * const LOG_CORE("CoreHttp"); + } // end anonymous namespace @@ -51,7 +53,8 @@ HttpLibcurl::HttpLibcurl(HttpService * service) : mService(service), mPolicyCount(0), mMultiHandles(NULL), - mActiveHandles(NULL) + mActiveHandles(NULL), + mDirtyPolicy(NULL) {} @@ -90,6 +93,9 @@ void HttpLibcurl::shutdown() delete [] mActiveHandles; mActiveHandles = NULL; + + delete [] mDirtyPolicy; + mDirtyPolicy = NULL; } mPolicyCount = 0; @@ -101,44 +107,21 @@ void HttpLibcurl::start(int policy_count) llassert_always(policy_count <= HTTP_POLICY_CLASS_LIMIT); llassert_always(! mMultiHandles); // One-time call only - HttpPolicy & policy(mService->getPolicy()); mPolicyCount = policy_count; mMultiHandles = new CURLM * [mPolicyCount]; mActiveHandles = new int [mPolicyCount]; + mDirtyPolicy = new bool [mPolicyCount]; for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) { - HttpPolicyClass & options(policy.getClassOptions(policy_class)); - - mActiveHandles[policy_class] = 0; if (NULL == (mMultiHandles[policy_class] = curl_multi_init())) { - LL_ERRS("CoreHttp") << "Failed to allocate multi handle in libcurl." - << LL_ENDL; - } - - if (options.mPipelining > 1) - { - CURLMcode code; - - // We'll try to do pipelining on this multihandle - code = curl_multi_setopt(mMultiHandles[policy_class], - CURLMOPT_PIPELINING, - 1L); - check_curl_multi_code(code, CURLMOPT_PIPELINING); - code = curl_multi_setopt(mMultiHandles[policy_class], - CURLMOPT_MAX_PIPELINE_LENGTH, - long(options.mPipelining)); - check_curl_multi_code(code, CURLMOPT_MAX_PIPELINE_LENGTH); - code = curl_multi_setopt(mMultiHandles[policy_class], - CURLMOPT_MAX_HOST_CONNECTIONS, - long(options.mPerHostConnectionLimit)); - check_curl_multi_code(code, CURLMOPT_MAX_HOST_CONNECTIONS); - code = curl_multi_setopt(mMultiHandles[policy_class], - CURLMOPT_MAX_TOTAL_CONNECTIONS, - long(options.mConnectionLimit)); - check_curl_multi_code(code, CURLMOPT_MAX_TOTAL_CONNECTIONS); + LL_ERRS(LOG_CORE) << "Failed to allocate multi handle in libcurl." + << LL_ENDL; } + mActiveHandles[policy_class] = 0; + mDirtyPolicy[policy_class] = false; + policyUpdated(policy_class); } } @@ -156,8 +139,19 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() // Give libcurl some cycles to do I/O & callbacks for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) { - if (! mActiveHandles[policy_class] || ! mMultiHandles[policy_class]) + if (! mMultiHandles[policy_class]) { + // No handle, nothing to do. + continue; + } + if (! mActiveHandles[policy_class]) + { + // If we've gone quiet and there's a dirty update, apply it, + // otherwise we're done. + if (mDirtyPolicy[policy_class]) + { + policyUpdated(policy_class); + } continue; } @@ -192,9 +186,9 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() } else { - LL_WARNS_ONCE("CoreHttp") << "Unexpected message from libcurl. Msg code: " - << msg->msg - << LL_ENDL; + LL_WARNS_ONCE(LOG_CORE) << "Unexpected message from libcurl. Msg code: " + << msg->msg + << LL_ENDL; } msgs_in_queue = 0; } @@ -230,11 +224,11 @@ void HttpLibcurl::addOp(HttpOpRequest * op) { HttpPolicy & policy(mService->getPolicy()); - LL_INFOS("CoreHttp") << "TRACE, ToActiveQueue, Handle: " - << static_cast(op) - << ", Actives: " << mActiveOps.size() - << ", Readies: " << policy.getReadyCount(op->mReqPolicy) - << LL_ENDL; + LL_INFOS(LOG_CORE) << "TRACE, ToActiveQueue, Handle: " + << static_cast(op) + << ", Actives: " << mActiveOps.size() + << ", Readies: " << policy.getReadyCount(op->mReqPolicy) + << LL_ENDL; } // On success, make operation active @@ -286,10 +280,10 @@ void HttpLibcurl::cancelRequest(HttpOpRequest * op) // Tracing if (op->mTracing > HTTP_TRACE_OFF) { - LL_INFOS("CoreHttp") << "TRACE, RequestCanceled, Handle: " - << static_cast(op) - << ", Status: " << op->mStatus.toTerseString() - << LL_ENDL; + LL_INFOS(LOG_CORE) << "TRACE, RequestCanceled, Handle: " + << static_cast(op) + << ", Status: " << op->mStatus.toTerseString() + << LL_ENDL; } // Cancel op and deliver for notification @@ -306,18 +300,18 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode if (handle != op->mCurlHandle || ! op->mCurlActive) { - LL_WARNS("CoreHttp") << "libcurl handle and HttpOpRequest handle in disagreement or inactive request." - << " Handle: " << static_cast(handle) - << LL_ENDL; + LL_WARNS(LOG_CORE) << "libcurl handle and HttpOpRequest handle in disagreement or inactive request." + << " Handle: " << static_cast(handle) + << LL_ENDL; return false; } active_set_t::iterator it(mActiveOps.find(op)); if (mActiveOps.end() == it) { - LL_WARNS("CoreHttp") << "libcurl completion for request not on active list. Continuing." - << " Handle: " << static_cast(handle) - << LL_ENDL; + LL_WARNS(LOG_CORE) << "libcurl completion for request not on active list. Continuing." + << " Handle: " << static_cast(handle) + << LL_ENDL; return false; } @@ -348,9 +342,9 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode } else { - LL_WARNS("CoreHttp") << "Invalid HTTP response code (" - << http_status << ") received from server." - << LL_ENDL; + LL_WARNS(LOG_CORE) << "Invalid HTTP response code (" + << http_status << ") received from server." + << LL_ENDL; op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS); } } @@ -363,10 +357,10 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode // Tracing if (op->mTracing > HTTP_TRACE_OFF) { - LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle: " - << static_cast(op) - << ", Status: " << op->mStatus.toTerseString() - << LL_ENDL; + LL_INFOS(LOG_CORE) << "TRACE, RequestComplete, Handle: " + << static_cast(op) + << ", Status: " << op->mStatus.toTerseString() + << LL_ENDL; } // Dispatch to next stage @@ -390,6 +384,88 @@ int HttpLibcurl::getActiveCountInClass(int policy_class) const return mActiveHandles ? mActiveHandles[policy_class] : 0; } +void HttpLibcurl::policyUpdated(int policy_class) +{ + if (policy_class < 0 || policy_class >= mPolicyCount || ! mMultiHandles) + { + return; + } + + HttpPolicy & policy(mService->getPolicy()); + + if (! mActiveHandles[policy_class]) + { + // Clear to set options. As of libcurl 7.37.0, if a pipelining + // multi handle has active requests and you try to set the + // multi handle to non-pipelining, the library gets very angry + // and goes off the rails corrupting memory. A clue that you're + // about to crash is that you'll get a missing server response + // error (curl code 9). So, if options are to be set, we let + // the multi handle run out of requests, then set options, and + // re-enable request processing. + // + // All of this stall mechanism exists for this reason. If + // libcurl becomes more resilient later, it should be possible + // to remove all of this. The connection limit settings are fine, + // it's just that pipelined-to-non-pipelined transition that + // is fatal at the moment. + + HttpPolicyClass & options(policy.getClassOptions(policy_class)); + CURLM * multi_handle(mMultiHandles[policy_class]); + CURLMcode code; + + // Enable policy if stalled + policy.stallPolicy(policy_class, false); + mDirtyPolicy[policy_class] = false; + + if (options.mPipelining > 1) + { + // We'll try to do pipelining on this multihandle + code = curl_multi_setopt(multi_handle, + CURLMOPT_PIPELINING, + 1L); + check_curl_multi_code(code, CURLMOPT_PIPELINING); + code = curl_multi_setopt(multi_handle, + CURLMOPT_MAX_PIPELINE_LENGTH, + long(options.mPipelining)); + check_curl_multi_code(code, CURLMOPT_MAX_PIPELINE_LENGTH); + code = curl_multi_setopt(multi_handle, + CURLMOPT_MAX_HOST_CONNECTIONS, + long(options.mPerHostConnectionLimit)); + check_curl_multi_code(code, CURLMOPT_MAX_HOST_CONNECTIONS); + code = curl_multi_setopt(multi_handle, + CURLMOPT_MAX_TOTAL_CONNECTIONS, + long(options.mConnectionLimit)); + check_curl_multi_code(code, CURLMOPT_MAX_TOTAL_CONNECTIONS); + } + else + { + code = curl_multi_setopt(multi_handle, + CURLMOPT_PIPELINING, + 0L); + check_curl_multi_code(code, CURLMOPT_PIPELINING); + code = curl_multi_setopt(multi_handle, + CURLMOPT_MAX_HOST_CONNECTIONS, + 0L); + check_curl_multi_code(code, CURLMOPT_MAX_HOST_CONNECTIONS); + code = curl_multi_setopt(multi_handle, + CURLMOPT_MAX_TOTAL_CONNECTIONS, + long(options.mConnectionLimit)); + check_curl_multi_code(code, CURLMOPT_MAX_TOTAL_CONNECTIONS); + } + } + else if (! mDirtyPolicy[policy_class]) + { + // Mark policy dirty and request a stall in the policy. + // When policy goes idle, we'll re-invoke this method + // and perform the change. Don't allow this thread to + // sleep while we're waiting for quiescence, we'll just + // stop processing. + mDirtyPolicy[policy_class] = true; + policy.stallPolicy(policy_class, true); + } +} + // --------------------------------------- // Free functions @@ -424,9 +500,9 @@ void check_curl_multi_code(CURLMcode code, int curl_setopt_option) { if (CURLM_OK != code) { - LL_WARNS("CoreHttp") << "libcurl multi error detected: " << curl_multi_strerror(code) - << ", curl_multi_setopt option: " << curl_setopt_option - << LL_ENDL; + LL_WARNS(LOG_CORE) << "libcurl multi error detected: " << curl_multi_strerror(code) + << ", curl_multi_setopt option: " << curl_setopt_option + << LL_ENDL; } } @@ -435,8 +511,8 @@ void check_curl_multi_code(CURLMcode code) { if (CURLM_OK != code) { - LL_WARNS("CoreHttp") << "libcurl multi error detected: " << curl_multi_strerror(code) - << LL_ENDL; + LL_WARNS(LOG_CORE) << "libcurl multi error detected: " << curl_multi_strerror(code) + << LL_ENDL; } } diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 67f98dd4f0..2c7ad1fa8e 100755 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -116,6 +116,14 @@ public: /// Threading: called by worker thread. bool cancel(HttpHandle handle); + /// Informs transport that a particular policy class has had + /// options changed and so should effect any transport state + /// change necessary to effect those changes. Used mainly for + /// initialization and dynamic option setting. + /// + /// Threading: called by worker thread. + void policyUpdated(int policy_class); + protected: /// Invoked when libcurl has indicated a request has been processed /// to completion and we need to move the request to a new state. @@ -134,6 +142,8 @@ protected: int mPolicyCount; CURLM ** mMultiHandles; // One handle per policy class int * mActiveHandles; // Active count per policy class + bool * mDirtyPolicy; // Dirty policy update waiting for stall (per pc) + }; // end class HttpLibcurl } // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index 5bb0654652..fefe561f80 100755 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -38,6 +38,14 @@ #include "lltimer.h" +namespace +{ + +static const char * const LOG_CORE("CoreHttp"); + +} // end anonymous namespace + + namespace LLCore { @@ -94,8 +102,8 @@ void HttpOperation::stageFromRequest(HttpService *) // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - LL_ERRS("CoreHttp") << "Default stageFromRequest method may not be called." - << LL_ENDL; + LL_ERRS(LOG_CORE) << "Default stageFromRequest method may not be called." + << LL_ENDL; } @@ -104,8 +112,8 @@ void HttpOperation::stageFromReady(HttpService *) // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - LL_ERRS("CoreHttp") << "Default stageFromReady method may not be called." - << LL_ENDL; + LL_ERRS(LOG_CORE) << "Default stageFromReady method may not be called." + << LL_ENDL; } @@ -114,8 +122,8 @@ void HttpOperation::stageFromActive(HttpService *) // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - LL_ERRS("CoreHttp") << "Default stageFromActive method may not be called." - << LL_ENDL; + LL_ERRS(LOG_CORE) << "Default stageFromActive method may not be called." + << LL_ENDL; } @@ -145,9 +153,9 @@ void HttpOperation::addAsReply() { if (mTracing > HTTP_TRACE_OFF) { - LL_INFOS("CoreHttp") << "TRACE, ToReplyQueue, Handle: " - << static_cast(this) - << LL_ENDL; + LL_INFOS(LOG_CORE) << "TRACE, ToReplyQueue, Handle: " + << static_cast(this) + << LL_ENDL; } if (mReplyQueue) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 43dd069bc6..eb664fdced 100755 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -94,6 +94,8 @@ void os_strlower(char * str); void check_curl_easy_code(CURLcode code); void check_curl_easy_code(CURLcode code, int curl_setopt_option); +static const char * const LOG_CORE("CoreHttp"); + } // end anonymous namespace @@ -416,8 +418,8 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) if (! mCurlHandle) { // We're in trouble. We'll continue but it won't go well. - LL_WARNS("CoreHttp") << "Failed to allocate libcurl easy handle. Continuing." - << LL_ENDL; + LL_WARNS(LOG_CORE) << "Failed to allocate libcurl easy handle. Continuing." + << LL_ENDL; return HttpStatus(HttpStatus::LLCORE, HE_BAD_ALLOC); } code = curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); @@ -538,9 +540,9 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) break; default: - LL_ERRS("CoreHttp") << "Invalid HTTP method in request: " - << int(mReqMethod) << ". Can't recover." - << LL_ENDL; + LL_ERRS(LOG_CORE) << "Invalid HTTP method in request: " + << int(mReqMethod) << ". Can't recover." + << LL_ENDL; break; } @@ -652,8 +654,8 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void { // Warn but continue if the read position moves beyond end-of-body // for some reason. - LL_WARNS("CoreHttp") << "Request body position beyond body size. Truncating request body." - << LL_ENDL; + LL_WARNS(LOG_CORE) << "Request body position beyond body size. Truncating request body." + << LL_ENDL; } return 0; } @@ -790,10 +792,10 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi else { // Ignore the unparsable. - LL_INFOS_ONCE("CoreHttp") << "Problem parsing odd Content-Range header: '" - << std::string(hdr_data, wanted_hdr_size) - << "'. Ignoring." - << LL_ENDL; + LL_INFOS_ONCE(LOG_CORE) << "Problem parsing odd Content-Range header: '" + << std::string(hdr_data, wanted_hdr_size) + << "'. Ignoring." + << LL_ENDL; } } @@ -895,11 +897,11 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe if (logit) { - LL_INFOS("CoreHttp") << "TRACE, LibcurlDebug, Handle: " - << static_cast(op) - << ", Type: " << tag - << ", Data: " << safe_line - << LL_ENDL; + LL_INFOS(LOG_CORE) << "TRACE, LibcurlDebug, Handle: " + << static_cast(op) + << ", Type: " << tag + << ", Data: " << safe_line + << LL_ENDL; } return 0; @@ -1094,9 +1096,9 @@ void check_curl_easy_code(CURLcode code, int curl_setopt_option) // // linux appears to throw a curl error once per session for a bad initialization // at a pretty random time (when enabling cookies). - LL_WARNS("CoreHttp") << "libcurl error detected: " << curl_easy_strerror(code) - << ", curl_easy_setopt option: " << curl_setopt_option - << LL_ENDL; + LL_WARNS(LOG_CORE) << "libcurl error detected: " << curl_easy_strerror(code) + << ", curl_easy_setopt option: " << curl_setopt_option + << LL_ENDL; } } @@ -1109,8 +1111,8 @@ void check_curl_easy_code(CURLcode code) // // linux appears to throw a curl error once per session for a bad initialization // at a pretty random time (when enabling cookies). - LL_WARNS("CoreHttp") << "libcurl error detected: " << curl_easy_strerror(code) - << LL_ENDL; + LL_WARNS(LOG_CORE) << "libcurl error detected: " << curl_easy_strerror(code) + << LL_ENDL; } } diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index bb7959b578..09b9206f63 100755 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -35,6 +35,13 @@ #include "lltimer.h" +namespace +{ + +static const char * const LOG_CORE("CoreHttp"); + +} // end anonymous namespace + namespace LLCore { @@ -51,7 +58,8 @@ public: ClassState() : mThrottleEnd(0), mThrottleLeft(0L), - mRequestCount(0L) + mRequestCount(0L), + mStallStaging(false) {} HttpReadyQueue mReadyQueue; @@ -61,6 +69,7 @@ public: HttpTime mThrottleEnd; long mThrottleLeft; long mRequestCount; + bool mStallStaging; }; @@ -171,19 +180,19 @@ void HttpPolicy::retryOp(HttpOpRequest * op) { ++op->mPolicy503Retries; } - LL_DEBUGS("CoreHttp") << "HTTP request " << static_cast(op) - << " retry " << op->mPolicyRetries - << " scheduled in " << (delta / HttpTime(1000)) - << " mS (" << (external_delta ? "external" : "internal") - << "). Status: " << op->mStatus.toTerseString() - << LL_ENDL; + LL_DEBUGS(LOG_CORE) << "HTTP request " << static_cast(op) + << " retry " << op->mPolicyRetries + << " scheduled in " << (delta / HttpTime(1000)) + << " mS (" << (external_delta ? "external" : "internal") + << "). Status: " << op->mStatus.toTerseString() + << LL_ENDL; if (op->mTracing > HTTP_TRACE_OFF) { - LL_INFOS("CoreHttp") << "TRACE, ToRetryQueue, Handle: " - << static_cast(op) - << ", Delta: " << (delta / HttpTime(1000)) - << ", Retries: " << op->mPolicyRetries - << LL_ENDL; + LL_INFOS(LOG_CORE) << "TRACE, ToRetryQueue, Handle: " + << static_cast(op) + << ", Delta: " << (delta / HttpTime(1000)) + << ", Retries: " << op->mPolicyRetries + << LL_ENDL; } mClasses[policy_class]->mRetryQueue.push(op); } @@ -219,6 +228,15 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() HttpRetryQueue & retryq(state.mRetryQueue); HttpReadyQueue & readyq(state.mReadyQueue); + if (state.mStallStaging) + { + // Stalling but don't sleep. Need to complete operations + // and get back to servicing queues. Do this test before + // the retryq/readyq test or you'll get stalls until you + // click a setting or an asset request comes in. + result = HttpService::NORMAL; + continue; + } if (retryq.empty() && readyq.empty()) { continue; @@ -262,9 +280,9 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() if (now >= state.mThrottleEnd) { // Throttle expired, move to next window - LL_DEBUGS("CoreHttp") << "Throttle expired with " << state.mThrottleLeft - << " requests to go and " << state.mRequestCount - << " requests issued." << LL_ENDL; + LL_DEBUGS(LOG_CORE) << "Throttle expired with " << state.mThrottleLeft + << " requests to go and " << state.mRequestCount + << " requests issued." << LL_ENDL; state.mThrottleLeft = state.mOptions.mThrottleRate; state.mThrottleEnd = now + HttpTime(1000000); } @@ -291,9 +309,9 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() if (now >= state.mThrottleEnd) { // Throttle expired, move to next window - LL_DEBUGS("CoreHttp") << "Throttle expired with " << state.mThrottleLeft - << " requests to go and " << state.mRequestCount - << " requests issued." << LL_ENDL; + LL_DEBUGS(LOG_CORE) << "Throttle expired with " << state.mThrottleLeft + << " requests to go and " << state.mRequestCount + << " requests issued." << LL_ENDL; state.mThrottleLeft = state.mOptions.mThrottleRate; state.mThrottleEnd = now + HttpTime(1000000); } @@ -408,17 +426,17 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) // This op is done, finalize it delivering it to the reply queue... if (! op->mStatus) { - LL_WARNS("CoreHttp") << "HTTP request " << static_cast(op) - << " failed after " << op->mPolicyRetries - << " retries. Reason: " << op->mStatus.toString() - << " (" << op->mStatus.toTerseString() << ")" - << LL_ENDL; + LL_WARNS(LOG_CORE) << "HTTP request " << static_cast(op) + << " failed after " << op->mPolicyRetries + << " retries. Reason: " << op->mStatus.toString() + << " (" << op->mStatus.toTerseString() << ")" + << LL_ENDL; } else if (op->mPolicyRetries) { - LL_DEBUGS("CoreHttp") << "HTTP request " << static_cast(op) - << " succeeded on retry " << op->mPolicyRetries << "." - << LL_ENDL; + LL_DEBUGS(LOG_CORE) << "HTTP request " << static_cast(op) + << " succeeded on retry " << op->mPolicyRetries << "." + << LL_ENDL; } op->stageFromActive(mService); @@ -446,4 +464,17 @@ int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const } +bool HttpPolicy::stallPolicy(HttpRequest::policy_t policy_class, bool stall) +{ + bool ret(false); + + if (policy_class < mClasses.size()) + { + ret = mClasses[policy_class]->mStallStaging; + mClasses[policy_class]->mStallStaging = stall; + } + return ret; +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index bf1aa74267..11cd89bbd1 100755 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -158,6 +158,14 @@ public: /// Threading: called by worker thread int getReadyCount(HttpRequest::policy_t policy_class) const; + /// Stall (or unstall) a policy class preventing requests from + /// transitioning to an active state. Used to allow an HTTP + /// request policy to empty prior to changing settings or state + /// that isn't tolerant of changes when work is outstanding. + /// + /// Threading: called by worker thread + bool stallPolicy(HttpRequest::policy_t policy_class, bool stall); + protected: struct ClassState; typedef std::vector class_list_t; diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index c94249dc2d..c673e1be1d 100755 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -40,6 +40,14 @@ #include "llthread.h" +namespace +{ + +static const char * const LOG_CORE("CoreHttp"); + +} // end anonymous namespace + + namespace LLCore { @@ -87,8 +95,8 @@ HttpService::~HttpService() // Failed to join, expect problems ahead so do a hard termination. mThread->cancel(); - LL_WARNS("CoreHttp") << "Destroying HttpService with running thread. Expect problems." - << LL_ENDL; + LL_WARNS(LOG_CORE) << "Destroying HttpService with running thread. Expect problems." + << LL_ENDL; } } } @@ -328,9 +336,9 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) if (op->mTracing > HTTP_TRACE_OFF) { - LL_INFOS("CoreHttp") << "TRACE, FromRequestQueue, Handle: " - << static_cast(op) - << LL_ENDL; + LL_INFOS(LOG_CORE) << "TRACE, FromRequestQueue, Handle: " + << static_cast(op) + << LL_ENDL; } // Stage @@ -437,9 +445,13 @@ HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ HttpPolicyClass & opts(mPolicy->getClassOptions(pclass)); status = opts.set(opt, value); - if (status && ret_value) + if (status) { - status = opts.get(opt, ret_value); + mTransport->policyUpdated(pclass); + if (ret_value) + { + status = opts.get(opt, ret_value); + } } } @@ -463,7 +475,7 @@ HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ return status; } - // Only string values are global at this time + // String values are always global (at this time). if (pclass == HttpRequest::GLOBAL_POLICY_ID) { HttpPolicyGlobal & opts(mPolicy->getGlobalOptions()); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 50f56cf6ff..0607579a08 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4459,7 +4459,7 @@ HttpPipelining Comment - If true, viewer will pipeline HTTP requests to servers. Static. + If true, viewer will pipeline HTTP requests to servers. Persist 1 Type @@ -4470,7 +4470,7 @@ HttpRangeRequestsDisable Comment - If true, viewer will not issued range GET requests for meshes and textures. + If true, viewer will not issued range GET requests for meshes and textures. May resolve problems with certain ISPs and networking gear. Persist 1 Type diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index d097f18d61..464e60948a 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -60,7 +60,7 @@ static const struct "other" }, { // AP_TEXTURE - 4, 1, 12, 0, true, + 8, 1, 12, 0, true, "TextureFetchConcurrency", "texture fetch" }, @@ -70,7 +70,7 @@ static const struct "mesh fetch" }, { // AP_MESH2 - 4, 1, 32, 100, true, + 8, 1, 32, 100, true, "Mesh2MaxConcurrentRequests", "mesh2 fetch" }, @@ -126,14 +126,6 @@ void LLAppCoreHttp::init() << LL_ENDL; } - // Global pipelining preference from settings - static const std::string http_pipelining("HttpPipelining"); - if (gSavedSettings.controlExists(http_pipelining)) - { - // Default to true if absent. - mPipelined = gSavedSettings.getBOOL(http_pipelining); - } - // Point to our certs or SSH/https: will fail on connect status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE, LLCore::HttpRequest::GLOBAL_POLICY_ID, @@ -210,12 +202,27 @@ void LLAppCoreHttp::init() << LL_ENDL; } - // *NOTE: Pipelining isn't dynamic yet. When it is, add a global - // signal for the setting here. - + // Signal for global pipelining preference from settings + static const std::string http_pipelining("HttpPipelining"); + if (gSavedSettings.controlExists(http_pipelining)) + { + LLPointer cntrl_ptr = gSavedSettings.getControl(http_pipelining); + if (cntrl_ptr.isNull()) + { + LL_WARNS("Init") << "Unable to set signal on global setting '" << http_pipelining + << "'" << LL_ENDL; + } + else + { + mPipelinedSignal = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed)); + } + } + // Register signals for settings and state changes for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) { + const EAppPolicy app_policy(static_cast(i)); + if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey)) { LLPointer cntrl_ptr = gSavedSettings.getControl(init_data[i].mKey); @@ -226,7 +233,7 @@ void LLAppCoreHttp::init() } else { - mHttpClasses[i].mSettingsSignal = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed)); + mHttpClasses[app_policy].mSettingsSignal = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed)); } } } @@ -282,6 +289,7 @@ void LLAppCoreHttp::cleanup() { mHttpClasses[i].mSettingsSignal.disconnect(); } + mPipelinedSignal.disconnect(); delete mRequest; mRequest = NULL; @@ -299,6 +307,20 @@ void LLAppCoreHttp::cleanup() void LLAppCoreHttp::refreshSettings(bool initial) { LLCore::HttpStatus status; + + // Global pipelining setting + bool pipeline_changed(false); + static const std::string http_pipelining("HttpPipelining"); + if (gSavedSettings.controlExists(http_pipelining)) + { + // Default to true (in ctor) if absent. + bool pipelined(gSavedSettings.getBOOL(http_pipelining)); + if (pipelined != mPipelined) + { + mPipelined = pipelined; + pipeline_changed = true; + } + } for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) { @@ -323,33 +345,42 @@ void LLAppCoreHttp::refreshSettings(bool initial) } } - mHttpClasses[app_policy].mPipelined = false; - if (mPipelined && init_data[i].mPipelined) + } + + // Init- or run-time settings. Must use the queued request API. + + // Pipelining changes + if (initial || pipeline_changed) + { + const bool to_pipeline(mPipelined && init_data[i].mPipelined); + if (to_pipeline != mHttpClasses[app_policy].mPipelined) { - // Pipelining election is currently static (init-time). - // Making it dynamic isn't too hard in the SL code but verifying - // that libcurl handles the on-to-off transition while holding - // outstanding requests is something that should be tested. + // Pipeline election changing, set dynamic option via request + + LLCore::HttpHandle handle; + const long new_depth(to_pipeline ? PIPELINING_DEPTH : 0); - status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_PIPELINING_DEPTH, - mHttpClasses[app_policy].mPolicy, - PIPELINING_DEPTH, - NULL); - if (! status) + handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_PIPELINING_DEPTH, + mHttpClasses[app_policy].mPolicy, + new_depth, + NULL); + if (LLCORE_HTTP_HANDLE_INVALID == handle) { + status = mRequest->getStatus(); LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage - << " to pipelined mode. Reason: " << status.toString() + << " pipelining. Reason: " << status.toString() << LL_ENDL; } else { - mHttpClasses[app_policy].mPipelined = true; + LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage + << " pipelining. New value: " << new_depth + << LL_ENDL; + mHttpClasses[app_policy].mPipelined = to_pipeline; } } } - - // Init- or run-time settings - + // Get target connection concurrency value U32 setting(init_data[i].mDefault); if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey)) @@ -362,63 +393,60 @@ void LLAppCoreHttp::refreshSettings(bool initial) } } - if (! initial && setting == mHttpClasses[app_policy].mConnLimit) + if (initial || setting != mHttpClasses[app_policy].mConnLimit || pipeline_changed) { - // Unchanged, try next setting - continue; - } - - // Set it and report. Strategies depend on pipelining: - // - // No Pipelining. Llcorehttp manages connections itself based - // on the PO_CONNECTION_LIMIT setting. Set both limits to the - // same value for logical consistency. In the future, may - // hand over connection management to libcurl after the - // connection cache has been better vetted. - // - // Pipelining. Libcurl is allowed to manage connections to a - // great degree. Steady state will connection limit based on - // the per-host setting. Transitions (region crossings, new - // avatars, etc.) can request additional outbound connections - // to other servers via 2X total connection limit. - // - LLCore::HttpHandle handle; - handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT, - mHttpClasses[app_policy].mPolicy, - (mHttpClasses[app_policy].mPipelined ? 2 * setting : setting), - NULL); - if (LLCORE_HTTP_HANDLE_INVALID == handle) - { - status = mRequest->getStatus(); - LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage - << " concurrency. Reason: " << status.toString() - << LL_ENDL; - } - else - { - handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_PER_HOST_CONNECTION_LIMIT, + // Set it and report. Strategies depend on pipelining: + // + // No Pipelining. Llcorehttp manages connections itself based + // on the PO_CONNECTION_LIMIT setting. Set both limits to the + // same value for logical consistency. In the future, may + // hand over connection management to libcurl after the + // connection cache has been better vetted. + // + // Pipelining. Libcurl is allowed to manage connections to a + // great degree. Steady state will connection limit based on + // the per-host setting. Transitions (region crossings, new + // avatars, etc.) can request additional outbound connections + // to other servers via 2X total connection limit. + // + LLCore::HttpHandle handle; + handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT, mHttpClasses[app_policy].mPolicy, - setting, + (mHttpClasses[app_policy].mPipelined ? 2 * setting : setting), NULL); if (LLCORE_HTTP_HANDLE_INVALID == handle) { status = mRequest->getStatus(); LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage - << " per-host concurrency. Reason: " << status.toString() + << " concurrency. Reason: " << status.toString() << LL_ENDL; } else { - LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage - << " concurrency. New value: " << setting - << LL_ENDL; - mHttpClasses[app_policy].mConnLimit = setting; - if (initial && setting != init_data[i].mDefault) + handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_PER_HOST_CONNECTION_LIMIT, + mHttpClasses[app_policy].mPolicy, + setting, + NULL); + if (LLCORE_HTTP_HANDLE_INVALID == handle) { - LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage - << " concurrency. New value: " << setting + status = mRequest->getStatus(); + LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage + << " per-host concurrency. Reason: " << status.toString() << LL_ENDL; } + else + { + LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage + << " concurrency. New value: " << setting + << LL_ENDL; + mHttpClasses[app_policy].mConnLimit = setting; + if (initial && setting != init_data[i].mDefault) + { + LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage + << " concurrency. New value: " << setting + << LL_ENDL; + } + } } } } diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h index 63c8a11180..9ad4eb4b30 100755 --- a/indra/newview/llappcorehttp.h +++ b/indra/newview/llappcorehttp.h @@ -218,6 +218,7 @@ private: bool mStopped; HttpClass mHttpClasses[AP_COUNT]; bool mPipelined; // Global setting + boost::signals2::connection mPipelinedSignal; // Signal for 'HttpPipelining' setting }; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 4008a6948d..097a7b374f 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -483,12 +483,12 @@ private: bool acquireHttpSemaphore() { llassert(! mHttpHasResource); - if (mFetcher->mHttpSemaphore <= 0) + if (mFetcher->mHttpSemaphore >= mFetcher->mHttpHighWater) { return false; } mHttpHasResource = true; - mFetcher->mHttpSemaphore--; + mFetcher->mHttpSemaphore++; return true; } @@ -498,7 +498,8 @@ private: { llassert(mHttpHasResource); mHttpHasResource = false; - mFetcher->mHttpSemaphore++; + mFetcher->mHttpSemaphore--; + llassert_always(mFetcher->mHttpSemaphore >= 0); } private: @@ -2523,6 +2524,8 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), U32Bytes(gSavedSettings.getU32("TextureLoggingThreshold"))); + LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); + mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_TEXTURE); mHttpRequest = new LLCore::HttpRequest; mHttpOptions = new LLCore::HttpOptions; mHttpOptionsWithHeaders = new LLCore::HttpOptions; @@ -2531,21 +2534,9 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpHeaders->append("Accept", "image/x-j2c"); mHttpMetricsHeaders = new LLCore::HttpHeaders; mHttpMetricsHeaders->append("Content-Type", "application/llsd+xml"); - LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); - mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_TEXTURE); - if (app_core_http.isPipelined(LLAppCoreHttp::AP_TEXTURE)) - { - // Init-time election that will have to change for - // support of dynamic changes to the pipelining enable flag. - mHttpHighWater = HTTP_PIPE_REQUESTS_HIGH_WATER; - mHttpLowWater = HTTP_PIPE_REQUESTS_LOW_WATER; - } - else - { - mHttpHighWater = HTTP_NONPIPE_REQUESTS_HIGH_WATER; - mHttpLowWater = HTTP_NONPIPE_REQUESTS_LOW_WATER; - } - mHttpSemaphore = mHttpHighWater; + mHttpHighWater = HTTP_NONPIPE_REQUESTS_HIGH_WATER; + mHttpLowWater = HTTP_NONPIPE_REQUESTS_LOW_WATER; + mHttpSemaphore = 0; // Conditionally construct debugger object after 'this' is // fully initialized. @@ -3032,6 +3023,20 @@ bool LLTextureFetch::runCondition() // Threads: Ttf void LLTextureFetch::commonUpdate() { + // Update low/high water levels based on pipelining. We pick + // up setting eventually, so the semaphore/request level can + // fall outside the [0..HIGH_WATER] range. Expect that. + if (LLAppViewer::instance()->getAppCoreHttp().isPipelined(LLAppCoreHttp::AP_TEXTURE)) + { + mHttpHighWater = HTTP_PIPE_REQUESTS_HIGH_WATER; + mHttpLowWater = HTTP_PIPE_REQUESTS_LOW_WATER; + } + else + { + mHttpHighWater = HTTP_NONPIPE_REQUESTS_HIGH_WATER; + mHttpLowWater = HTTP_NONPIPE_REQUESTS_LOW_WATER; + } + // Release waiters releaseHttpWaiters(); @@ -3693,8 +3698,16 @@ void LLTextureFetch::releaseHttpWaiters() { // Use mHttpSemaphore rather than mHTTPTextureQueue.size() // to avoid a lock. - if (mHttpSemaphore < (mHttpHighWater - mHttpLowWater)) + if (mHttpSemaphore >= mHttpLowWater) return; + S32 needed(mHttpHighWater - mHttpSemaphore); + if (needed <= 0) + { + // Would only happen if High/LowWater were changed behind + // our back. In that case, defer fill until usage falls within + // limits. + return; + } // Quickly make a copy of all the LLUIDs. Get off the // mutex as early as possible. @@ -3743,10 +3756,10 @@ void LLTextureFetch::releaseHttpWaiters() tids.clear(); // Sort into priority order, if necessary and only as much as needed - if (tids2.size() > mHttpSemaphore) + if (tids2.size() > needed) { LLTextureFetchWorker::Compare compare; - std::partial_sort(tids2.begin(), tids2.begin() + mHttpSemaphore, tids2.end(), compare); + std::partial_sort(tids2.begin(), tids2.begin() + needed, tids2.end(), compare); } // Release workers up to the high water mark. Since we aren't diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index d13736997f..89d18e2c67 100755 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -356,8 +356,8 @@ private: LLCore::HttpHeaders * mHttpHeaders; // Ttf LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf LLCore::HttpRequest::policy_t mHttpPolicyClass; // T* - S32 mHttpHighWater; // T* (ro) - S32 mHttpLowWater; // T* (ro) + S32 mHttpHighWater; // Ttf + S32 mHttpLowWater; // Ttf // We use a resource semaphore to keep HTTP requests in // WAIT_HTTP_RESOURCE2 if there aren't sufficient slots in the @@ -366,7 +366,11 @@ private: // where it's more expensive to get at them. Requests in either // SEND_HTTP_REQ or WAIT_HTTP_REQ charge against the semaphore // and tracking state transitions is critical to liveness. - LLAtomicS32 mHttpSemaphore; // Ttf + Tmain + // + // Originally implemented as a traditional semaphore (heading towards + // zero), it now is an outstanding request count that is allowed to + // exceed the high water level (but not go below zero). + LLAtomicS32 mHttpSemaphore; // Ttf typedef std::set wait_http_res_queue_t; wait_http_res_queue_t mHttpWaitResource; // Mfnq -- cgit v1.3 From f71c6c745bc390fadc571801a0d7c043249ade24 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 9 Sep 2014 15:36:35 -0400 Subject: Cleanup pass. Documentation. Get older llcorehttp-using code to use utils for any LLSD interfaces. --- indra/llmessage/llcorehttputil.cpp | 4 +- indra/newview/llinventorymodel.cpp | 14 ++++- indra/newview/llinventorymodelbackgroundfetch.cpp | 28 +++++++--- indra/newview/llmeshrepository.cpp | 64 +++++++++-------------- indra/newview/lltexturefetch.cpp | 34 ++++++------ 5 files changed, 78 insertions(+), 66 deletions(-) (limited to 'indra/newview/lltexturefetch.cpp') diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index 8d09aac971..ee80b0fd94 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -39,7 +39,9 @@ using namespace LLCore; namespace LLCoreHttpUtil { - +// *TODO: Currently converts only from XML content. A mode +// to convert using fromBinary() might be useful as well. Mesh +// headers could use it. bool responseToLLSD(HttpResponse * response, bool log, LLSD & out_llsd) { // Convert response to LLSD diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 5273fb6d96..1625ae0d1b 100755 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -3958,7 +3958,10 @@ BOOL decompress_file(const char* src_filename, const char* dst_filename) } #endif -// ==== FetchItemHttpHandler ==== + +///---------------------------------------------------------------------------- +/// Class LLInventoryModel::FetchItemHttpHandler +///---------------------------------------------------------------------------- LLInventoryModel::FetchItemHttpHandler::FetchItemHttpHandler(const LLSD & request_sd) : LLCore::HttpHandler(), @@ -4006,6 +4009,15 @@ void LLInventoryModel::FetchItemHttpHandler::onCompleted(LLCore::HttpHandle hand } // Check for 200-with-error failures + // + // Original Responder-based serivce model didn't check for these errors. + // It may be more robust to ignore this condition. With aggregated requests, + // an error in one inventory item might take down the entire request. + // So if this instead broke up the aggregated items into single requests, + // maybe that would make progress. Or perhaps there's structured information + // that can tell us what went wrong. Need to dig into this and firm up + // the API. + // // body_llsd["error"] = LLSD::emptyMap(); // Dev tool to force error handling // body_llsd["error"]["identifier"] = "Development"; // body_llsd["error"]["message"] = "You left development code in the viewer"; diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index 0c04a9c039..013134843a 100755 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -45,6 +45,10 @@ namespace { +///---------------------------------------------------------------------------- +/// Class ::BGItemHttpHandler +///---------------------------------------------------------------------------- + // // Http request handler class for single inventory item requests. // @@ -80,6 +84,10 @@ protected: }; +///---------------------------------------------------------------------------- +/// Class ::BGFolderHttpHandler +///---------------------------------------------------------------------------- + // Http request handler class for folders. // // Handler for FetchInventoryDescendents2 and FetchLibDescendents2 @@ -130,6 +138,10 @@ const char * const LOG_INV("Inventory"); } // end of namespace anonymous +///---------------------------------------------------------------------------- +/// Class LLInventoryModelBackgroundFetch +///---------------------------------------------------------------------------- + LLInventoryModelBackgroundFetch::LLInventoryModelBackgroundFetch(): mBackgroundFetchActive(FALSE), mFolderFetchActive(false), @@ -684,14 +696,12 @@ bool LLInventoryModelBackgroundFetch::fetchQueueContainsNoDescendentsOf(const LL } -// =============================== -// Anonymous Namespace Definitions -// =============================== - namespace { -// ==== BGFolderHttpHandler ==== +///---------------------------------------------------------------------------- +/// Class ::BGFolderHttpHandler +///---------------------------------------------------------------------------- void BGFolderHttpHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { @@ -734,6 +744,9 @@ void BGFolderHttpHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRes } // Check for 200-with-error failures + // + // See comments in llinventorymodel.cpp about this mode of error. + // // body_llsd["error"] = LLSD::emptyMap(); // Dev tool to force error handling // body_llsd["error"]["identifier"] = "Development"; // body_llsd["error"]["message"] = "You left development code in the viewer"; @@ -976,8 +989,9 @@ bool BGFolderHttpHandler::getIsRecursive(const LLUUID & cat_id) const return std::find(mRecursiveCatUUIDs.begin(), mRecursiveCatUUIDs.end(), cat_id) != mRecursiveCatUUIDs.end(); } - -// ==== BGItemHttpHandler ==== +///---------------------------------------------------------------------------- +/// Class ::BGItemHttpHandler +///---------------------------------------------------------------------------- // Nothing to implement here. All ctor/dtor changes. diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index a6707392fe..2b044c6916 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -71,6 +71,7 @@ #include "bufferarray.h" #include "bufferstream.h" #include "llfasttimer.h" +#include "llcorehttputil.h" #include "boost/lexical_cast.hpp" @@ -2236,21 +2237,17 @@ void LLMeshUploadThread::doWholeModelUpload() mModelData = LLSD::emptyMap(); wholeModelToLLSD(mModelData, true); LLSD body = mModelData["asset_resources"]; - dump_llsd_to_file(body,make_dump_name("whole_model_body_",dump_num)); - - LLCore::BufferArray * ba = new LLCore::BufferArray; - LLCore::BufferArrayStream bas(ba); - LLSDSerialize::toXML(body, bas); - // LLSDSerialize::toXML(mModelData, bas); // <- Enabling this will generate a convenient upload error - LLCore::HttpHandle handle = mHttpRequest->requestPost(mHttpPolicyClass, - mHttpPriority, - mWholeModelUploadURL, - ba, - mHttpOptions, - mHttpHeaders, - this); - ba->release(); - + + dump_llsd_to_file(body, make_dump_name("whole_model_body_", dump_num)); + + LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, + mHttpPolicyClass, + mHttpPriority, + mWholeModelUploadURL, + body, + mHttpOptions, + mHttpHeaders, + this); if (LLCORE_HTTP_HANDLE_INVALID == handle) { mHttpStatus = mHttpRequest->getStatus(); @@ -2294,19 +2291,14 @@ void LLMeshUploadThread::requestWholeModelFee() mModelData = LLSD::emptyMap(); wholeModelToLLSD(mModelData, false); dump_llsd_to_file(mModelData, make_dump_name("whole_model_fee_request_", dump_num)); - - LLCore::BufferArray * ba = new LLCore::BufferArray; - LLCore::BufferArrayStream bas(ba); - LLSDSerialize::toXML(mModelData, bas); - - LLCore::HttpHandle handle = mHttpRequest->requestPost(mHttpPolicyClass, - mHttpPriority, - mWholeModelFeeCapability, - ba, - mHttpOptions, - mHttpHeaders, - this); - ba->release(); + LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, + mHttpPolicyClass, + mHttpPriority, + mWholeModelFeeCapability, + mModelData, + mHttpOptions, + mHttpHeaders, + this); if (LLCORE_HTTP_HANDLE_INVALID == handle) { mHttpStatus = mHttpRequest->getStatus(); @@ -2379,12 +2371,8 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp } else { - LLCore::BufferArray * ba(response->getBody()); - if (ba && ba->size()) - { - LLCore::BufferArrayStream bas(ba); - LLSDSerialize::fromXML(body, bas); - } + // *TODO: handle error in conversion process + LLCoreHttpUtil::responseToLLSD(response, true, body); } dump_llsd_to_file(body, make_dump_name("whole_model_upload_response_", dump_num)); @@ -2443,12 +2431,8 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp } else { - LLCore::BufferArray * ba(response->getBody()); - if (ba && ba->size()) - { - LLCore::BufferArrayStream bas(ba); - LLSDSerialize::fromXML(body, bas); - } + // *TODO: handle error in conversion process + LLCoreHttpUtil::responseToLLSD(response, true, body); } dump_llsd_to_file(body, make_dump_name("whole_model_fee_response_", dump_num)); diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index a64a6ee091..a955b907d4 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -56,13 +56,13 @@ #include "llsdparam.h" #include "llsdutil.h" #include "llstartup.h" -#include "llsdserialize.h" #include "httprequest.h" #include "httphandler.h" #include "httpresponse.h" #include "bufferarray.h" #include "bufferstream.h" +#include "llcorehttputil.h" #include "llhttpretrypolicy.h" @@ -1349,20 +1349,20 @@ bool LLTextureFetchWorker::doWork(S32 param) LL_WARNS(LOG_TXT) << "trying to seek a non-default texture on the sim. Bad!" << LL_ENDL; } setUrl(http_url + "/?texture_id=" + mID.asString().c_str()); - LL_DEBUGS("Texture") << "Texture URL " << mUrl << LL_ENDL; + LL_DEBUGS(LOG_TXT) << "Texture URL: " << mUrl << LL_ENDL; mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id. } else { mCanUseHTTP = false ; - LL_DEBUGS("Texture") << "Texture not available via HTTP: no URL " << mUrl << LL_ENDL; + LL_DEBUGS(LOG_TXT) << "Texture not available via HTTP: empty URL." << LL_ENDL; } } else { // This will happen if not logged in or if a region deoes not have HTTP Texture enabled //LL_WARNS(LOG_TXT) << "Region not found for host: " << mHost << LL_ENDL; - LL_DEBUGS("Texture") << "Texture not available via HTTP: no region " << mUrl << LL_ENDL; + LL_DEBUGS(LOG_TXT) << "Texture not available via HTTP: no region " << mUrl << LL_ENDL; mCanUseHTTP = false; } } @@ -4026,7 +4026,9 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) // Update sequence number if (S32_MAX == ++report_sequence) + { report_sequence = 0; + } reporting_started = true; // Limit the size of the stats report if necessary. @@ -4035,18 +4037,16 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) if (! mCapsURL.empty()) { - LLCore::BufferArray * ba = new LLCore::BufferArray; - LLCore::BufferArrayStream bas(ba); - LLSDSerialize::toXML(sd, bas); - - fetcher->getHttpRequest().requestPost(fetcher->getPolicyClass(), - report_priority, - mCapsURL, - ba, - NULL, - fetcher->getMetricsHeaders(), - handler); - ba->release(); + // *TODO: Move this to a different class that expects POSTs sometime. + // Don't care about handle, this is a fire-and-forget operation. + LLCoreHttpUtil::requestPostWithLLSD(&fetcher->getHttpRequest(), + fetcher->getPolicyClass(), + report_priority, + mCapsURL, + sd, + NULL, + fetcher->getMetricsHeaders(), + handler); LLTextureFetch::svMetricsDataBreak = false; } else @@ -4057,7 +4057,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) // In QA mode, Metrics submode, log the result for ease of testing if (fetcher->isQAMode()) { - LL_INFOS("Textures") << ll_pretty_print_sd(sd) << LL_ENDL; + LL_INFOS(LOG_TXT) << ll_pretty_print_sd(sd) << LL_ENDL; } return true; -- cgit v1.3 From 11036d7bf471953ada9b877b8d9ce9de4b94dc5b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 19 Sep 2014 19:43:25 -0400 Subject: Cleanup work. Use http constants for content-type and accept headers in mesh and textures. For texture metrics reporting, use the AP_INVENTORY policy class which is non-pipelined and pointing (usually) in the right direction. Use a do-while(false) structure to manage common exit path code in onCompleted() methods. Identical to a 'goto' but might amuse the pedantic. Tuning on background fetch to have it cycle faster. This is experimental. I suspect with HTTP balancing in llcorehttp, we can do away with the timers here. --- indra/llmessage/llhttpconstants.cpp | 6 +- indra/llmessage/llhttpconstants.h | 3 +- indra/newview/llappcorehttp.h | 9 +- indra/newview/llinventorymodel.cpp | 102 +++++++++++---------- indra/newview/llinventorymodelbackgroundfetch.cpp | 106 +++++++++++----------- indra/newview/llmeshrepository.cpp | 4 +- indra/newview/lltexturefetch.cpp | 15 +-- indra/newview/lltexturefetch.h | 6 +- 8 files changed, 130 insertions(+), 121 deletions(-) (limited to 'indra/newview/lltexturefetch.cpp') diff --git a/indra/llmessage/llhttpconstants.cpp b/indra/llmessage/llhttpconstants.cpp index 01f4a080b0..32f76f0d70 100755 --- a/indra/llmessage/llhttpconstants.cpp +++ b/indra/llmessage/llhttpconstants.cpp @@ -3,11 +3,8 @@ * @brief Implementation of the HTTP request / response constant lookups * * $LicenseInfo:firstyear=2013&license=viewerlgpl$ - * - * Copyright (c) 2013, Linden Research, Inc. - * * Second Life Viewer Source Code - * Copyright (C) 2013, Linden Research, Inc. + * Copyright (C) 2013-2014, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -107,6 +104,7 @@ const std::string HTTP_IN_HEADER_X_FORWARDED_FOR("x-forwarded-for"); const std::string HTTP_CONTENT_LLSD_XML("application/llsd+xml"); const std::string HTTP_CONTENT_OCTET_STREAM("application/octet-stream"); +const std::string HTTP_CONTENT_VND_LL_MESH("application/vnd.ll.mesh"); const std::string HTTP_CONTENT_XML("application/xml"); const std::string HTTP_CONTENT_JSON("application/json"); const std::string HTTP_CONTENT_TEXT_HTML("text/html"); diff --git a/indra/llmessage/llhttpconstants.h b/indra/llmessage/llhttpconstants.h index 4aa3cc6394..d6bcbd3c19 100755 --- a/indra/llmessage/llhttpconstants.h +++ b/indra/llmessage/llhttpconstants.h @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2001-2013, Linden Research, Inc. + * Copyright (C) 2001-2014, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -203,6 +203,7 @@ extern const std::string HTTP_IN_HEADER_X_FORWARDED_FOR; extern const std::string HTTP_CONTENT_LLSD_XML; extern const std::string HTTP_CONTENT_OCTET_STREAM; +extern const std::string HTTP_CONTENT_VND_LL_MESH; extern const std::string HTTP_CONTENT_XML; extern const std::string HTTP_CONTENT_JSON; extern const std::string HTTP_CONTENT_TEXT_HTML; diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h index e822a40300..37d7a737e7 100755 --- a/indra/newview/llappcorehttp.h +++ b/indra/newview/llappcorehttp.h @@ -66,7 +66,7 @@ public: /// baking service. Deep queueing of requests. /// Do not share. GET requests only. /// - /// Destination: simhost:12046 & bake-texture:80 + /// Destination: simhost:12046 & {bake-texture,cdn}:80 /// Protocol: http: /// Transfer size: KB-MB /// Long poll: no @@ -95,7 +95,7 @@ public: /// Very deeply queued. Do not share. GET /// requests only. /// - /// Destination: simhost:12046 + /// Destination: simhost:12046 & cdn:80 /// Protocol: http: /// Transfer size: KB-MB /// Long poll: no @@ -113,7 +113,7 @@ public: /// traffic that can wait for longish stalls /// (default timeout 600S). /// - /// Destination: simhost:12046 + /// Destination: simhost:12046 & cdn:80 /// Protocol: http: /// Transfer size: MB /// Long poll: no @@ -163,7 +163,8 @@ public: /// Request rate: high /// Pipelined: no AP_INVENTORY, - + AP_REPORTING = AP_INVENTORY, // Piggy-back on inventory + AP_COUNT // Must be last }; diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 1625ae0d1b..ee28cef640 100755 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -3974,64 +3974,68 @@ LLInventoryModel::FetchItemHttpHandler::~FetchItemHttpHandler() void LLInventoryModel::FetchItemHttpHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { - LLCore::HttpStatus status(response->getStatus()); - // status = LLCore::HttpStatus(404); // Dev tool to force error handling - if (! status) - { - processFailure(status, response); - } - else + // Single-pass do-while used for common exit handling + do { - LLCore::BufferArray * body(response->getBody()); - // body = NULL; // Dev tool to force error handling - if (! body || ! body->size()) + LLCore::HttpStatus status(response->getStatus()); + // status = LLCore::HttpStatus(404); // Dev tool to force error handling + if (! status) { - LL_WARNS(LOG_INV) << "Missing data in inventory item query." << LL_ENDL; - processFailure("HTTP response for inventory item query missing body", response); - goto only_exit; + processFailure(status, response); } - - // body->write(0, "Garbage Response", 16); // Dev tool to force error handling - LLSD body_llsd; - if (! LLCoreHttpUtil::responseToLLSD(response, true, body_llsd)) + else { - // INFOS-level logging will occur on the parsed failure - processFailure("HTTP response for inventory item query has malformed LLSD", response); - goto only_exit; - } + LLCore::BufferArray * body(response->getBody()); + // body = NULL; // Dev tool to force error handling + if (! body || ! body->size()) + { + LL_WARNS(LOG_INV) << "Missing data in inventory item query." << LL_ENDL; + processFailure("HTTP response for inventory item query missing body", response); + break; // Goto common exit + } - // Expect top-level structure to be a map - // body_llsd = LLSD::emptyArray(); // Dev tool to force error handling - if (! body_llsd.isMap()) - { - processFailure("LLSD response for inventory item not a map", response); - goto only_exit; - } + // body->write(0, "Garbage Response", 16); // Dev tool to force error handling + LLSD body_llsd; + if (! LLCoreHttpUtil::responseToLLSD(response, true, body_llsd)) + { + // INFOS-level logging will occur on the parsed failure + processFailure("HTTP response for inventory item query has malformed LLSD", response); + break; // Goto common exit + } - // Check for 200-with-error failures - // - // Original Responder-based serivce model didn't check for these errors. - // It may be more robust to ignore this condition. With aggregated requests, - // an error in one inventory item might take down the entire request. - // So if this instead broke up the aggregated items into single requests, - // maybe that would make progress. Or perhaps there's structured information - // that can tell us what went wrong. Need to dig into this and firm up - // the API. - // - // body_llsd["error"] = LLSD::emptyMap(); // Dev tool to force error handling - // body_llsd["error"]["identifier"] = "Development"; - // body_llsd["error"]["message"] = "You left development code in the viewer"; - if (body_llsd.has("error")) - { - processFailure("Inventory application error (200-with-error)", response); - goto only_exit; - } + // Expect top-level structure to be a map + // body_llsd = LLSD::emptyArray(); // Dev tool to force error handling + if (! body_llsd.isMap()) + { + processFailure("LLSD response for inventory item not a map", response); + break; // Goto common exit + } - // Okay, process data if possible - processData(body_llsd, response); + // Check for 200-with-error failures + // + // Original Responder-based serivce model didn't check for these errors. + // It may be more robust to ignore this condition. With aggregated requests, + // an error in one inventory item might take down the entire request. + // So if this instead broke up the aggregated items into single requests, + // maybe that would make progress. Or perhaps there's structured information + // that can tell us what went wrong. Need to dig into this and firm up + // the API. + // + // body_llsd["error"] = LLSD::emptyMap(); // Dev tool to force error handling + // body_llsd["error"]["identifier"] = "Development"; + // body_llsd["error"]["message"] = "You left development code in the viewer"; + if (body_llsd.has("error")) + { + processFailure("Inventory application error (200-with-error)", response); + break; // Goto common exit + } + + // Okay, process data if possible + processData(body_llsd, response); + } } + while (false); -only_exit: // Must delete on completion. delete this; } diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index 013134843a..de1d123fe5 100755 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -288,6 +288,7 @@ void LLInventoryModelBackgroundFetch::setAllFoldersFetched() } mFolderFetchActive = false; mBackgroundFetchActive = false; + LL_INFOS(LOG_INV) << "Inventory background fetch completed" << LL_ENDL; } void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *) @@ -314,12 +315,7 @@ void LLInventoryModelBackgroundFetch::backgroundFetch() // No more categories to fetch, stop fetch process. if (mFetchQueue.empty()) { - LL_INFOS(LOG_INV) << "Inventory fetch completed" << LL_ENDL; - setAllFoldersFetched(); - mBackgroundFetchActive = false; - mFolderFetchActive = false; - return; } @@ -493,9 +489,10 @@ void LLInventoryModelBackgroundFetch::bulkFetch() // is mostly loaded, we could turn up the throttle and fill missing // inventory more quickly. static const S32 max_concurrent_fetches(12); // Outstanding requests, not connections - static const F32 new_min_time(0.5f); // *HACK: Clean this up when old code goes away entirely. + static const F32 new_min_time(0.05f); // *HACK: Clean this up when old code goes away entirely. static const U32 max_batch_size(10); + mMinTimeBetweenFetches = 0.01f; if (mMinTimeBetweenFetches < new_min_time) { mMinTimeBetweenFetches = new_min_time; // *HACK: See above. @@ -705,62 +702,66 @@ namespace void BGFolderHttpHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { - LLCore::HttpStatus status(response->getStatus()); - // status = LLCore::HttpStatus(404); // Dev tool to force error handling - if (! status) - { - processFailure(status, response); - } - else + // Single-pass do-while used for common exit handling + do { - // Response body should be present. - LLCore::BufferArray * body(response->getBody()); - // body = NULL; // Dev tool to force error handling - if (! body || ! body->size()) + LLCore::HttpStatus status(response->getStatus()); + // status = LLCore::HttpStatus(404); // Dev tool to force error handling + if (! status) { - LL_WARNS(LOG_INV) << "Missing data in inventory folder query." << LL_ENDL; - processFailure("HTTP response missing expected body", response); - goto only_exit; + processFailure(status, response); } + else + { + // Response body should be present. + LLCore::BufferArray * body(response->getBody()); + // body = NULL; // Dev tool to force error handling + if (! body || ! body->size()) + { + LL_WARNS(LOG_INV) << "Missing data in inventory folder query." << LL_ENDL; + processFailure("HTTP response missing expected body", response); + break; // Goto common exit + } - // Could test 'Content-Type' header but probably unreliable. + // Could test 'Content-Type' header but probably unreliable. - // Convert response to LLSD - // body->write(0, "Garbage Response", 16); // Dev tool to force error handling - LLSD body_llsd; - if (! LLCoreHttpUtil::responseToLLSD(response, true, body_llsd)) - { - // INFOS-level logging will occur on the parsed failure - processFailure("HTTP response contained malformed LLSD", response); - goto only_exit; - } + // Convert response to LLSD + // body->write(0, "Garbage Response", 16); // Dev tool to force error handling + LLSD body_llsd; + if (! LLCoreHttpUtil::responseToLLSD(response, true, body_llsd)) + { + // INFOS-level logging will occur on the parsed failure + processFailure("HTTP response contained malformed LLSD", response); + break; // goto common exit + } - // Expect top-level structure to be a map - // body_llsd = LLSD::emptyArray(); // Dev tool to force error handling - if (! body_llsd.isMap()) - { - processFailure("LLSD response not a map", response); - goto only_exit; - } + // Expect top-level structure to be a map + // body_llsd = LLSD::emptyArray(); // Dev tool to force error handling + if (! body_llsd.isMap()) + { + processFailure("LLSD response not a map", response); + break; // goto common exit + } - // Check for 200-with-error failures - // - // See comments in llinventorymodel.cpp about this mode of error. - // - // body_llsd["error"] = LLSD::emptyMap(); // Dev tool to force error handling - // body_llsd["error"]["identifier"] = "Development"; - // body_llsd["error"]["message"] = "You left development code in the viewer"; - if (body_llsd.has("error")) - { - processFailure("Inventory application error (200-with-error)", response); - goto only_exit; - } + // Check for 200-with-error failures + // + // See comments in llinventorymodel.cpp about this mode of error. + // + // body_llsd["error"] = LLSD::emptyMap(); // Dev tool to force error handling + // body_llsd["error"]["identifier"] = "Development"; + // body_llsd["error"]["message"] = "You left development code in the viewer"; + if (body_llsd.has("error")) + { + processFailure("Inventory application error (200-with-error)", response); + break; // goto common exit + } - // Okay, process data if possible - processData(body_llsd, response); + // Okay, process data if possible + processData(body_llsd, response); + } } + while (false); -only_exit: // Must delete on completion. delete this; } @@ -895,7 +896,6 @@ void BGFolderHttpHandler::processData(LLSD & content, LLCore::HttpResponse * res if (fetcher->isBulkFetchProcessingComplete()) { - LL_INFOS(LOG_INV) << "Inventory fetch completed" << LL_ENDL; fetcher->setAllFoldersFetched(); } diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 2b044c6916..648056484e 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -760,7 +760,7 @@ LLMeshRepoThread::LLMeshRepoThread() mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT); mHttpLargeOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); mHttpHeaders = new LLCore::HttpHeaders; - mHttpHeaders->append("Accept", "application/vnd.ll.mesh"); + mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_VND_LL_MESH); mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH2); mHttpLegacyPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH1); mHttpLargePolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_LARGE_MESH); @@ -1887,7 +1887,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); mHttpOptions->setRetries(UPLOAD_RETRY_LIMIT); mHttpHeaders = new LLCore::HttpHeaders; - mHttpHeaders->append("Content-Type", "application/llsd+xml"); + mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS); mHttpPriority = 0; } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index a955b907d4..acd4cf2d8d 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2514,8 +2514,9 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpOptions(NULL), mHttpOptionsWithHeaders(NULL), mHttpHeaders(NULL), - mHttpMetricsHeaders(NULL), mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpMetricsHeaders(NULL), + mHttpMetricsPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mTotalCacheReadCount(0U), mTotalCacheWriteCount(0U), mTotalResourceWaitCount(0U), @@ -2528,15 +2529,16 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), U32Bytes(gSavedSettings.getU32("TextureLoggingThreshold"))); LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); - mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_TEXTURE); mHttpRequest = new LLCore::HttpRequest; mHttpOptions = new LLCore::HttpOptions; mHttpOptionsWithHeaders = new LLCore::HttpOptions; mHttpOptionsWithHeaders->setWantHeaders(true); mHttpHeaders = new LLCore::HttpHeaders; - mHttpHeaders->append("Accept", "image/x-j2c"); + mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C); + mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_TEXTURE); mHttpMetricsHeaders = new LLCore::HttpHeaders; - mHttpMetricsHeaders->append("Content-Type", "application/llsd+xml"); + mHttpMetricsHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); + mHttpMetricsPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_REPORTING); mHttpHighWater = HTTP_NONPIPE_REQUESTS_HIGH_WATER; mHttpLowWater = HTTP_NONPIPE_REQUESTS_LOW_WATER; mHttpSemaphore = 0; @@ -4037,10 +4039,9 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) if (! mCapsURL.empty()) { - // *TODO: Move this to a different class that expects POSTs sometime. // Don't care about handle, this is a fire-and-forget operation. LLCoreHttpUtil::requestPostWithLLSD(&fetcher->getHttpRequest(), - fetcher->getPolicyClass(), + fetcher->getMetricsPolicyClass(), report_priority, mCapsURL, sd, @@ -4227,7 +4228,7 @@ void LLTextureFetchDebugger::init() if (! mHttpHeaders) { mHttpHeaders = new LLCore::HttpHeaders; - mHttpHeaders->append("Accept", "image/x-j2c"); + mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C); } } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 89d18e2c67..27779a31e0 100755 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -179,6 +179,9 @@ public: // Threads: T* LLCore::HttpHeaders * getMetricsHeaders() const { return mHttpMetricsHeaders; } + // Threads: T* + LLCore::HttpRequest::policy_t getMetricsPolicyClass() const { return mHttpMetricsPolicyClass; } + bool isQAMode() const { return mQAMode; } // ---------------------------------- @@ -354,8 +357,9 @@ private: LLCore::HttpOptions * mHttpOptions; // Ttf LLCore::HttpOptions * mHttpOptionsWithHeaders; // Ttf LLCore::HttpHeaders * mHttpHeaders; // Ttf - LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf LLCore::HttpRequest::policy_t mHttpPolicyClass; // T* + LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf + LLCore::HttpRequest::policy_t mHttpMetricsPolicyClass; // T* S32 mHttpHighWater; // Ttf S32 mHttpLowWater; // Ttf -- cgit v1.3