From 1310630ac60ba292f52761e795eaa55610818b9b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 12 Apr 2013 20:11:17 +0000 Subject: SH-4090 [WIP] Basic deadman timer integration started on Linux. Moving to windows to do real work. --- indra/newview/llmeshrepository.cpp | 45 ++++++++++++++++++++++++++++++++++++-- indra/newview/llmeshrepository.h | 11 ++++++++-- 2 files changed, 52 insertions(+), 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 1223615079..11c5780a30 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -37,6 +37,7 @@ #include "llcallbacklist.h" #include "llcurl.h" #include "lldatapacker.h" +#include "lldeadmantimer.h" #include "llfloatermodelpreview.h" #include "llfloaterperms.h" #include "lleconomy.h" @@ -94,8 +95,9 @@ U32 LLMeshRepository::sLODPending = 0; U32 LLMeshRepository::sCacheBytesRead = 0; U32 LLMeshRepository::sCacheBytesWritten = 0; U32 LLMeshRepository::sPeakKbps = 0; - +LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0); + const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5; static S32 dump_num = 0; @@ -115,7 +117,6 @@ std::string header_lod[] = "high_lod" }; - //get the number of bytes resident in memory for given volume U32 get_volume_memory_size(const LLVolume* volume) { @@ -2678,6 +2679,9 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume) { //called from main thread + // Manage time-to-load metrics for mesh download operations. + metricsCheck(); + S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail()); //get list of objects waiting to be notified this mesh is loaded @@ -3699,3 +3703,40 @@ bool LLMeshRepository::meshRezEnabled() } return false; } + +void LLMeshRepository::metricsStart() +{ + sQuiescentTimer.start(); +} + +void LLMeshRepository::metricsStop() +{ + sQuiescentTimer.stop(); +} + +void LLMeshRepository::metricsCheck() +{ + static bool first_start(true); + F64 started, stopped; + U64 count; + + if (first_start) + { + // Let the first request start the timing cycle for login. + metricsStart(); + first_start = false; + } + sQuiescentTimer.ringBell(); + if (sQuiescentTimer.isExpired(started, stopped, count)) + { + LLSD metrics; + + metrics["reason"] = "Mesh download quiescent"; + metrics["scope"] = "Login"; + metrics["start"] = started; + metrics["stop"] = stopped; + metrics["downloads"] = LLSD::Integer(count); + llinfos << "MetricsMarker" << metrics << llendl; + } +} + diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 8602271f84..f08feedf81 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -32,6 +32,7 @@ #include "lluuid.h" #include "llviewertexture.h" #include "llvolume.h" +#include "lldeadmantimer.h" #define LLCONVEXDECOMPINTER_STATIC 1 @@ -455,14 +456,15 @@ public: static U32 sCacheBytesRead; static U32 sCacheBytesWritten; static U32 sPeakKbps; - + static LLDeadmanTimer sQuiescentTimer; // time-to-complete-mesh-downloads after significant events + static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL); LLMeshRepository(); void init(); void shutdown(); - S32 update() ; + S32 update(); //mesh management functions S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1); @@ -495,6 +497,11 @@ public: S32 getMeshSize(const LLUUID& mesh_id, S32 lod); + // Quiescent timer management, main thread only. + static void metricsStart(); + static void metricsStop(); + static void metricsCheck(); + typedef std::map > mesh_load_map; mesh_load_map mLoadingMeshes[4]; -- cgit v1.2.3 From 2df0a2494691ebfdd305e6a5ee280dd758a1b337 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 7 May 2013 16:18:31 +0000 Subject: SH-4139 Convert http downloaders and responders to llcorehttp patterns Initial work completed on linux, moving over to windows to do debug and refinement. This includes 5/6 handlers based on existing responders and use of llcorehttp for the mesh header fetch. --- indra/newview/llappcorehttp.cpp | 48 ++- indra/newview/llappcorehttp.h | 16 +- indra/newview/llmeshrepository.cpp | 854 ++++++++++++++++++++++++++++++++++++- indra/newview/llmeshrepository.h | 16 +- 4 files changed, 915 insertions(+), 19 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 0d7d41304d..4386e3283b 100644 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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,7 +38,9 @@ LLAppCoreHttp::LLAppCoreHttp() mStopHandle(LLCORE_HTTP_HANDLE_INVALID), mStopRequested(0.0), mStopped(false), - mPolicyDefault(-1) + mPolicyDefault(-1), + mPolicyTexture(-1), + mPolicyMesh(-1) {} @@ -95,6 +97,9 @@ void LLAppCoreHttp::init() // Setup default policy and constrain if directed to mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID; + + // Texture policy will use default for now. + mPolicyTexture = mPolicyDefault; static const std::string texture_concur("TextureFetchConcurrency"); if (gSavedSettings.controlExists(texture_concur)) { @@ -103,7 +108,7 @@ void LLAppCoreHttp::init() if (concur > 0) { LLCore::HttpStatus status; - status = LLCore::HttpRequest::setPolicyClassOption(mPolicyDefault, + status = LLCore::HttpRequest::setPolicyClassOption(mPolicyTexture, LLCore::HttpRequest::CP_CONNECTION_LIMIT, concur); if (! status) @@ -120,6 +125,43 @@ void LLAppCoreHttp::init() } } } + + // Create the mesh class + mPolicyMesh = LLCore::HttpRequest::createPolicyClass(); + if (! mPolicyMesh) + { + LL_WARNS("Init") << "Failed to create HTTP policy class for Mesh. Using default policy." + << LL_ENDL; + mPolicyMesh = mPolicyDefault; + } + else + { + static const std::string mesh_concur("MeshMaxConcurrentRequests"); + if (gSavedSettings.controlExists(mesh_concur)) + { + U32 setting(llmin(gSavedSettings.getU32(mesh_concur), U32(32))); + + if (setting > 0) + { + LLCore::HttpStatus status; + status = LLCore::HttpRequest::setPolicyClassOption(mPolicyMesh, + LLCore::HttpRequest::CP_CONNECTION_LIMIT, + setting); + if (! status) + { + LL_WARNS("Init") << "Unable to set mesh fetch concurrency. Reason: " + << status.toString() + << LL_ENDL; + } + else + { + LL_INFOS("Init") << "Application settings overriding default mesh fetch concurrency. New value: " + << setting + << LL_ENDL; + } + } + } + } // Kick the thread status = LLCore::HttpRequest::startThread(); diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h index 241d73ad52..d90af9e5ca 100644 --- a/indra/newview/llappcorehttp.h +++ b/indra/newview/llappcorehttp.h @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -70,6 +70,18 @@ public: { return mPolicyDefault; } + + // Get the texture fetch policy class. + int getPolicyTexture() const + { + return mPolicyTexture; + } + + // Get the mesh fetch policy class. + int getPolicyMesh() const + { + return mPolicyMesh; + } private: static const F64 MAX_THREAD_WAIT_TIME; @@ -80,6 +92,8 @@ private: F64 mStopRequested; bool mStopped; int mPolicyDefault; + int mPolicyTexture; + int mPolicyMesh; }; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 0f33128057..21b7b120f6 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -66,6 +66,7 @@ #include "llfoldertype.h" #include "llviewerparcelmgr.h" #include "lluploadfloaterobservers.h" +#include "bufferarray.h" #include "boost/lexical_cast.hpp" @@ -77,6 +78,7 @@ LLMeshRepository gMeshRepo; +const S32 MESH_HEADER_SIZE = 4096; const U32 MAX_MESH_REQUESTS_PER_SECOND = 100; // Maximum mesh version to support. Three least significant digits are reserved for the minor version, @@ -124,6 +126,7 @@ static bool metrics_inited(false); static boost::signals2::connection metrics_teleport_connection; static unsigned int metrics_teleport_start_count(0); static void metrics_teleport_started(); +static bool is_retryable(LLCore::HttpStatus status); //get the number of bytes resident in memory for given volume U32 get_volume_memory_size(const LLVolume* volume) @@ -209,6 +212,160 @@ S32 LLMeshRepoThread::sActiveHeaderRequests = 0; S32 LLMeshRepoThread::sActiveLODRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; +class LLMeshHandlerBase : public LLCore::HttpHandler +{ +public: + LLMeshHandlerBase() + : LLCore::HttpHandler(), + mMeshParams(), + mProcessed(false) + {} + virtual ~LLMeshHandlerBase(); + +protected: + LLMeshHandlerBase(const LLMeshHandlerBase &); // Not defined + void operator=(const LLMeshHandlerBase &); // Not defined + +public: + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size) = 0; + virtual void processFailure(LLCore::HttpStatus status) = 0; + +public: + LLVolumeParams mMeshParams; + bool mProcessed; + LLCore::HttpHandle mHttpHandle; +}; + + +class LLMeshHeaderHandler : public LLMeshHandlerBase +{ +public: + LLMeshHeaderHandler(const LLVolumeParams & mesh_params) + : LLMeshHandlerBase() + { + mMeshParams = mesh_params; + LLMeshRepoThread::incActiveHeaderRequests(); + } + virtual ~LLMeshHeaderHandler(); + +protected: + LLMeshHeaderHandler(const LLMeshHeaderHandler &); // Not defined + void operator=(const LLMeshHeaderHandler &); // Not defined + +public: + virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); + virtual void processFailure(LLCore::HttpStatus status); +}; + + +class LLMeshLODHandler : public LLMeshHandlerBase +{ +public: + LLMeshLODHandler(const LLVolumeParams & mesh_params, S32 lod, U32 offset, U32 requested_bytes) + : LLMeshHandlerBase(), + mLOD(lod), + mRequestedBytes(requested_bytes), + mOffset(offset) + { + mMeshParams = mesh_params; + LLMeshRepoThread::incActiveLODRequests(); + } + virtual ~LLMeshLODHandler(); + +protected: + LLMeshLODHandler(const LLMeshLODHandler &); // Not defined + void operator=(const LLMeshLODHandler &); // Not defined + +public: + virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); + virtual void processFailure(LLCore::HttpStatus status); + +public: + S32 mLOD; + U32 mRequestedBytes; + U32 mOffset; +}; + + +class LLMeshSkinInfoHandler : public LLMeshHandlerBase +{ +public: + LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 size) + : LLMeshHandlerBase(), + mMeshID(id), + mRequestedBytes(size), + mOffset(offset) + {} + virtual ~LLMeshSkinInfoHandler(); + +protected: + LLMeshSkinInfoHandler(const LLMeshSkinInfoHandler &); // Not defined + void operator=(const LLMeshSkinInfoHandler &); // Not defined + +public: + virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); + virtual void processFailure(LLCore::HttpStatus status); + +public: + LLUUID mMeshID; + U32 mRequestedBytes; + U32 mOffset; +}; + + +class LLMeshDecompositionHandler : public LLMeshHandlerBase +{ +public: + LLMeshDecompositionHandler(const LLUUID& id, U32 offset, U32 size) + : LLMeshHandlerBase(), + mMeshID(id), + mRequestedBytes(size), + mOffset(offset) + {} + virtual ~LLMeshDecompositionHandler(); + +protected: + LLMeshDecompositionHandler(const LLMeshDecompositionHandler &); // Not defined + void operator=(const LLMeshDecompositionHandler &); // Not defined + +public: + virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); + virtual void processFailure(LLCore::HttpStatus status); + +public: + LLUUID mMeshID; + U32 mRequestedBytes; + U32 mOffset; +}; + + +class LLMeshPhysicsShapeHandler : public LLMeshHandlerBase +{ +public: + LLMeshPhysicsShapeHandler(const LLUUID& id, U32 offset, U32 size) + : LLMeshHandlerBase(), + mMeshID(id), + mRequestedBytes(size), + mOffset(offset) + {} + virtual ~LLMeshPhysicsShapeHandler(); + +protected: + LLMeshPhysicsShapeHandler(const LLMeshPhysicsShapeHandler &); // Not defined + void operator=(const LLMeshPhysicsShapeHandler &); // Not defined + +public: + virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); + virtual void processFailure(LLCore::HttpStatus status); + +public: + LLUUID mMeshID; + U32 mRequestedBytes; + U32 mOffset; +}; + + class LLMeshHeaderResponder : public LLCurl::Responder { public: @@ -538,16 +695,45 @@ public: }; LLMeshRepoThread::LLMeshRepoThread() -: LLThread("mesh repo") +: LLThread("mesh repo"), + mCurlRequest(NULL), + mWaiting(false), + mHttpRequest(NULL), + mHttpOptions(NULL), + mHttpHeaders(NULL), + mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID) { - mWaiting = false; mMutex = new LLMutex(NULL); mHeaderMutex = new LLMutex(NULL); mSignal = new LLCondition(NULL); + mHttpRequest = new LLCore::HttpRequest; + mHttpOptions = new LLCore::HttpOptions; + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->mHeaders.push_back("Accept: application/vnd.ll.mesh"); + mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyMesh(); } LLMeshRepoThread::~LLMeshRepoThread() { + for (http_request_set::iterator iter(mHttpRequestSet.begin()); + iter != mHttpRequestSet.end(); + ++iter) + { + delete *iter; + } + mHttpRequestSet.clear(); + if (mHttpHeaders) + { + mHttpHeaders->release(); + mHttpHeaders = NULL; + } + if (mHttpOptions) + { + mHttpOptions->release(); + mHttpOptions = NULL; + } + delete mHttpRequest; + mHttpRequest = NULL; delete mMutex; mMutex = NULL; delete mHeaderMutex; @@ -571,7 +757,7 @@ void LLMeshRepoThread::run() mSignal->wait(); mWaiting = false; - if (!LLApp::isQuitting()) + if (! LLApp::isQuitting() && ! mHttpRequestSet.empty()) { static U32 count = 0; @@ -660,6 +846,7 @@ void LLMeshRepoThread::run() } mCurlRequest->process(); + mHttpRequest->update(0L); } } @@ -1045,13 +1232,15 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c S32 size = file.getSize(); if (size > 0) - { //NOTE -- if the header size is ever more than 4KB, this will break - U8 buffer[4096]; - S32 bytes = llmin(size, 4096); + { + // *NOTE: if the header size is ever more than 4KB, this will break + U8 buffer[MESH_HEADER_SIZE]; + S32 bytes = llmin(size, MESH_HEADER_SIZE); LLMeshRepository::sCacheBytesRead += bytes; file.read(buffer, bytes); if (headerReceived(mesh_params, buffer, bytes)) - { //did not do an HTTP request, return false + { + // Found mesh in VFS cache return true; } } @@ -1068,7 +1257,31 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits //within the first 4KB //NOTE -- this will break of headers ever exceed 4KB - retval = mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params)); +#if 0 + retval = mCurlRequest->getByteRange(http_url, headers, 0, MESH_HEADER_SIZE, new LLMeshHeaderResponder(mesh_params)); +#else + LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params); + LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, + 0, // *TODO: Get better priority value + http_url, + 0, + MESH_HEADER_SIZE, + mHttpOptions, + mHttpHeaders, + handler); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + // *TODO: Better error message + llwarns << "HTTP GET request failed for mesh " << mID << llendl; + delete handler; + retval = false; + } + else + { + handler->mHttpHandle = handle; + mHttpRequestSet.insert(handler); + } +#endif if(retval) { LLMeshRepository::sHTTPRequestCount++; @@ -1209,7 +1422,7 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat LLMutexLock lock(mHeaderMutex); mMeshHeaderSize[mesh_id] = header_size; mMeshHeader[mesh_id] = header; - } + } LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time. @@ -2162,6 +2375,336 @@ void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& re delete [] data; } + +LLMeshHandlerBase::~LLMeshHandlerBase() +{} + + +void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + mProcessed = true; + + LLCore::HttpStatus status(response->getStatus()); + if (! status) + { + processFailure(status); + } + else + { + // From texture fetch code and applies here: + // + // A warning about partial (HTTP 206) data. Some grid services + // do *not* return a 'Content-Range' header in the response to + // Range requests with a 206 status. We're forced to assume + // we get what we asked for in these cases until we can fix + // the services. + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + + LLCore::BufferArray * body(response->getBody()); + S32 data_size(body ? body->size() : 0); + U8 * data(NULL); + + if (data_size > 0) + { + // *TODO: Try to get rid of data copying and add interfaces + // that support BufferArray directly. + data = new U8[data_size]; + body->read(0, (char *) data, data_size); + LLMeshRepository::sBytesReceived += llmin(data_size, MESH_HEADER_SIZE); + } + + processData(body, data, data_size); + + delete [] data; + } + + // Release handler + gMeshRepo.mThread->mHttpRequestSet.erase(this); + + delete this; // Must be last statement +} + + +LLMeshHeaderHandler::~LLMeshHeaderHandler() +{ + if (!LLApp::isQuitting()) + { + if (! mProcessed) + { + // something went wrong, retry + llwarns << "Timeout or service unavailable, retrying." << llendl; + LLMeshRepository::sHTTPRetryCount++; + LLMeshRepoThread::HeaderRequest req(mMeshParams); + LLMutexLock lock(gMeshRepo.mThread->mMutex); + gMeshRepo.mThread->mHeaderReqQ.push(req); + } + LLMeshRepoThread::decActiveHeaderRequests(); + } +} + + +void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) +{ + if (is_retryable(status)) + { + llwarns << "Timeout or service unavailable, retrying." << llendl; + LLMeshRepository::sHTTPRetryCount++; + LLMeshRepoThread::HeaderRequest req(mMeshParams); + LLMutexLock lock(gMeshRepo.mThread->mMutex); + gMeshRepo.mThread->mHeaderReqQ.push(req); + } + else + { + // *TODO: better error message + llwarns << "Unhandled status." << llendl; + } +} + + +void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +{ + bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); + llassert(success); + if (! success) + { + // *TODO: Get real reason for parse failure here + llwarns << "Unable to parse mesh header: " << llendl; + } + else if (data && data_size > 0) + { + // header was successfully retrieved from sim, cache in vfs + LLUUID mesh_id = mMeshParams.getSculptID(); + LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id]; + + S32 version = header["version"].asInteger(); + + if (version <= MAX_MESH_VERSION) + { + std::stringstream str; + + 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 + 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()); + lod_bytes = llmax(lod_bytes, header["physics_convex"]["offset"].asInteger() + header["physics_convex"]["size"].asInteger()); + + S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id]; + S32 bytes = lod_bytes + header_bytes; + + + // It's possible for the remote asset to have more data than is needed for the local cache + // only allocate as much space in the VFS as is needed for the local cache + data_size = llmin(data_size, bytes); + + LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); + if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) + { + LLMeshRepository::sCacheBytesWritten += data_size; + + file.write(data, data_size); + + // zero out the rest of the file + U8 block[MESH_HEADER_SIZE]; + memset(block, 0, MESH_HEADER_SIZE); + + while (bytes-file.tell() > MESH_HEADER_SIZE) + { + file.write(block, MESH_HEADER_SIZE); + } + + S32 remaining = bytes-file.tell(); + + if (remaining > 0) + { + file.write(block, remaining); + } + } + } + } +} + + +LLMeshLODHandler::~LLMeshLODHandler() +{ + if (! LLApp::isQuitting()) + { + if (! mProcessed) + { + llwarns << "Killed without being processed, retrying." << llendl; + LLMeshRepository::sHTTPRetryCount++; + gMeshRepo.mThread->lockAndLoadMeshLOD(mMeshParams, mLOD); + } + LLMeshRepoThread::decActiveLODRequests(); + } +} + +void LLMeshLODHandler::processFailure(LLCore::HttpStatus status) +{ + if (is_retryable(status)) + { + llwarns << "Timeout or service unavailable, retrying." << llendl; + LLMeshRepository::sHTTPRetryCount++; + // *FIXME: Is this safe? Does this need locking? + gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD); + } + else + { + // *TODO: better error message + llwarns << "Unhandled status." << llendl; + } +} + +void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +{ + if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size)) + { + // good fetch from sim, write to VFS for caching + LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + file.seek(offset); + file.write(data, size); + LLMeshRepository::sCacheBytesWritten += size; + } + } +} + + +LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler() +{ + llassert(mProcessed); +} + +void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) +{ + if (is_retryable(status)) + { + llwarns << "Timeout or service unavailable, retrying." << llendl; + LLMeshRepository::sHTTPRetryCount++; + // *FIXME: Is this safe? Does this need locking? + gMeshRepo.mThread->loadMeshSkinInfo(mMeshID); + } + else + { + // *TODO: better error message + llwarns << "Unhandled status." << llendl; + } +} + +void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +{ + if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) + { + // good fetch from sim, write to VFS for caching + LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesWritten += size; + file.seek(offset); + file.write(data, size); + } + } +} + + +LLMeshDecompositionHandler::~LLMeshDecompositionHandler() +{ + llassert(mProcessed); +} + +void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status) +{ + if (is_retryable(status)) + { + llwarns << "Timeout or service unavailable, retrying." << llendl; + LLMeshRepository::sHTTPRetryCount++; + // *FIXME: Is this safe? Does this need locking? + gMeshRepo.mThread->loadMeshDecomposition(mMeshID); + } + else + { + // *TODO: better error message + llwarns << "Unhandled status." << llendl; + } +} + +void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +{ + if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) + { + // good fetch from sim, write to VFS for caching + LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesWritten += size; + file.seek(offset); + file.write(data, size); + } + } +} + + +LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler() +{ + llassert(mProcessed); +} + +void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status) +{ + if (is_retryable(status)) + { + llwarns << "Timeout or service unavailable, retrying." << llendl; + LLMeshRepository::sHTTPRetryCount++; + // *FIXME: Is this safe? Does this need locking? + gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID); + } + else + { + // *TODO: better error message + llwarns << "Unhandled status." << llendl; + } +} + + +void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +{ + if (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); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesWritten += size; + file.seek(offset); + file.write(data, size); + } + } +} + + void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) @@ -2215,7 +2758,7 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, buffer->readAfter(channels.in(), NULL, data, data_size); } - LLMeshRepository::sBytesReceived += llmin(data_size, 4096); + LLMeshRepository::sBytesReceived += llmin(data_size, MESH_HEADER_SIZE); bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); @@ -2267,12 +2810,12 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, file.write((const U8*) data, data_size); //zero out the rest of the file - U8 block[4096]; - memset(block, 0, 4096); + U8 block[MESH_HEADER_SIZE]; + memset(block, 0, MESH_HEADER_SIZE); - while (bytes-file.tell() > 4096) + while (bytes-file.tell() > MESH_HEADER_SIZE) { - file.write(block, 4096); + file.write(block, MESH_HEADER_SIZE); } S32 remaining = bytes-file.tell(); @@ -3797,3 +4340,286 @@ void metrics_teleport_started() ++metrics_teleport_start_count; } + +// This comes from an edit in viewer-cat. Unify this once that's +// available everywhere. +bool is_retryable(LLCore::HttpStatus status) +{ + static const LLCore::HttpStatus cant_connect(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); + static const LLCore::HttpStatus cant_res_proxy(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); + static const LLCore::HttpStatus cant_res_host(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); + static const LLCore::HttpStatus send_error(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR); + static const LLCore::HttpStatus recv_error(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR); + static const LLCore::HttpStatus upload_failed(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_UPLOAD_FAILED); + static const LLCore::HttpStatus op_timedout(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); + static const LLCore::HttpStatus post_error(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_HTTP_POST_ERROR); + static const LLCore::HttpStatus partial_file(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_PARTIAL_FILE); + static const LLCore::HttpStatus inv_cont_range(LLCore::HttpStatus::LLCORE, LLCore::HE_INV_CONTENT_RANGE_HDR); + + return ((! status) && + ((status.isHttpStatus() && status.mType >= 499 && status.mType <= 599) || // Include special 499 in retryables + status == cant_connect || // Connection reset/endpoint problems + status == cant_res_proxy || // DNS problems + status == cant_res_host || // DNS problems + status == send_error || // General socket problems + status == recv_error || // General socket problems + status == upload_failed || // Transport problem + status == op_timedout || // Timer expired + status == post_error || // Transport problem + status == partial_file || // Data inconsistency in response + status == inv_cont_range)); // Short data read disagrees with content-range +} + + + + +// =========== +// +// HTTP fragments I'll be needing +// +// +#if 0 + mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpRequest = new LLCore::HttpRequest; + mHttpOptions = new LLCore::HttpOptions; + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); + mHttpMetricsHeaders = new LLCore::HttpHeaders; + mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml"); + mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault(); + + + LLCore::HttpHandle mHttpHandle; // Handle of any active request + LLCore::BufferArray * mHttpBufferArray; // Refcounted pointer to response data + int mHttpPolicyClass; + bool mHttpActive; // Active request to http library + unsigned int mHttpReplySize; // Actual received data size + unsigned int mHttpReplyOffset; // Actual received data offset + bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore + + + mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; + if (!mUrl.empty()) + { + mRequestedTimer.reset(); + mLoaded = FALSE; + mGetStatus = LLCore::HttpStatus(); + mGetReason.clear(); + LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset + << " Bytes: " << mRequestedSize + << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth + << LL_ENDL; + + // Will call callbackHttpGet when curl request completes + mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, + mWorkPriority, + mUrl, + mRequestedOffset, + mRequestedSize, + mFetcher->mHttpOptions, + mFetcher->mHttpHeaders, + this); + } + if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) + { + llwarns << "HTTP GET request failed for " << mID << llendl; + resetFormattedData(); + releaseHttpSemaphore(); + return true; // failed + } + + mHttpActive = true; + mFetcher->addToHTTPQueue(mID); + recordTextureStart(true); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mState = WAIT_HTTP_REQ; + + // fall through + } + + + +// Threads: Ttf +// virtual +void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + static LLCachedControl log_to_viewer_log(gSavedSettings, "LogTextureDownloadsToViewerLog"); + static LLCachedControl log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator"); + static LLCachedControl log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic") ; + + LLMutexLock lock(&mWorkMutex); // +Mw + + mHttpActive = false; + + if (log_to_viewer_log || log_to_sim) + { + U64 timeNow = LLTimer::getTotalTime(); + mFetcher->mTextureInfo.setRequestStartTime(mID, mMetricsStartTime); + mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); + mFetcher->mTextureInfo.setRequestOffset(mID, mRequestedOffset); + mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); + } + + bool success = true; + bool partial = false; + LLCore::HttpStatus status(response->getStatus()); + + lldebugs << "HTTP COMPLETE: " << mID + << " status: " << status.toHex() + << " '" << status.toString() << "'" + << llendl; +// unsigned int offset(0), length(0), full_length(0); +// response->getRange(&offset, &length, &full_length); +// llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle +// << " status: " << status.toULong() << " '" << status.toString() << "'" +// << " req offset: " << mRequestedOffset << " req length: " << mRequestedSize +// << " offset: " << offset << " length: " << length +// << llendl; + + if (! status) + { + success = false; + std::string reason(status.toString()); + setGetStatus(status, reason); + llwarns << "CURL GET FAILED, status: " << status.toHex() + << " reason: " << reason << llendl; + } + else + { + // A warning about partial (HTTP 206) data. Some grid services + // do *not* return a 'Content-Range' header in the response to + // Range requests with a 206 status. We're forced to assume + // we get what we asked for in these cases until we can fix + // the services. + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + + partial = (par_status == status); + } + + S32 data_size = callbackHttpGet(response, partial, success); + + if (log_texture_traffic && data_size > 0) + { + LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID); + if (tex) + { + gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; + } + } + + mFetcher->removeFromHTTPQueue(mID, data_size); + + recordTextureDone(true); +} // -Mw + + +// Threads: Ttf +// Locks: Mw +S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, + bool partial, bool success) +{ + S32 data_size = 0 ; + + if (mState != WAIT_HTTP_REQ) + { + llwarns << "callbackHttpGet for unrequested fetch worker: " << mID + << " req=" << mSentRequest << " state= " << mState << llendl; + return data_size; + } + if (mLoaded) + { + llwarns << "Duplicate callback for " << mID.asString() << llendl; + return data_size ; // ignore duplicate callback + } + if (success) + { + // get length of stream: + LLCore::BufferArray * body(response->getBody()); + data_size = body ? body->size() : 0; + + LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL; + if (data_size > 0) + { + LLViewerStatsRecorder::instance().textureFetch(data_size); + // *TODO: set the formatted image data here directly to avoid the copy + + // Hold on to body for later copy + llassert_always(NULL == mHttpBufferArray); + body->addRef(); + mHttpBufferArray = body; + + if (partial) + { + unsigned int offset(0), length(0), full_length(0); + 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... + mHttpReplySize = data_size; + mHttpReplyOffset = mRequestedOffset; + } + else + { + mHttpReplySize = length; + mHttpReplyOffset = offset; + } + } + + if (! partial) + { + // Response indicates this is the entire asset regardless + // of our asking for a byte range. Mark it so and drop + // any partial data we might have so that the current + // response body becomes the entire dataset. + if (data_size <= mRequestedOffset) + { + LL_WARNS("Texture") << "Fetched entire texture " << mID + << " when it was expected to be marked complete. mImageSize: " + << mFileSize << " datasize: " << mFormattedImage->getDataSize() + << LL_ENDL; + } + mHaveAllData = TRUE; + llassert_always(mDecodeHandle == 0); + mFormattedImage = NULL; // discard any previous data we had + } + else if (data_size < mRequestedSize) + { + mHaveAllData = TRUE; + } + else if (data_size > mRequestedSize) + { + // *TODO: This shouldn't be happening any more (REALLY don't expect this anymore) + llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl; + mHaveAllData = TRUE; + llassert_always(mDecodeHandle == 0); + mFormattedImage = NULL; // discard any previous data we had + } + } + else + { + // We requested data but received none (and no error), + // so presumably we have all of it + mHaveAllData = TRUE; + } + mRequestedSize = data_size; + } + else + { + mRequestedSize = -1; // error + } + + mLoaded = TRUE; + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + + LLViewerStatsRecorder::instance().log(0.2f); + return data_size ; +} + +#endif +// ============ + diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 3cdc66e1f0..8ffe2d68cb 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2010-2013, 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 @@ -33,6 +33,11 @@ #include "llviewertexture.h" #include "llvolume.h" #include "lldeadmantimer.h" +#include "httpcommon.h" +#include "httprequest.h" +#include "httpoptions.h" +#include "httpheaders.h" +#include "httphandler.h" #define LLCONVEXDECOMPINTER_STATIC 1 @@ -316,6 +321,15 @@ public: typedef std::map > pending_lod_map; pending_lod_map mPendingLOD; + // llcorehttp library interface objects. + LLCore::HttpRequest * mHttpRequest; + LLCore::HttpOptions * mHttpOptions; + LLCore::HttpHeaders * mHttpHeaders; + LLCore::HttpRequest::policy_t mHttpPolicyClass; + + typedef std::set http_request_set; + http_request_set mHttpRequestSet; // Outstanding HTTP requests + static std::string constructUrl(LLUUID mesh_id); LLMeshRepoThread(); -- cgit v1.2.3 From aa855b18ecde2786c758ac27d2ddb049d0929d55 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 7 May 2013 17:24:12 -0400 Subject: SH-4139 Convert http downloaders and responders to llcorehttp patterns First version running with all five downloaders converted. Not certain all are functional yet and the whole thing is slow but it is running. --- indra/newview/llmeshrepository.cpp | 247 ++++++++++++++++++------------------- 1 file changed, 123 insertions(+), 124 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 21b7b120f6..419613091a 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -753,11 +753,19 @@ void LLMeshRepoThread::run() while (!LLApp::isQuitting()) { - mWaiting = true; - mSignal->wait(); - mWaiting = false; - - if (! LLApp::isQuitting() && ! mHttpRequestSet.empty()) + if (! mHttpRequestSet.empty()) + { + mHttpRequest->update(0L); + ms_sleep(100); + } + else + { + mWaiting = true; + mSignal->wait(); + mWaiting = false; + } + + if (! LLApp::isQuitting()) { static U32 count = 0; @@ -846,7 +854,6 @@ void LLMeshRepoThread::run() } mCurlRequest->process(); - mHttpRequest->update(0L); } } @@ -1007,10 +1014,35 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) - { + { +#if 0 ret = mCurlRequest->getByteRange(http_url, headers, offset, size, new LLMeshSkinInfoResponder(mesh_id, offset, size)); - if(ret) +#else + LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size); + // LL_WARNS("Mesh") << "MESH: Issuing Skin Info Request" << LL_ENDL; + LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, + 0, // *TODO: Get better priority value + http_url, + offset, + size, + mHttpOptions, + mHttpHeaders, + handler); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + // *TODO: Better error message + llwarns << "HTTP GET request failed for mesh " << mID << llendl; + delete handler; + ret = false; + } + else + { + handler->mHttpHandle = handle; + mHttpRequestSet.insert(handler); + } +#endif + if (ret) { LLMeshRepository::sHTTPRequestCount++; } @@ -1089,10 +1121,35 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) - { + { +#if 0 ret = mCurlRequest->getByteRange(http_url, headers, offset, size, new LLMeshDecompositionResponder(mesh_id, offset, size)); - if(ret) +#else + LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size); + // LL_WARNS("Mesh") << "MESH: Issuing Decomp Request" << LL_ENDL; + LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, + 0, // *TODO: Get better priority value + http_url, + offset, + size, + mHttpOptions, + mHttpHeaders, + handler); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + // *TODO: Better error message + llwarns << "HTTP GET request failed for decomposition mesh " << mID << llendl; + delete handler; + ret = false; + } + else + { + handler->mHttpHandle = handle; + mHttpRequestSet.insert(handler); + } +#endif + if (ret) { LLMeshRepository::sHTTPRequestCount++; } @@ -1170,11 +1227,35 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) - { + { +#if 0 ret = mCurlRequest->getByteRange(http_url, headers, offset, size, new LLMeshPhysicsShapeResponder(mesh_id, offset, size)); - - if(ret) +#else + LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size); + // LL_WARNS("Mesh") << "MESH: Issuing Physics Shape Request" << LL_ENDL; + LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, + 0, // *TODO: Get better priority value + http_url, + offset, + size, + mHttpOptions, + mHttpHeaders, + handler); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + // *TODO: Better error message + llwarns << "HTTP GET request failed for physics shape mesh " << mID << llendl; + delete handler; + ret = false; + } + else + { + handler->mHttpHandle = handle; + mHttpRequestSet.insert(handler); + } +#endif + if (ret) { LLMeshRepository::sHTTPRequestCount++; } @@ -1261,6 +1342,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c retval = mCurlRequest->getByteRange(http_url, headers, 0, MESH_HEADER_SIZE, new LLMeshHeaderResponder(mesh_params)); #else LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params); + // LL_WARNS("Mesh") << "MESH: Issuing Request" << LL_ENDL; LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, 0, // *TODO: Get better priority value http_url, @@ -1352,10 +1434,34 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) - { + { +#if 0 retval = mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size, new LLMeshLODResponder(mesh_params, lod, offset, size)); - +#else + LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size); + // LL_WARNS("Mesh") << "MESH: Issuing LOD Request" << LL_ENDL; + LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, + 0, // *TODO: Get better priority value + http_url, + offset, + size, + mHttpOptions, + mHttpHeaders, + handler); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + // *TODO: Better error message + llwarns << "HTTP GET request failed for LOD mesh " << mID << llendl; + delete handler; + retval = false; + } + else + { + handler->mHttpHandle = handle; + mHttpRequestSet.insert(handler); + } +#endif if(retval) { LLMeshRepository::sHTTPRequestCount++; @@ -2445,6 +2551,7 @@ LLMeshHeaderHandler::~LLMeshHeaderHandler() void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) { + LL_WARNS("Mesh") << "MESH: Processing Failure" << LL_ENDL; if (is_retryable(status)) { llwarns << "Timeout or service unavailable, retrying." << llendl; @@ -2463,6 +2570,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) { + // LL_WARNS("Mesh") << "MESH: Processing Data" << LL_ENDL; bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); llassert(success); if (! success) @@ -4379,103 +4487,6 @@ bool is_retryable(LLCore::HttpStatus status) // // #if 0 - mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), - mHttpRequest = new LLCore::HttpRequest; - mHttpOptions = new LLCore::HttpOptions; - mHttpHeaders = new LLCore::HttpHeaders; - mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); - mHttpMetricsHeaders = new LLCore::HttpHeaders; - mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml"); - mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault(); - - - LLCore::HttpHandle mHttpHandle; // Handle of any active request - LLCore::BufferArray * mHttpBufferArray; // Refcounted pointer to response data - int mHttpPolicyClass; - bool mHttpActive; // Active request to http library - unsigned int mHttpReplySize; // Actual received data size - unsigned int mHttpReplyOffset; // Actual received data offset - bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore - - - mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; - if (!mUrl.empty()) - { - mRequestedTimer.reset(); - mLoaded = FALSE; - mGetStatus = LLCore::HttpStatus(); - mGetReason.clear(); - LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset - << " Bytes: " << mRequestedSize - << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth - << LL_ENDL; - - // Will call callbackHttpGet when curl request completes - mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, - mWorkPriority, - mUrl, - mRequestedOffset, - mRequestedSize, - mFetcher->mHttpOptions, - mFetcher->mHttpHeaders, - this); - } - if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) - { - llwarns << "HTTP GET request failed for " << mID << llendl; - resetFormattedData(); - releaseHttpSemaphore(); - return true; // failed - } - - mHttpActive = true; - mFetcher->addToHTTPQueue(mID); - recordTextureStart(true); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - mState = WAIT_HTTP_REQ; - - // fall through - } - - - -// Threads: Ttf -// virtual -void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) -{ - static LLCachedControl log_to_viewer_log(gSavedSettings, "LogTextureDownloadsToViewerLog"); - static LLCachedControl log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator"); - static LLCachedControl log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic") ; - - LLMutexLock lock(&mWorkMutex); // +Mw - - mHttpActive = false; - - if (log_to_viewer_log || log_to_sim) - { - U64 timeNow = LLTimer::getTotalTime(); - mFetcher->mTextureInfo.setRequestStartTime(mID, mMetricsStartTime); - mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); - mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); - mFetcher->mTextureInfo.setRequestOffset(mID, mRequestedOffset); - mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); - } - - bool success = true; - bool partial = false; - LLCore::HttpStatus status(response->getStatus()); - - lldebugs << "HTTP COMPLETE: " << mID - << " status: " << status.toHex() - << " '" << status.toString() << "'" - << llendl; -// unsigned int offset(0), length(0), full_length(0); -// response->getRange(&offset, &length, &full_length); -// llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle -// << " status: " << status.toULong() << " '" << status.toString() << "'" -// << " req offset: " << mRequestedOffset << " req length: " << mRequestedSize -// << " offset: " << offset << " length: " << length -// << llendl; if (! status) { @@ -4499,19 +4510,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe S32 data_size = callbackHttpGet(response, partial, success); - if (log_texture_traffic && data_size > 0) - { - LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID); - if (tex) - { - gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; - } - } - mFetcher->removeFromHTTPQueue(mID, data_size); - - recordTextureDone(true); -} // -Mw // Threads: Ttf -- cgit v1.2.3 From e3db003cbff0faa44d29e35139601b9778acfbca Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 8 May 2013 13:48:14 -0400 Subject: SH-4139 Convert http downloaders and responders to llcorehttp patterns Conversion was mostly trivial. Did some refactoring in the conversion of Responders to Handlers which eliminated 5X code replication. More will be done especially as this is extended to deal with the various possible combinations of 200/206/416 status for ranged gets. There are a lot of thread races in the existing code, that is going to need some real attention. And the scheduling/liveness logic in the thread management bounces around from thread to thread wasting a lot of time and using expensive synchronization. Much can be done here. But the result is that the 8 connections in the Mesh corehttp class now perform as did the 32 connections of the original. And that 32 actually looks like it could bleed to over 64. So, progress... --- indra/newview/llmeshrepository.cpp | 54 +++++--------------------------------- 1 file changed, 6 insertions(+), 48 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 2d4692be5b..7a7cc72711 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -756,25 +756,21 @@ void LLMeshRepoThread::run() if (! mHttpRequestSet.empty()) { mHttpRequest->update(0L); - ms_sleep(100); - } - else - { - mWaiting = true; - mSignal->wait(); - mWaiting = false; } + + mWaiting = true; + mSignal->wait(); + mWaiting = false; if (! LLApp::isQuitting()) { static U32 count = 0; - static F32 last_hundred = gFrameTimeSeconds; if (gFrameTimeSeconds - last_hundred > 1.f) { //a second has gone by, clear count last_hundred = gFrameTimeSeconds; - count = 0; + count = 0; } // NOTE: throttling intentionally favors LOD requests over header requests @@ -1015,10 +1011,6 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { -#if 0 - ret = mCurlRequest->getByteRange(http_url, headers, offset, size, - new LLMeshSkinInfoResponder(mesh_id, offset, size)); -#else LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size); // LL_WARNS("Mesh") << "MESH: Issuing Skin Info Request" << LL_ENDL; LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, @@ -1040,10 +1032,6 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - } -#endif - if (ret) - { LLMeshRepository::sHTTPRequestCount++; } } @@ -1122,10 +1110,6 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { -#if 0 - ret = mCurlRequest->getByteRange(http_url, headers, offset, size, - new LLMeshDecompositionResponder(mesh_id, offset, size)); -#else LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size); // LL_WARNS("Mesh") << "MESH: Issuing Decomp Request" << LL_ENDL; LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, @@ -1147,10 +1131,6 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - } -#endif - if (ret) - { LLMeshRepository::sHTTPRequestCount++; } } @@ -1228,10 +1208,6 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { -#if 0 - ret = mCurlRequest->getByteRange(http_url, headers, offset, size, - new LLMeshPhysicsShapeResponder(mesh_id, offset, size)); -#else LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size); // LL_WARNS("Mesh") << "MESH: Issuing Physics Shape Request" << LL_ENDL; LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, @@ -1253,10 +1229,6 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - } -#endif - if (ret) - { LLMeshRepository::sHTTPRequestCount++; } } @@ -1338,9 +1310,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits //within the first 4KB //NOTE -- this will break of headers ever exceed 4KB -#if 0 - retval = mCurlRequest->getByteRange(http_url, headers, 0, MESH_HEADER_SIZE, new LLMeshHeaderResponder(mesh_params)); -#else + LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params); // LL_WARNS("Mesh") << "MESH: Issuing Request" << LL_ENDL; LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, @@ -1362,10 +1332,6 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - } -#endif - if(retval) - { LLMeshRepository::sHTTPRequestCount++; } count++; @@ -1435,10 +1401,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { -#if 0 - retval = mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size, - new LLMeshLODResponder(mesh_params, lod, offset, size)); -#else LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size); // LL_WARNS("Mesh") << "MESH: Issuing LOD Request" << LL_ENDL; LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, @@ -1460,10 +1422,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - } -#endif - if(retval) - { LLMeshRepository::sHTTPRequestCount++; } count++; -- cgit v1.2.3 From f55f4bf1b25d14bcd3d956e6c170ece3880ad5f5 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 8 May 2013 18:37:08 -0400 Subject: SH-4163 Run an initial series of 'B' tests on the mesh downloader code Bleh, had some old initialization code in place that meant I was using 32 connections. (Always verify with 'netstat'...) Logic is now to use 1/4 of MeshMaxConncurrentRequests to head in the direction of 8 at a time. Full count is used to implement a high-water level keeping llcorehttp in work. --- indra/newview/llappcorehttp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 4386e3283b..142344e277 100644 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -139,7 +139,8 @@ void LLAppCoreHttp::init() static const std::string mesh_concur("MeshMaxConcurrentRequests"); if (gSavedSettings.controlExists(mesh_concur)) { - U32 setting(llmin(gSavedSettings.getU32(mesh_concur), U32(32))); + U32 setting(llmin(gSavedSettings.getU32(mesh_concur), 256U) / 4U); + setting = llmax(setting, 2U); if (setting > 0) { -- cgit v1.2.3 From 211d1dfb770aa029d77cd231815a5848640b54a6 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 3 Jun 2013 13:54:15 -0400 Subject: SH-4184 Conversion to llcorehttp. Remove unneeded responder classes (moved to Handlers). --- indra/newview/llmeshrepository.cpp | 559 +------------------------------------ 1 file changed, 2 insertions(+), 557 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 7a7cc72711..f1def284df 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -218,7 +218,8 @@ public: LLMeshHandlerBase() : LLCore::HttpHandler(), mMeshParams(), - mProcessed(false) + mProcessed(false), + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID) {} virtual ~LLMeshHandlerBase(); @@ -365,154 +366,6 @@ public: U32 mOffset; }; - -class LLMeshHeaderResponder : public LLCurl::Responder -{ -public: - LLVolumeParams mMeshParams; - bool mProcessed; - - LLMeshHeaderResponder(const LLVolumeParams& mesh_params) - : mMeshParams(mesh_params) - { - LLMeshRepoThread::incActiveHeaderRequests(); - mProcessed = false; - } - - ~LLMeshHeaderResponder() - { - if (!LLApp::isQuitting()) - { - if (!mProcessed) - { //something went wrong, retry - llwarns << "Timeout or service unavailable, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; - LLMeshRepoThread::HeaderRequest req(mMeshParams); - LLMutexLock lock(gMeshRepo.mThread->mMutex); - gMeshRepo.mThread->mHeaderReqQ.push(req); - } - - LLMeshRepoThread::decActiveHeaderRequests(); - } - } - - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - -}; - -class LLMeshLODResponder : public LLCurl::Responder -{ -public: - LLVolumeParams mMeshParams; - S32 mLOD; - U32 mRequestedBytes; - U32 mOffset; - bool mProcessed; - - LLMeshLODResponder(const LLVolumeParams& mesh_params, S32 lod, U32 offset, U32 requested_bytes) - : mMeshParams(mesh_params), mLOD(lod), mOffset(offset), mRequestedBytes(requested_bytes) - { - LLMeshRepoThread::incActiveLODRequests(); - mProcessed = false; - } - - ~LLMeshLODResponder() - { - if (!LLApp::isQuitting()) - { - if (!mProcessed) - { - llwarns << "Killed without being processed, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; - gMeshRepo.mThread->lockAndLoadMeshLOD(mMeshParams, mLOD); - } - LLMeshRepoThread::decActiveLODRequests(); - } - } - - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - -}; - -class LLMeshSkinInfoResponder : public LLCurl::Responder -{ -public: - LLUUID mMeshID; - U32 mRequestedBytes; - U32 mOffset; - bool mProcessed; - - LLMeshSkinInfoResponder(const LLUUID& id, U32 offset, U32 size) - : mMeshID(id), mRequestedBytes(size), mOffset(offset) - { - mProcessed = false; - } - - ~LLMeshSkinInfoResponder() - { - llassert(mProcessed); - } - - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - -}; - -class LLMeshDecompositionResponder : public LLCurl::Responder -{ -public: - LLUUID mMeshID; - U32 mRequestedBytes; - U32 mOffset; - bool mProcessed; - - LLMeshDecompositionResponder(const LLUUID& id, U32 offset, U32 size) - : mMeshID(id), mRequestedBytes(size), mOffset(offset) - { - mProcessed = false; - } - - ~LLMeshDecompositionResponder() - { - llassert(mProcessed); - } - - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - -}; - -class LLMeshPhysicsShapeResponder : public LLCurl::Responder -{ -public: - LLUUID mMeshID; - U32 mRequestedBytes; - U32 mOffset; - bool mProcessed; - - LLMeshPhysicsShapeResponder(const LLUUID& id, U32 offset, U32 size) - : mMeshID(id), mRequestedBytes(size), mOffset(offset) - { - mProcessed = false; - } - - ~LLMeshPhysicsShapeResponder() - { - llassert(mProcessed); - } - - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - -}; - void log_upload_error(S32 status, const LLSD& content, std::string stage, std::string model_name) { // Add notification popup. @@ -1005,9 +858,6 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - std::vector headers; - headers.push_back("Accept: application/octet-stream"); - std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { @@ -1104,9 +954,6 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - std::vector headers; - headers.push_back("Accept: application/octet-stream"); - std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { @@ -1202,9 +1049,6 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - std::vector headers; - headers.push_back("Accept: application/octet-stream"); - std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { @@ -1301,9 +1145,6 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c //either cache entry doesn't exist or is corrupt, request header from simulator bool retval = true ; - std::vector headers; - headers.push_back("Accept: application/octet-stream"); - std::string http_url = constructUrl(mesh_params.getSculptID()); if (!http_url.empty()) { @@ -1395,9 +1236,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, } //reading from VFS failed for whatever reason, fetch from sim - std::vector headers; - headers.push_back("Accept: application/octet-stream"); - std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { @@ -2184,270 +2022,12 @@ void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header) } -void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) -{ - mProcessed = true; - - // thread could have already be destroyed during logout - if( !gMeshRepo.mThread ) - { - return; - } - - S32 data_size = buffer->countAfter(channels.in(), NULL); - - if (status < 200 || status > 400) - { - llwarns << status << ": " << reason << llendl; - } - - if (data_size < mRequestedBytes) - { - if (status == 499 || status == 503) - { //timeout or service unavailable, try again - llwarns << "Timeout or service unavailable, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; - gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD); - } - else - { - llassert(status == 499 || status == 503); //intentionally trigger a breakpoint - llwarns << "Unhandled status " << status << llendl; - } - return; - } - - LLMeshRepository::sBytesReceived += mRequestedBytes; - - U8* data = NULL; - - if (data_size > 0) - { - data = new U8[data_size]; - buffer->readAfter(channels.in(), NULL, data, data_size); - } - - if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size)) - { - //good fetch from sim, write to VFS for caching - LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE); - - S32 offset = mOffset; - S32 size = mRequestedBytes; - - if (file.getSize() >= offset+size) - { - file.seek(offset); - file.write(data, size); - LLMeshRepository::sCacheBytesWritten += size; - } - } - - delete [] data; -} - -void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) -{ - mProcessed = true; - - // thread could have already be destroyed during logout - if( !gMeshRepo.mThread ) - { - return; - } - - S32 data_size = buffer->countAfter(channels.in(), NULL); - - if (status < 200 || status > 400) - { - llwarns << status << ": " << reason << llendl; - } - - if (data_size < mRequestedBytes) - { - if (status == 499 || status == 503) - { //timeout or service unavailable, try again - llwarns << "Timeout or service unavailable, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; - gMeshRepo.mThread->loadMeshSkinInfo(mMeshID); - } - else - { - llassert(status == 499 || status == 503); //intentionally trigger a breakpoint - llwarns << "Unhandled status " << status << llendl; - } - return; - } - - LLMeshRepository::sBytesReceived += mRequestedBytes; - - U8* data = NULL; - - if (data_size > 0) - { - data = new U8[data_size]; - buffer->readAfter(channels.in(), NULL, data, data_size); - } - - if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) - { - //good fetch from sim, write to VFS for caching - LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); - - S32 offset = mOffset; - S32 size = mRequestedBytes; - - if (file.getSize() >= offset+size) - { - LLMeshRepository::sCacheBytesWritten += size; - file.seek(offset); - file.write(data, size); - } - } - - delete [] data; -} - -void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) -{ - mProcessed = true; - - if( !gMeshRepo.mThread ) - { - return; - } - - S32 data_size = buffer->countAfter(channels.in(), NULL); - - if (status < 200 || status > 400) - { - llwarns << status << ": " << reason << llendl; - } - - if (data_size < mRequestedBytes) - { - if (status == 499 || status == 503) - { //timeout or service unavailable, try again - llwarns << "Timeout or service unavailable, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; - gMeshRepo.mThread->loadMeshDecomposition(mMeshID); - } - else - { - llassert(status == 499 || status == 503); //intentionally trigger a breakpoint - llwarns << "Unhandled status " << status << llendl; - } - return; - } - - LLMeshRepository::sBytesReceived += mRequestedBytes; - - U8* data = NULL; - - if (data_size > 0) - { - data = new U8[data_size]; - buffer->readAfter(channels.in(), NULL, data, data_size); - } - - if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) - { - //good fetch from sim, write to VFS for caching - LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); - - S32 offset = mOffset; - S32 size = mRequestedBytes; - - if (file.getSize() >= offset+size) - { - LLMeshRepository::sCacheBytesWritten += size; - file.seek(offset); - file.write(data, size); - } - } - - delete [] data; -} - -void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) -{ - mProcessed = true; - - // thread could have already be destroyed during logout - if( !gMeshRepo.mThread ) - { - return; - } - - S32 data_size = buffer->countAfter(channels.in(), NULL); - - if (status < 200 || status > 400) - { - llwarns << status << ": " << reason << llendl; - } - - if (data_size < mRequestedBytes) - { - if (status == 499 || status == 503) - { //timeout or service unavailable, try again - llwarns << "Timeout or service unavailable, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; - gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID); - } - else - { - llassert(status == 499 || status == 503); //intentionally trigger a breakpoint - llwarns << "Unhandled status " << status << llendl; - } - return; - } - - LLMeshRepository::sBytesReceived += mRequestedBytes; - - U8* data = NULL; - - if (data_size > 0) - { - data = new U8[data_size]; - buffer->readAfter(channels.in(), NULL, data, data_size); - } - - if (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); - - S32 offset = mOffset; - S32 size = mRequestedBytes; - - if (file.getSize() >= offset+size) - { - LLMeshRepository::sCacheBytesWritten += size; - file.seek(offset); - file.write(data, size); - } - } - - delete [] data; -} - - LLMeshHandlerBase::~LLMeshHandlerBase() {} - void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { mProcessed = true; - LLCore::HttpStatus status(response->getStatus()); if (! status) { @@ -2506,7 +2086,6 @@ LLMeshHeaderHandler::~LLMeshHeaderHandler() } } - void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) { LL_WARNS("Mesh") << "MESH: Processing Failure" << LL_ENDL; @@ -2525,7 +2104,6 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) } } - void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) { // LL_WARNS("Mesh") << "MESH: Processing Data" << LL_ENDL; @@ -2596,7 +2174,6 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 } } - LLMeshLODHandler::~LLMeshLODHandler() { if (! LLApp::isQuitting()) @@ -2646,7 +2223,6 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 da } } - LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler() { llassert(mProcessed); @@ -2687,7 +2263,6 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S } } - LLMeshDecompositionHandler::~LLMeshDecompositionHandler() { llassert(mProcessed); @@ -2728,7 +2303,6 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * da } } - LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler() { llassert(mProcessed); @@ -2750,7 +2324,6 @@ void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status) } } - void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) { if (gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size)) @@ -2770,134 +2343,6 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * dat } } - -void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) -{ - mProcessed = true; - - // thread could have already be destroyed during logout - if( !gMeshRepo.mThread ) - { - return; - } - - if (status < 200 || status > 400) - { - //llwarns - // << "Header responder failed with status: " - // << status << ": " << reason << llendl; - - // 503 (service unavailable) or 499 (timeout) - // can be due to server load and can be retried - - // TODO*: Add maximum retry logic, exponential backoff - // and (somewhat more optional than the others) retries - // again after some set period of time - - llassert(status == 503 || status == 499); - - if (status == 503 || status == 499) - { //retry - llwarns << "Timeout or service unavailable, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; - LLMeshRepoThread::HeaderRequest req(mMeshParams); - LLMutexLock lock(gMeshRepo.mThread->mMutex); - gMeshRepo.mThread->mHeaderReqQ.push(req); - - return; - } - else - { - llwarns << "Unhandled status." << llendl; - } - } - - S32 data_size = buffer->countAfter(channels.in(), NULL); - - U8* data = NULL; - - if (data_size > 0) - { - data = new U8[data_size]; - buffer->readAfter(channels.in(), NULL, data, data_size); - } - - LLMeshRepository::sBytesReceived += llmin(data_size, MESH_HEADER_SIZE); - - bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); - - llassert(success); - - if (!success) - { - llwarns - << "Unable to parse mesh header: " - << status << ": " << reason << llendl; - } - else if (data && data_size > 0) - { - //header was successfully retrieved from sim, cache in vfs - LLUUID mesh_id = mMeshParams.getSculptID(); - LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id]; - - S32 version = header["version"].asInteger(); - - if (version <= MAX_MESH_VERSION) - { - std::stringstream str; - - 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 - 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()); - lod_bytes = llmax(lod_bytes, header["physics_convex"]["offset"].asInteger() + header["physics_convex"]["size"].asInteger()); - - S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id]; - S32 bytes = lod_bytes + header_bytes; - - - //it's possible for the remote asset to have more data than is needed for the local cache - //only allocate as much space in the VFS as is needed for the local cache - data_size = llmin(data_size, bytes); - - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); - if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) - { - LLMeshRepository::sCacheBytesWritten += data_size; - - file.write((const U8*) data, data_size); - - //zero out the rest of the file - U8 block[MESH_HEADER_SIZE]; - memset(block, 0, MESH_HEADER_SIZE); - - while (bytes-file.tell() > MESH_HEADER_SIZE) - { - file.write(block, MESH_HEADER_SIZE); - } - - S32 remaining = bytes-file.tell(); - - if (remaining > 0) - { - file.write(block, remaining); - } - } - } - } - - delete [] data; -} - - LLMeshRepository::LLMeshRepository() : mMeshMutex(NULL), mMeshThreadCount(0), -- cgit v1.2.3 From 7c9d0b58aca072ff932b30f927e2876dfc6858aa Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 7 Jun 2013 20:14:52 -0400 Subject: Mostly cleanup. A chunk of comment code nobody needs. Dereference after delete, erase() on end() iterator, a few more like that. Killed a dead variable. --- indra/newview/llmeshrepository.cpp | 163 ++----------------------------------- indra/newview/llmeshrepository.h | 1 - 2 files changed, 9 insertions(+), 155 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 8f6860aec7..702e940983 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -97,7 +97,6 @@ U32 LLMeshRepository::sLODPending = 0; U32 LLMeshRepository::sCacheBytesRead = 0; U32 LLMeshRepository::sCacheBytesWritten = 0; -U32 LLMeshRepository::sPeakKbps = 0; LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true); // true -> gather cpu metrics @@ -641,6 +640,7 @@ void LLMeshRepoThread::run() { mMutex->lock(); mLODReqQ.push(req) ; + ++LLMeshRepository::sLODProcessing; mMutex->unlock(); } } @@ -882,7 +882,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - LLMeshRepository::sHTTPRequestCount++; + ++LLMeshRepository::sHTTPRequestCount; } } } @@ -978,7 +978,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - LLMeshRepository::sHTTPRequestCount++; + ++LLMeshRepository::sHTTPRequestCount; } } } @@ -1073,7 +1073,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - LLMeshRepository::sHTTPRequestCount++; + ++LLMeshRepository::sHTTPRequestCount; } } } @@ -1173,7 +1173,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - LLMeshRepository::sHTTPRequestCount++; + ++LLMeshRepository::sHTTPRequestCount; } count++; } @@ -1260,7 +1260,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - LLMeshRepository::sHTTPRequestCount++; + ++LLMeshRepository::sHTTPRequestCount; } count++; } @@ -2738,9 +2738,8 @@ void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info) vobj->notifyMeshLoaded(); } } + mLoadingSkins.erase(info.mMeshID); } - - mLoadingSkins.erase(info.mMeshID); } void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp) @@ -2749,14 +2748,14 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom if (iter == mDecompositionMap.end()) { //just insert decomp into map mDecompositionMap[decomp->mMeshID] = decomp; + mLoadingDecompositions.erase(decomp->mMeshID); } else { //merge decomp with existing entry iter->second->merge(decomp); + mLoadingDecompositions.erase(decomp->mMeshID); delete decomp; } - - mLoadingDecompositions.erase(decomp->mMeshID); } void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume) @@ -3881,147 +3880,3 @@ bool is_retryable(LLCore::HttpStatus status) status == inv_cont_range)); // Short data read disagrees with content-range } - - - -// =========== -// -// HTTP fragments I'll be needing -// -// -#if 0 - - if (! status) - { - success = false; - std::string reason(status.toString()); - setGetStatus(status, reason); - llwarns << "CURL GET FAILED, status: " << status.toHex() - << " reason: " << reason << llendl; - } - else - { - // A warning about partial (HTTP 206) data. Some grid services - // do *not* return a 'Content-Range' header in the response to - // Range requests with a 206 status. We're forced to assume - // we get what we asked for in these cases until we can fix - // the services. - static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); - - partial = (par_status == status); - } - - S32 data_size = callbackHttpGet(response, partial, success); - - - - -// Threads: Ttf -// Locks: Mw -S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, - bool partial, bool success) -{ - S32 data_size = 0 ; - - if (mState != WAIT_HTTP_REQ) - { - llwarns << "callbackHttpGet for unrequested fetch worker: " << mID - << " req=" << mSentRequest << " state= " << mState << llendl; - return data_size; - } - if (mLoaded) - { - llwarns << "Duplicate callback for " << mID.asString() << llendl; - return data_size ; // ignore duplicate callback - } - if (success) - { - // get length of stream: - LLCore::BufferArray * body(response->getBody()); - data_size = body ? body->size() : 0; - - LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL; - if (data_size > 0) - { - LLViewerStatsRecorder::instance().textureFetch(data_size); - // *TODO: set the formatted image data here directly to avoid the copy - - // Hold on to body for later copy - llassert_always(NULL == mHttpBufferArray); - body->addRef(); - mHttpBufferArray = body; - - if (partial) - { - unsigned int offset(0), length(0), full_length(0); - 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... - mHttpReplySize = data_size; - mHttpReplyOffset = mRequestedOffset; - } - else - { - mHttpReplySize = length; - mHttpReplyOffset = offset; - } - } - - if (! partial) - { - // Response indicates this is the entire asset regardless - // of our asking for a byte range. Mark it so and drop - // any partial data we might have so that the current - // response body becomes the entire dataset. - if (data_size <= mRequestedOffset) - { - LL_WARNS("Texture") << "Fetched entire texture " << mID - << " when it was expected to be marked complete. mImageSize: " - << mFileSize << " datasize: " << mFormattedImage->getDataSize() - << LL_ENDL; - } - mHaveAllData = TRUE; - llassert_always(mDecodeHandle == 0); - mFormattedImage = NULL; // discard any previous data we had - } - else if (data_size < mRequestedSize) - { - mHaveAllData = TRUE; - } - else if (data_size > mRequestedSize) - { - // *TODO: This shouldn't be happening any more (REALLY don't expect this anymore) - llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl; - mHaveAllData = TRUE; - llassert_always(mDecodeHandle == 0); - mFormattedImage = NULL; // discard any previous data we had - } - } - else - { - // We requested data but received none (and no error), - // so presumably we have all of it - mHaveAllData = TRUE; - } - mRequestedSize = data_size; - } - else - { - mRequestedSize = -1; // error - } - - mLoaded = TRUE; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - - LLViewerStatsRecorder::instance().log(0.2f); - return data_size ; -} - -#endif -// ============ - diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 8ffe2d68cb..62f81ce9e2 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -469,7 +469,6 @@ public: static U32 sLODProcessing; static U32 sCacheBytesRead; static U32 sCacheBytesWritten; - static U32 sPeakKbps; static LLDeadmanTimer sQuiescentTimer; // time-to-complete-mesh-downloads after significant events static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL); -- cgit v1.2.3 From 626752beab7c12a355ab707d70aba6f4fe096c10 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 19 Jun 2013 13:55:54 -0400 Subject: SH-4252 Add second policy class for large mesh asset downloads Added second mesh class as well as an asset upload class. Refactored initialization to use less code and more data to cleanly get http started. Modified mesh to use the new http class for large requests (>2MB for now). Added additional timeout setting to llcorehttp to distinguish connection timeout from transport timeout and are now using transport timeout values for large asset downloads that may need more time. --- indra/newview/llappcorehttp.cpp | 156 ++++++++++++++++------------- indra/newview/llappcorehttp.h | 36 +++---- indra/newview/llmeshrepository.cpp | 197 +++++++++++++++++++++---------------- indra/newview/llmeshrepository.h | 21 +++- indra/newview/lltexturefetch.cpp | 4 +- 5 files changed, 243 insertions(+), 171 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 142344e277..b601b31d21 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -37,11 +37,13 @@ LLAppCoreHttp::LLAppCoreHttp() : mRequest(NULL), mStopHandle(LLCORE_HTTP_HANDLE_INVALID), mStopRequested(0.0), - mStopped(false), - mPolicyDefault(-1), - mPolicyTexture(-1), - mPolicyMesh(-1) -{} + mStopped(false) +{ + for (int i(0); i < LL_ARRAY_SIZE(mPolicies); ++i) + { + mPolicies[i] = LLCore::HttpRequest::DEFAULT_POLICY_ID; + } +} LLAppCoreHttp::~LLAppCoreHttp() @@ -53,11 +55,43 @@ LLAppCoreHttp::~LLAppCoreHttp() void LLAppCoreHttp::init() { + static const struct + { + EAppPolicy mPolicy; + U32 mDefault; + U32 mMin; + U32 mMax; + U32 mDivisor; + std::string mKey; + const char * mUsage; + } init_data[] = // Default and dynamic values for classes + { + { + AP_TEXTURE, 8, 1, 12, 1, + "TextureFetchConcurrency", + "texture fetch" + }, + { + AP_MESH, 8, 1, 32, 4, + "MeshMaxConcurrentRequests", + "mesh fetch" + }, + { + AP_LARGE_MESH, 2, 1, 8, 1, + "", + "large mesh fetch" + }, + { + AP_UPLOADS, 2, 1, 8, 1, + "", + "asset upload" + } + }; + LLCore::HttpStatus status = LLCore::HttpRequest::createService(); if (! status) { - LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: " - << status.toString() + LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: " << status.toString() << LL_ENDL; } @@ -66,8 +100,7 @@ void LLAppCoreHttp::init() gDirUtilp->getCAFile()); if (! status) { - LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " - << status.toString() + LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " << status.toString() << LL_ENDL; } @@ -77,8 +110,7 @@ void LLAppCoreHttp::init() status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); if (! status) { - LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " - << status.toString() + LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " << status.toString() << LL_ENDL; } @@ -96,80 +128,72 @@ void LLAppCoreHttp::init() } // Setup default policy and constrain if directed to - mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID; + mPolicies[AP_DEFAULT] = LLCore::HttpRequest::DEFAULT_POLICY_ID; - // Texture policy will use default for now. - mPolicyTexture = mPolicyDefault; - static const std::string texture_concur("TextureFetchConcurrency"); - if (gSavedSettings.controlExists(texture_concur)) + // Setup additional policies based on table and some special rules + // *TODO: Make these configurations dynamic later + for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) { - U32 concur(llmin(gSavedSettings.getU32(texture_concur), U32(12))); + const EAppPolicy policy(init_data[i].mPolicy); - if (concur > 0) + // Create a policy class but use default for texture for now. + // This also has the side-effect of initializing the default + // class to desired values. + if (AP_TEXTURE == policy) { - LLCore::HttpStatus status; - status = LLCore::HttpRequest::setPolicyClassOption(mPolicyTexture, - LLCore::HttpRequest::CP_CONNECTION_LIMIT, - concur); - if (! status) - { - LL_WARNS("Init") << "Unable to set texture fetch concurrency. Reason: " - << status.toString() - << LL_ENDL; - } - else + mPolicies[policy] = mPolicies[AP_DEFAULT]; + } + else + { + mPolicies[policy] = LLCore::HttpRequest::createPolicyClass(); + if (! mPolicies[policy]) { - LL_INFOS("Init") << "Application settings overriding default texture fetch concurrency. New value: " - << concur + // Use default policy (but don't accidentally modify default) + LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage + << ". Using default policy." << LL_ENDL; + mPolicies[policy] = mPolicies[AP_DEFAULT]; + continue; } } - } - // Create the mesh class - mPolicyMesh = LLCore::HttpRequest::createPolicyClass(); - if (! mPolicyMesh) - { - LL_WARNS("Init") << "Failed to create HTTP policy class for Mesh. Using default policy." - << LL_ENDL; - mPolicyMesh = mPolicyDefault; - } - else - { - static const std::string mesh_concur("MeshMaxConcurrentRequests"); - if (gSavedSettings.controlExists(mesh_concur)) + // Get target connection concurrency value + U32 setting(init_data[i].mDefault); + if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey)) { - U32 setting(llmin(gSavedSettings.getU32(mesh_concur), 256U) / 4U); - setting = llmax(setting, 2U); - - if (setting > 0) + U32 new_setting(gSavedSettings.getU32(init_data[i].mKey)); + if (new_setting) { - LLCore::HttpStatus status; - status = LLCore::HttpRequest::setPolicyClassOption(mPolicyMesh, - LLCore::HttpRequest::CP_CONNECTION_LIMIT, - setting); - if (! status) - { - LL_WARNS("Init") << "Unable to set mesh fetch concurrency. Reason: " - << status.toString() - << LL_ENDL; - } - else - { - LL_INFOS("Init") << "Application settings overriding default mesh fetch concurrency. New value: " - << setting - << LL_ENDL; - } + // Treat zero settings as an ask for default + setting = new_setting / init_data[i].mDivisor; + setting = llclamp(setting, init_data[i].mMin, init_data[i].mMax); } } + + // Set it and report + LLCore::HttpStatus status; + status = LLCore::HttpRequest::setPolicyClassOption(mPolicies[policy], + LLCore::HttpRequest::CP_CONNECTION_LIMIT, + setting); + if (! status) + { + LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage + << " concurrency. Reason: " << status.toString() + << LL_ENDL; + } + else if (setting != init_data[i].mDefault) + { + LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage + << " concurrency. New value: " << setting + << LL_ENDL; + } } // Kick the thread status = LLCore::HttpRequest::startThread(); if (! status) { - LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: " - << status.toString() + LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: " << status.toString() << LL_ENDL; } diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h index d90af9e5ca..532e1f5cb0 100755 --- a/indra/newview/llappcorehttp.h +++ b/indra/newview/llappcorehttp.h @@ -40,6 +40,19 @@ // as a singleton and static construction is fine. class LLAppCoreHttp : public LLCore::HttpHandler { +public: + typedef LLCore::HttpRequest::policy_t policy_t; + + enum EAppPolicy + { + AP_DEFAULT, + AP_TEXTURE, + AP_MESH, + AP_LARGE_MESH, + AP_UPLOADS, + AP_COUNT // Must be last + }; + public: LLAppCoreHttp(); ~LLAppCoreHttp(); @@ -65,22 +78,11 @@ public: // Notification when the stop request is complete. virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); - // Retrieve the policy class for default operations. - int getPolicyDefault() const - { - return mPolicyDefault; - } - - // Get the texture fetch policy class. - int getPolicyTexture() const - { - return mPolicyTexture; - } - - // Get the mesh fetch policy class. - int getPolicyMesh() const + // Retrieve a policy class identifier for desired + // application function. + policy_t getPolicy(EAppPolicy policy) const { - return mPolicyMesh; + return mPolicies[policy]; } private: @@ -91,9 +93,7 @@ private: LLCore::HttpHandle mStopHandle; F64 mStopRequested; bool mStopped; - int mPolicyDefault; - int mPolicyTexture; - int mPolicyMesh; + policy_t mPolicies[AP_COUNT]; }; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 702e940983..f0ec97a34d 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -80,6 +80,10 @@ LLMeshRepository gMeshRepo; const S32 MESH_HEADER_SIZE = 4096; const U32 MAX_MESH_REQUESTS_PER_SECOND = 100; +const S32 REQUEST_HIGH_WATER_MIN = 32; +const S32 REQUEST_LOW_WATER_MIN = 16; +const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes to narrow/slow queue +const long LARGE_MESH_XFER_TIMEOUT = 240L; // Seconds to complete xfer // Maximum mesh version to support. Three least significant digits are reserved for the minor version, // with major version changes indicating a format change that is not backwards compatible and should not @@ -210,6 +214,8 @@ void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res, S32 LLMeshRepoThread::sActiveHeaderRequests = 0; S32 LLMeshRepoThread::sActiveLODRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; +S32 LLMeshRepoThread::sRequestLowWater = REQUEST_LOW_WATER_MIN; +S32 LLMeshRepoThread::sRequestHighWater = REQUEST_HIGH_WATER_MIN; class LLMeshHandlerBase : public LLCore::HttpHandler { @@ -548,25 +554,37 @@ public: LLMeshRepoThread::LLMeshRepoThread() : LLThread("mesh repo"), - mCurlRequest(NULL), mWaiting(false), mHttpRequest(NULL), mHttpOptions(NULL), + mHttpLargeOptions(NULL), mHttpHeaders(NULL), - mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID) + mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpPriority(0), + mHttpGetCount(0U), + mHttpLargeGetCount(0U) { mMutex = new LLMutex(NULL); mHeaderMutex = new LLMutex(NULL); mSignal = new LLCondition(NULL); mHttpRequest = new LLCore::HttpRequest; mHttpOptions = new LLCore::HttpOptions; + mHttpLargeOptions = new LLCore::HttpOptions; + mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT); mHttpHeaders = new LLCore::HttpHeaders; mHttpHeaders->mHeaders.push_back("Accept: application/vnd.ll.mesh"); - mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyMesh(); + mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH); + mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_LARGE_MESH); } + LLMeshRepoThread::~LLMeshRepoThread() { + LL_INFOS("Mesh") << "Small GETs issued: " + << mHttpGetCount << ", Large GETs issued: " + << mHttpLargeGetCount << LL_ENDL; + for (http_request_set::iterator iter(mHttpRequestSet.begin()); iter != mHttpRequestSet.end(); ++iter) @@ -584,6 +602,11 @@ LLMeshRepoThread::~LLMeshRepoThread() mHttpOptions->release(); mHttpOptions = NULL; } + if (mHttpLargeOptions) + { + mHttpLargeOptions->release(); + mHttpLargeOptions = NULL; + } delete mHttpRequest; mHttpRequest = NULL; delete mMutex; @@ -596,7 +619,6 @@ LLMeshRepoThread::~LLMeshRepoThread() void LLMeshRepoThread::run() { - mCurlRequest = new LLCurlRequest(); LLCDResult res = LLConvexDecomposition::initThread(); if (res != LLCD_OK) { @@ -627,7 +649,7 @@ void LLMeshRepoThread::run() // NOTE: throttling intentionally favors LOD requests over header requests - while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < sMaxConcurrentRequests) + while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND) { if (mMutex) { @@ -646,7 +668,7 @@ void LLMeshRepoThread::run() } } - while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < sMaxConcurrentRequests) + while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND) { if (mMutex) { @@ -701,8 +723,6 @@ void LLMeshRepoThread::run() } mPhysicsShapeRequests = incomplete; } - - mCurlRequest->process(); } } @@ -716,9 +736,6 @@ void LLMeshRepoThread::run() { llwarns << "convex decomposition unable to be quit" << llendl; } - - delete mCurlRequest; - mCurlRequest = NULL; } void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id) @@ -800,6 +817,42 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) return http_url; } +// May only be called by repo thread +LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, + size_t offset, + size_t len, + LLCore::HttpHandler * handler) +{ + LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + if (len < LARGE_MESH_FETCH_THRESHOLD) + { + handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, + mHttpPriority, + url, + offset, + len, + mHttpOptions, + mHttpHeaders, + handler); + ++mHttpGetCount; + } + else + { + handle = mHttpRequest->requestGetByteRange(mHttpLargePolicyClass, + mHttpPriority, + url, + offset, + len, + mHttpLargeOptions, + mHttpHeaders, + handler); + ++mHttpLargeGetCount; + } + return handle; +} + + bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) { //protected by mMutex @@ -863,14 +916,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) { LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size); // LL_WARNS("Mesh") << "MESH: Issuing Skin Info Request" << LL_ENDL; - LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, - 0, // *TODO: Get better priority value - http_url, - offset, - size, - mHttpOptions, - mHttpHeaders, - handler); + LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { // *TODO: Better error message @@ -959,14 +1005,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) { LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size); // LL_WARNS("Mesh") << "MESH: Issuing Decomp Request" << LL_ENDL; - LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, - 0, // *TODO: Get better priority value - http_url, - offset, - size, - mHttpOptions, - mHttpHeaders, - handler); + LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { // *TODO: Better error message @@ -1054,14 +1093,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) { LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size); // LL_WARNS("Mesh") << "MESH: Issuing Physics Shape Request" << LL_ENDL; - LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, - 0, // *TODO: Get better priority value - http_url, - offset, - size, - mHttpOptions, - mHttpHeaders, - handler); + LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { // *TODO: Better error message @@ -1154,14 +1186,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params); // LL_WARNS("Mesh") << "MESH: Issuing Request" << LL_ENDL; - LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, - 0, // *TODO: Get better priority value - http_url, - 0, - MESH_HEADER_SIZE, - mHttpOptions, - mHttpHeaders, - handler); + LLCore::HttpHandle handle = getByteRange(http_url, 0, MESH_HEADER_SIZE, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { // *TODO: Better error message @@ -1241,14 +1266,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, { LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size); // LL_WARNS("Mesh") << "MESH: Issuing LOD Request" << LL_ENDL; - LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, - 0, // *TODO: Get better priority value - http_url, - offset, - size, - mHttpOptions, - mHttpHeaders, - handler); + LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { // *TODO: Better error message @@ -2537,9 +2555,14 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para void LLMeshRepository::notifyLoadedMeshes() { //called from main thread - - LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests"); - + // *FIXME: Scaling down the setting by a factor of 4 for now to reflect + // target goal. May want to rename the setting before release. + LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests") / 4; + LLMeshRepoThread::sRequestHighWater = llmax(50 * S32(LLMeshRepoThread::sMaxConcurrentRequests), + REQUEST_HIGH_WATER_MIN); + LLMeshRepoThread::sRequestLowWater = llmax(LLMeshRepoThread::sRequestLowWater / 2, + REQUEST_LOW_WATER_MIN); + //clean up completed upload threads for (std::vector::iterator iter = mUploads.begin(); iter != mUploads.end(); ) { @@ -2617,7 +2640,7 @@ void LLMeshRepository::notifyLoadedMeshes() //call completed callbacks on finished decompositions mDecompThread->notifyCompleted(); - if (!mThread->mWaiting) + if (!mThread->mWaiting && mPendingRequests.empty()) { //curl thread is churning, wait for it to go idle return; } @@ -2644,47 +2667,55 @@ void LLMeshRepository::notifyLoadedMeshes() mUploadErrorQ.pop(); } - S32 push_count = LLMeshRepoThread::sMaxConcurrentRequests-(LLMeshRepoThread::sActiveHeaderRequests+LLMeshRepoThread::sActiveLODRequests); - - if (push_count > 0) + S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests; + if (active_count < LLMeshRepoThread::sRequestLowWater) { - //calculate "score" for pending requests - - //create score map - std::map score_map; + S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count; - for (U32 i = 0; i < 4; ++i) + if (mPendingRequests.size() > push_count) { - for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter) + // More requests than the high-water limit allows so + // sort and forward the most important. + + //calculate "score" for pending requests + + //create score map + std::map score_map; + + 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" - std::sort(mPendingRequests.begin(), mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater()); + //sort by "score" + std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count, + mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater()); + } while (!mPendingRequests.empty() && push_count > 0) { diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 62f81ce9e2..0dca29e7d4 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -224,13 +224,14 @@ public: static S32 sActiveHeaderRequests; static S32 sActiveLODRequests; static U32 sMaxConcurrentRequests; + static S32 sRequestLowWater; + static S32 sRequestHighWater; - LLCurlRequest* mCurlRequest; LLMutex* mMutex; LLMutex* mHeaderMutex; LLCondition* mSignal; - bool mWaiting; + volatile bool mWaiting; //map of known mesh headers typedef std::map mesh_header_map; @@ -324,8 +325,11 @@ public: // llcorehttp library interface objects. LLCore::HttpRequest * mHttpRequest; LLCore::HttpOptions * mHttpOptions; + LLCore::HttpOptions * mHttpLargeOptions; LLCore::HttpHeaders * mHttpHeaders; LLCore::HttpRequest::policy_t mHttpPolicyClass; + LLCore::HttpRequest::policy_t mHttpLargePolicyClass; + LLCore::HttpRequest::priority_t mHttpPriority; typedef std::set http_request_set; http_request_set mHttpRequestSet; // Outstanding HTTP requests @@ -373,6 +377,19 @@ public: static void incActiveHeaderRequests(); static void decActiveHeaderRequests(); +private: + // Issue a GET request to a URL with 'Range' header using + // the correct policy class and other attributes. If an invalid + // handle is returned, the request failed and caller must retry + // or dispose of handler. + // + // Threads: Repo thread only + LLCore::HttpHandle getByteRange(const std::string & url, size_t offset, size_t len, + LLCore::HttpHandler * handler); + +private: + U32 mHttpGetCount; + U32 mHttpLargeGetCount; }; class LLMeshUploadThread : public LLThread diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index be5fde9e2b..d934ef9dc4 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -2410,7 +2410,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); mHttpMetricsHeaders = new LLCore::HttpHeaders; mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml"); - mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault(); + mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_TEXTURE); } LLTextureFetch::~LLTextureFetch() -- cgit v1.2.3 From 4eef1c8a2e2a8abcc463b9df38b66f025da57589 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 15 Apr 2013 16:55:35 +0000 Subject: SH-4106 Significantly upgrade the HttpHeaders interface for SSB. Header container moves from a vector of raw lines to a vector of string pairs representing name/value pairs in headers. For incoming headers, we normalize the name to lowercase and trim it. Values are only left-trimmed. Outgoing headers are left as-is. Simple find() method for the common case, forward and reverse iterators for those few who need to do it themselves. The HTTP status line (e.g. 'HTTP/1.1 200 Ok') is no longer treated as a header to be returned to caller. Unit tests, as usual, were a bear but they absolutely ensured outgoing HTTP header conformance after the change. Grunt work paid off. LLTextureFetch was also given a second options structure for texture fetches. Same as the original but with header return to caller requested. Baked textures should use this, the other 20,000 texture fetch requests should continue to use the original. --- indra/newview/lltexturefetch.cpp | 15 ++++++++++++--- indra/newview/lltexturefetch.h | 3 ++- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index d934ef9dc4..a8c8445ee1 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2376,6 +2376,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mQAMode(qa_mode), mHttpRequest(NULL), mHttpOptions(NULL), + mHttpOptionsWithHeaders(NULL), mHttpHeaders(NULL), mHttpMetricsHeaders(NULL), mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), @@ -2406,10 +2407,12 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpRequest = new LLCore::HttpRequest; mHttpOptions = new LLCore::HttpOptions; + mHttpOptionsWithHeaders = new LLCore::HttpOptions; + mHttpOptionsWithHeaders->setWantHeaders(true); mHttpHeaders = new LLCore::HttpHeaders; - mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); + mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C); mHttpMetricsHeaders = new LLCore::HttpHeaders; - mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml"); + mHttpMetricsHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_TEXTURE); } @@ -2430,6 +2433,12 @@ LLTextureFetch::~LLTextureFetch() mHttpOptions = NULL; } + if (mHttpOptionsWithHeaders) + { + mHttpOptionsWithHeaders->release(); + mHttpOptionsWithHeaders = NULL; + } + if (mHttpHeaders) { mHttpHeaders->release(); @@ -4041,7 +4050,7 @@ void LLTextureFetchDebugger::init() if (! mHttpHeaders) { mHttpHeaders = new LLCore::HttpHeaders; - mHttpHeaders->mHeaders.push_back("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 902a3d7a25..3c79a5a24d 100755 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -351,6 +351,7 @@ private: // LLCurl interfaces used in the past. LLCore::HttpRequest * mHttpRequest; // Ttf LLCore::HttpOptions * mHttpOptions; // Ttf + LLCore::HttpOptions * mHttpOptionsWithHeaders; // Ttf LLCore::HttpHeaders * mHttpHeaders; // Ttf LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf LLCore::HttpRequest::policy_t mHttpPolicyClass; // T* -- cgit v1.2.3 From e59d59878200d25dc6e94753026d986d2704ab8d Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 17 Apr 2013 18:30:46 -0400 Subject: SH-4090 Integrating deadman timer with mesh repo downloads. Timer interface violated my design rules and I paid for it with clumsiness and silent errors. Cleaned it up mainly removing the evil default values. Found better integration points in the mesh downloader and it's producing fairly consistent numbers on the MeshTest2 test region (about 5500 downloads, ~90 seconds, +/- 10 seconds). Will review with davep and do an early timer stop on teleport which invalidates a timing sequence. --- indra/newview/llmeshrepository.cpp | 48 ++++++++++++++++++++++++++++---------- indra/newview/llmeshrepository.h | 3 ++- 2 files changed, 38 insertions(+), 13 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 11c5780a30..8f97f3e71a 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -2168,6 +2168,9 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, if (status < 200 || status > 400) { + // Manage time-to-load metrics for mesh download operations. + LLMeshRepository::metricsProgress(0); + //llwarns // << "Header responder failed with status: " // << status << ": " << reason << llendl; @@ -2358,6 +2361,9 @@ void LLMeshRepository::shutdown() //called in the main thread. S32 LLMeshRepository::update() { + // Conditionally log a mesh metrics event + metricsUpdate(); + if(mUploadWaitList.empty()) { return 0 ; @@ -2377,6 +2383,9 @@ S32 LLMeshRepository::update() S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod) { + // Manage time-to-load metrics for mesh download operations. + metricsProgress(1); + if (detail < 0 || detail > 4) { return detail; @@ -2680,7 +2689,7 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume) { //called from main thread // Manage time-to-load metrics for mesh download operations. - metricsCheck(); + metricsProgress(0); S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail()); @@ -2725,6 +2734,9 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod) { //called from main thread + // Manage time-to-load metrics for mesh download operations. + metricsProgress(0); + //get list of objects waiting to be notified this mesh is loaded mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params); @@ -3704,39 +3716,51 @@ bool LLMeshRepository::meshRezEnabled() return false; } +// Threading: main thread only +// static void LLMeshRepository::metricsStart() { - sQuiescentTimer.start(); + sQuiescentTimer.start(0); } +// Threading: main thread only +// static void LLMeshRepository::metricsStop() { - sQuiescentTimer.stop(); + sQuiescentTimer.stop(0); } -void LLMeshRepository::metricsCheck() +// Threading: main thread only +// static +void LLMeshRepository::metricsProgress(unsigned int this_count) { static bool first_start(true); - F64 started, stopped; - U64 count; - if (first_start) { // Let the first request start the timing cycle for login. metricsStart(); first_start = false; } - sQuiescentTimer.ringBell(); - if (sQuiescentTimer.isExpired(started, stopped, count)) + sQuiescentTimer.ringBell(0, this_count); +} + +// Threading: main thread only +// static +void LLMeshRepository::metricsUpdate() +{ + F64 started, stopped; + U64 total_count; + + if (sQuiescentTimer.isExpired(0, started, stopped, total_count)) { LLSD metrics; - metrics["reason"] = "Mesh download quiescent"; + metrics["reason"] = "Mesh Download Quiescent"; metrics["scope"] = "Login"; metrics["start"] = started; metrics["stop"] = stopped; - metrics["downloads"] = LLSD::Integer(count); - llinfos << "MetricsMarker" << metrics << llendl; + metrics["downloads"] = LLSD::Integer(total_count); + llinfos << "EventMarker " << metrics << llendl; } } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index f08feedf81..3cdc66e1f0 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -500,7 +500,8 @@ public: // Quiescent timer management, main thread only. static void metricsStart(); static void metricsStop(); - static void metricsCheck(); + static void metricsProgress(unsigned int count); + static void metricsUpdate(); typedef std::map > mesh_load_map; mesh_load_map mLoadingMeshes[4]; -- cgit v1.2.3 From 7911f065cde252d3f1eda4a1577e5d0b3eb94e19 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 18 Apr 2013 16:23:15 -0400 Subject: SH-4090 Metrics for mesh load time - cleanup after davep review One of the metrics calls was running in an LLCurl-owned thread doing responder invocation. Deleted that invocation and will do with the other safe ones. Added a boost signal on the TeleportStarted message which is now used to restart the metrics timer. I think I'd like to move the metric blob into a free- standing entity later... --- indra/newview/llmeshrepository.cpp | 47 ++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 8f97f3e71a..0f33128057 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2010-2013, 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 @@ -52,6 +52,7 @@ #include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewermenufile.h" +#include "llviewermessage.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewertexturelist.h" @@ -109,7 +110,7 @@ std::string make_dump_name(std::string prefix, S32 num) void dump_llsd_to_file(const LLSD& content, std::string filename); LLSD llsd_from_file(std::string filename); -std::string header_lod[] = +const std::string header_lod[] = { "lowest_lod", "low_lod", @@ -117,6 +118,13 @@ std::string header_lod[] = "high_lod" }; +// Static data and functions to measure mesh load +// time metrics for a new region scene. +static bool metrics_inited(false); +static boost::signals2::connection metrics_teleport_connection; +static unsigned int metrics_teleport_start_count(0); +static void metrics_teleport_started(); + //get the number of bytes resident in memory for given volume U32 get_volume_memory_size(const LLVolume* volume) { @@ -2168,9 +2176,6 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, if (status < 200 || status > 400) { - // Manage time-to-load metrics for mesh download operations. - LLMeshRepository::metricsProgress(0); - //llwarns // << "Header responder failed with status: " // << status << ": " << reason << llendl; @@ -2310,12 +2315,26 @@ void LLMeshRepository::init() mThread = new LLMeshRepoThread(); mThread->start(); + + if (! metrics_inited) + { + // Get teleport started signals to restart timings. + metrics_teleport_connection = LLViewerMessage::getInstance()-> + setTeleportStartedCallback(metrics_teleport_started); + metrics_inited = true; + } } void LLMeshRepository::shutdown() { llinfos << "Shutting down mesh repository." << llendl; + if (metrics_inited) + { + metrics_teleport_connection.disconnect(); + metrics_inited = false; + } + for (U32 i = 0; i < mUploads.size(); ++i) { llinfos << "Discard the pending mesh uploads " << llendl; @@ -3735,9 +3754,10 @@ void LLMeshRepository::metricsStop() void LLMeshRepository::metricsProgress(unsigned int this_count) { static bool first_start(true); + if (first_start) { - // Let the first request start the timing cycle for login. + ++metrics_teleport_start_count; metricsStart(); first_start = false; } @@ -3760,7 +3780,20 @@ void LLMeshRepository::metricsUpdate() metrics["start"] = started; metrics["stop"] = stopped; metrics["downloads"] = LLSD::Integer(total_count); + metrics["teleports"] = LLSD::Integer(metrics_teleport_start_count); llinfos << "EventMarker " << metrics << llendl; } } - + +// Will use a request to start a teleport as a signal to +// restart a timing sequence. We don't get one of these +// for login so initial start is done above. +// +// Threading: main thread only +// static +void metrics_teleport_started() +{ + LLMeshRepository::metricsStart(); + ++metrics_teleport_start_count; +} + -- cgit v1.2.3 From 11cca95187f01f594172ca950315dcd8d99dc2c3 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 2 May 2013 21:59:53 +0000 Subject: SH-4161 Integrate cpu metrics into LLDeadmanTimer and then metrics viewer Integrated as a ctor-time option to LLDeadmanTimer and have mesh use this mode for the stats I'm gathering. --- indra/newview/llmeshrepository.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 0f33128057..2a863a3103 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -96,7 +96,7 @@ U32 LLMeshRepository::sLODPending = 0; U32 LLMeshRepository::sCacheBytesRead = 0; U32 LLMeshRepository::sCacheBytesWritten = 0; U32 LLMeshRepository::sPeakKbps = 0; -LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0); +LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true); const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5; -- cgit v1.2.3 From 960139aa6f02f90c6102d3c5d5c38b5ebe689f9c Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 2 May 2013 19:12:59 -0400 Subject: SH-4161 Integrate cpu metrics into LLDeadmanTimer and then metrics viewer Normalize deadman timer's args on U64/F64. Internals remain the same. Modify mesh to collect and output enhanced CPU metrics. --- indra/newview/llmeshrepository.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 2a863a3103..4d3937ded1 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -96,7 +96,7 @@ U32 LLMeshRepository::sLODPending = 0; U32 LLMeshRepository::sCacheBytesRead = 0; U32 LLMeshRepository::sCacheBytesWritten = 0; U32 LLMeshRepository::sPeakKbps = 0; -LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true); +LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true); // true -> gather cpu metrics const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5; @@ -3769,9 +3769,9 @@ void LLMeshRepository::metricsProgress(unsigned int this_count) void LLMeshRepository::metricsUpdate() { F64 started, stopped; - U64 total_count; - - if (sQuiescentTimer.isExpired(0, started, stopped, total_count)) + U64 total_count(U64L(0)), user_cpu(U64L(0)), sys_cpu(U64L(0)); + + if (sQuiescentTimer.isExpired(0, started, stopped, total_count, user_cpu, sys_cpu)) { LLSD metrics; @@ -3781,6 +3781,8 @@ void LLMeshRepository::metricsUpdate() metrics["stop"] = stopped; metrics["downloads"] = LLSD::Integer(total_count); metrics["teleports"] = LLSD::Integer(metrics_teleport_start_count); + metrics["user_cpu"] = double(user_cpu) / 1.0e6; + metrics["sys_cpu"] = double(sys_cpu) / 1.0e6; llinfos << "EventMarker " << metrics << llendl; } } -- cgit v1.2.3 From d6741a4fc088632c179f767df240953fc4f7474f Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 19 Jun 2013 19:58:09 +0000 Subject: Fixups for the transplant of the HttpHeader changes from sunshine. --- indra/newview/llmeshrepository.cpp | 2 +- indra/newview/lltexturefetch.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index f0ec97a34d..221a797fc7 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -573,7 +573,7 @@ LLMeshRepoThread::LLMeshRepoThread() mHttpLargeOptions = new LLCore::HttpOptions; mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT); mHttpHeaders = new LLCore::HttpHeaders; - mHttpHeaders->mHeaders.push_back("Accept: application/vnd.ll.mesh"); + mHttpHeaders->append("Accept", "application/vnd.ll.mesh"); mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH); mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_LARGE_MESH); } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index a8c8445ee1..bf9d803098 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2410,9 +2410,9 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpOptionsWithHeaders = new LLCore::HttpOptions; mHttpOptionsWithHeaders->setWantHeaders(true); mHttpHeaders = new LLCore::HttpHeaders; - mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C); + mHttpHeaders->append("Accept", "image/x-j2c"); mHttpMetricsHeaders = new LLCore::HttpHeaders; - mHttpMetricsHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); + mHttpMetricsHeaders->append("Content-Type", "application/llsd+xml"); mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_TEXTURE); } @@ -4050,7 +4050,7 @@ void LLTextureFetchDebugger::init() if (! mHttpHeaders) { mHttpHeaders = new LLCore::HttpHeaders; - mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C); + mHttpHeaders->append("Accept", "image/x-j2c"); } } -- cgit v1.2.3 From d6cbcd591aea32357d50b266efe8a95754302cbf Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 20 Jun 2013 19:18:39 -0400 Subject: SH-4257 Preparation for a new cap grant: GetMesh2 Mesh repo is using three policy classes now: one for large objects, one for GetMesh2 regions, one for GetMesh regions. It's also detecting the presence of the cap and using the correct class. Class initialization cleaned up significantly in llappcorehttp using data-directed code. Pulled in the changes to HttpHeader done for sunshine-internal then did a refactoring pass on the header callback which now uses a unified approach to clean up and deliver header information to all interested parties. Added support for using Retry-After header information on 503 retries. --- indra/newview/llappcorehttp.cpp | 10 ++++++- indra/newview/llappcorehttp.h | 3 ++- indra/newview/llmeshrepository.cpp | 55 +++++++++++++++++++++++++------------- indra/newview/llmeshrepository.h | 8 +++--- 4 files changed, 53 insertions(+), 23 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index b601b31d21..2467c02d4d 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -72,10 +72,16 @@ void LLAppCoreHttp::init() "texture fetch" }, { - AP_MESH, 8, 1, 32, 4, + // *FIXME: Should become 32, 1, 32, 1 before release + AP_MESH1, 8, 1, 32, 4, "MeshMaxConcurrentRequests", "mesh fetch" }, + { + AP_MESH2, 8, 1, 32, 4, + "MeshMaxConcurrentRequests", + "mesh2 fetch" + }, { AP_LARGE_MESH, 2, 1, 8, 1, "", @@ -171,6 +177,8 @@ void LLAppCoreHttp::init() } // Set it and report + // *TODO: These are intended to be per-host limits when we can + // support that in llcorehttp/libcurl. LLCore::HttpStatus status; status = LLCore::HttpRequest::setPolicyClassOption(mPolicies[policy], LLCore::HttpRequest::CP_CONNECTION_LIMIT, diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h index 532e1f5cb0..4a14c35966 100755 --- a/indra/newview/llappcorehttp.h +++ b/indra/newview/llappcorehttp.h @@ -47,7 +47,8 @@ public: { AP_DEFAULT, AP_TEXTURE, - AP_MESH, + AP_MESH1, + AP_MESH2, AP_LARGE_MESH, AP_UPLOADS, AP_COUNT // Must be last diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 221a797fc7..1cda0b6a71 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -560,6 +560,7 @@ LLMeshRepoThread::LLMeshRepoThread() mHttpLargeOptions(NULL), mHttpHeaders(NULL), mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpLegacyPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpPriority(0), mHttpGetCount(0U), @@ -574,8 +575,9 @@ LLMeshRepoThread::LLMeshRepoThread() mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT); mHttpHeaders = new LLCore::HttpHeaders; mHttpHeaders->append("Accept", "application/vnd.ll.mesh"); - mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH); - mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_LARGE_MESH); + mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH2); + mHttpLegacyPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH1); + mHttpLargePolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_LARGE_MESH); } @@ -795,13 +797,22 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } //static -std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) +std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id, int * cap_version) { + int version(1); std::string http_url; if (gAgent.getRegion()) { - http_url = gMeshRepo.mGetMeshCapability; + if (! gMeshRepo.mGetMesh2Capability.empty()) + { + version = 2; + http_url = gMeshRepo.mGetMesh2Capability; + } + else + { + http_url = gMeshRepo.mGetMeshCapability; + } } if (!http_url.empty()) @@ -814,20 +825,22 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) llwarns << "Current region does not have GetMesh capability! Cannot load " << mesh_id << ".mesh" << llendl; } + *cap_version = version; return http_url; } // May only be called by repo thread -LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, - size_t offset, - size_t len, +LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int cap_version, + size_t offset, size_t len, LLCore::HttpHandler * handler) { LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); if (len < LARGE_MESH_FETCH_THRESHOLD) { - handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass, + handle = mHttpRequest->requestGetByteRange((2 == cap_version + ? mHttpPolicyClass + : mHttpLegacyPolicyClass), mHttpPriority, url, offset, @@ -911,12 +924,13 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - std::string http_url = constructUrl(mesh_id); + int cap_version(1); + std::string http_url = constructUrl(mesh_id, &cap_version); if (!http_url.empty()) { LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size); // LL_WARNS("Mesh") << "MESH: Issuing Skin Info Request" << LL_ENDL; - LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); + LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { // *TODO: Better error message @@ -1000,12 +1014,13 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - std::string http_url = constructUrl(mesh_id); + int cap_version(1); + std::string http_url = constructUrl(mesh_id, &cap_version); if (!http_url.empty()) { LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size); // LL_WARNS("Mesh") << "MESH: Issuing Decomp Request" << LL_ENDL; - LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); + LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { // *TODO: Better error message @@ -1088,12 +1103,13 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - std::string http_url = constructUrl(mesh_id); + int cap_version(1); + std::string http_url = constructUrl(mesh_id, &cap_version); if (!http_url.empty()) { LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size); // LL_WARNS("Mesh") << "MESH: Issuing Physics Shape Request" << LL_ENDL; - LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); + LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { // *TODO: Better error message @@ -1177,7 +1193,8 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c //either cache entry doesn't exist or is corrupt, request header from simulator bool retval = true ; - std::string http_url = constructUrl(mesh_params.getSculptID()); + int cap_version(1); + std::string http_url = constructUrl(mesh_params.getSculptID(), &cap_version); if (!http_url.empty()) { //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits @@ -1186,7 +1203,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params); // LL_WARNS("Mesh") << "MESH: Issuing Request" << LL_ENDL; - LLCore::HttpHandle handle = getByteRange(http_url, 0, MESH_HEADER_SIZE, handler); + LLCore::HttpHandle handle = getByteRange(http_url, cap_version, 0, MESH_HEADER_SIZE, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { // *TODO: Better error message @@ -1261,12 +1278,13 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, } //reading from VFS failed for whatever reason, fetch from sim - std::string http_url = constructUrl(mesh_id); + int cap_version(1); + std::string http_url = constructUrl(mesh_id, &cap_version); if (!http_url.empty()) { LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size); // LL_WARNS("Mesh") << "MESH: Issuing LOD Request" << LL_ENDL; - LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); + LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { // *TODO: Better error message @@ -2653,6 +2671,7 @@ void LLMeshRepository::notifyLoadedMeshes() { region_name = gAgent.getRegion()->getName(); mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh"); + mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2"); } } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 0dca29e7d4..74690e5a2a 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -328,13 +328,14 @@ public: LLCore::HttpOptions * mHttpLargeOptions; LLCore::HttpHeaders * mHttpHeaders; LLCore::HttpRequest::policy_t mHttpPolicyClass; + LLCore::HttpRequest::policy_t mHttpLegacyPolicyClass; LLCore::HttpRequest::policy_t mHttpLargePolicyClass; LLCore::HttpRequest::priority_t mHttpPriority; typedef std::set http_request_set; http_request_set mHttpRequestSet; // Outstanding HTTP requests - static std::string constructUrl(LLUUID mesh_id); + static std::string constructUrl(LLUUID mesh_id, int * cap_version); LLMeshRepoThread(); ~LLMeshRepoThread(); @@ -384,7 +385,8 @@ private: // or dispose of handler. // // Threads: Repo thread only - LLCore::HttpHandle getByteRange(const std::string & url, size_t offset, size_t len, + LLCore::HttpHandle getByteRange(const std::string & url, int cap_version, + size_t offset, size_t len, LLCore::HttpHandler * handler); private: @@ -595,7 +597,7 @@ public: void updateInventory(inventory_data data); std::string mGetMeshCapability; - + std::string mGetMesh2Capability; }; extern LLMeshRepository gMeshRepo; -- cgit v1.2.3 From 2cdddab384ce0bf4f6786a3fee08bea3f467e7c9 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 27 Jun 2013 13:55:05 -0400 Subject: SH-4310/BUG-2810/MAINT-2794 Better status checking and error logging in Mesh code. Pay correct attention to status codes coming back from services. Generate better and consistent error messages when problems arise. There's more to do in error handling, need a way to cleanly fail all request types, only have that for LOD at this point. Do better keeping the HTTP pipeline between the low and high water marks. This was made challenging because the outer most code couldn't really see what's going on internally (whose actions are delayed in a worker thread). More to do here, the debug-like requests don't honor limits, that will come later. Made retry counts available from llcorehttp which can be used by the throttle-anticipating logic to advance the count. It helps but it reinforces the coupling between viewer and server which I do not like. --- indra/newview/llmeshrepository.cpp | 354 ++++++++++++++++++++++++------------- indra/newview/llmeshrepository.h | 6 +- 2 files changed, 236 insertions(+), 124 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 1cda0b6a71..a86f0e86f1 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -78,10 +78,12 @@ LLMeshRepository gMeshRepo; -const S32 MESH_HEADER_SIZE = 4096; +const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space const U32 MAX_MESH_REQUESTS_PER_SECOND = 100; const S32 REQUEST_HIGH_WATER_MIN = 32; +const S32 REQUEST_HIGH_WATER_MAX = 80; const S32 REQUEST_LOW_WATER_MIN = 16; +const S32 REQUEST_LOW_WATER_MAX = 40; const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes to narrow/slow queue const long LARGE_MESH_XFER_TIMEOUT = 240L; // Seconds to complete xfer @@ -122,6 +124,7 @@ const std::string header_lod[] = "medium_lod", "high_lod" }; +const char * const LOG_MESH = "Mesh"; // Static data and functions to measure mesh load // time metrics for a new region scene. @@ -211,12 +214,21 @@ void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res, } } -S32 LLMeshRepoThread::sActiveHeaderRequests = 0; -S32 LLMeshRepoThread::sActiveLODRequests = 0; +volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0; +volatile S32 LLMeshRepoThread::sActiveLODRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; S32 LLMeshRepoThread::sRequestLowWater = REQUEST_LOW_WATER_MIN; S32 LLMeshRepoThread::sRequestHighWater = REQUEST_HIGH_WATER_MIN; +// Base handler class for all mesh users of llcorehttp. +// This is roughly equivalent to a Responder class in +// traditional LL code. The base is going to perform +// common response/data handling in the inherited +// onCompleted() method. Derived classes, one for each +// type of HTTP action, define processData() and +// processFailure() methods to customize handling and +// error messages. +// class LLMeshHandlerBase : public LLCore::HttpHandler { public: @@ -225,8 +237,10 @@ public: mMeshParams(), mProcessed(false), mHttpHandle(LLCORE_HTTP_HANDLE_INVALID) - {} - virtual ~LLMeshHandlerBase(); + {} + + virtual ~LLMeshHandlerBase() + {} protected: LLMeshHandlerBase(const LLMeshHandlerBase &); // Not defined @@ -244,6 +258,9 @@ public: }; +// Subclass for header fetches. +// +// Thread: repo class LLMeshHeaderHandler : public LLMeshHandlerBase { public: @@ -265,6 +282,9 @@ public: }; +// Subclass for LOD fetches. +// +// Thread: repo class LLMeshLODHandler : public LLMeshHandlerBase { public: @@ -294,6 +314,9 @@ public: }; +// Subclass for skin info fetches. +// +// Thread: repo class LLMeshSkinInfoHandler : public LLMeshHandlerBase { public: @@ -320,6 +343,9 @@ public: }; +// Subclass for decomposition fetches. +// +// Thread: repo class LLMeshDecompositionHandler : public LLMeshHandlerBase { public: @@ -346,6 +372,9 @@ public: }; +// Subclass for physics shape fetches. +// +// Thread: repo class LLMeshPhysicsShapeHandler : public LLMeshHandlerBase { public: @@ -371,6 +400,7 @@ public: U32 mOffset; }; + void log_upload_error(S32 status, const LLSD& content, std::string stage, std::string model_name) { // Add notification popup. @@ -555,6 +585,7 @@ public: LLMeshRepoThread::LLMeshRepoThread() : LLThread("mesh repo"), mWaiting(false), + mHttpRetries(0U), mHttpRequest(NULL), mHttpOptions(NULL), mHttpLargeOptions(NULL), @@ -583,9 +614,9 @@ LLMeshRepoThread::LLMeshRepoThread() LLMeshRepoThread::~LLMeshRepoThread() { - LL_INFOS("Mesh") << "Small GETs issued: " - << mHttpGetCount << ", Large GETs issued: " - << mHttpLargeGetCount << LL_ENDL; + LL_INFOS(LOG_MESH) << "Small GETs issued: " << mHttpGetCount + << ", Large GETs issued: " << mHttpLargeGetCount + << LL_ENDL; for (http_request_set::iterator iter(mHttpRequestSet.begin()); iter != mHttpRequestSet.end(); @@ -631,10 +662,12 @@ void LLMeshRepoThread::run() { if (! mHttpRequestSet.empty()) { + // Dispatch all HttpHandler notifications mHttpRequest->update(0L); } mWaiting = true; + ms_sleep(5); mSignal->wait(); mWaiting = false; @@ -648,42 +681,49 @@ void LLMeshRepoThread::run() last_hundred = gFrameTimeSeconds; count = 0; } - + else + { + count += mHttpRetries; + } + mHttpRetries = 0U; + // NOTE: throttling intentionally favors LOD requests over header requests - while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND) + while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater) { - if (mMutex) + if (! mMutex) + { + break; + } + mMutex->lock(); + LODRequest req = mLODReqQ.front(); + mLODReqQ.pop(); + LLMeshRepository::sLODProcessing--; + mMutex->unlock(); + if (!fetchMeshLOD(req.mMeshParams, req.mLOD, count))//failed, resubmit { mMutex->lock(); - LODRequest req = mLODReqQ.front(); - mLODReqQ.pop(); - LLMeshRepository::sLODProcessing--; + mLODReqQ.push(req) ; + ++LLMeshRepository::sLODProcessing; mMutex->unlock(); - if (!fetchMeshLOD(req.mMeshParams, req.mLOD, count))//failed, resubmit - { - mMutex->lock(); - mLODReqQ.push(req) ; - ++LLMeshRepository::sLODProcessing; - mMutex->unlock(); - } } } - while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND) + while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater) { - if (mMutex) + if (! mMutex) + { + break; + } + mMutex->lock(); + HeaderRequest req = mHeaderReqQ.front(); + mHeaderReqQ.pop(); + mMutex->unlock(); + if (!fetchMeshHeader(req.mMeshParams, count))//failed, resubmit { mMutex->lock(); - HeaderRequest req = mHeaderReqQ.front(); - mHeaderReqQ.pop(); + mHeaderReqQ.push(req) ; mMutex->unlock(); - if (!fetchMeshHeader(req.mMeshParams, count))//failed, resubmit - { - mMutex->lock(); - mHeaderReqQ.push(req) ; - mMutex->unlock(); - } } } @@ -697,7 +737,7 @@ void LLMeshRepoThread::run() incomplete.insert(mesh_id); } } - mSkinRequests = incomplete; + mSkinRequests.swap(incomplete); } { //mDecompositionRequests is protected by mSignal @@ -710,7 +750,7 @@ void LLMeshRepoThread::run() incomplete.insert(mesh_id); } } - mDecompositionRequests = incomplete; + mDecompositionRequests.swap(incomplete); } { //mPhysicsShapeRequests is protected by mSignal @@ -723,7 +763,7 @@ void LLMeshRepoThread::run() incomplete.insert(mesh_id); } } - mPhysicsShapeRequests = incomplete; + mPhysicsShapeRequests.swap(incomplete); } } } @@ -796,6 +836,10 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } } +// Constructs a Cap URL for the mesh. Prefers a GetMesh2 cap +// over a GetMesh cap and returns what it finds to the caller +// as an int ([1..2]). +// //static std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id, int * cap_version) { @@ -822,14 +866,26 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id, int * cap_version) } else { - llwarns << "Current region does not have GetMesh capability! Cannot load " << mesh_id << ".mesh" << llendl; + LL_WARNS_ONCE(LOG_MESH) << "Current region does not have GetMesh capability! Cannot load " + << mesh_id << ".mesh" << LL_ENDL; } *cap_version = version; return http_url; } -// May only be called by repo thread +// Issue an HTTP GET request with byte range using the right +// policy class. Large requests go to the large request class. +// If the current region supports GetMesh2, we prefer that for +// smaller requests otherwise we try to use the traditional +// GetMesh capability and connection concurrency. +// +// @return Valid handle or LLCORE_HTTP_HANDLE_INVALID. +// If the latter, actual status is found in +// mHttpStatus member which is valid until the +// next call to this method. +// +// Thread: repo LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int cap_version, size_t offset, size_t len, LLCore::HttpHandler * handler) @@ -862,6 +918,11 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c handler); ++mHttpLargeGetCount; } + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + // Something went wrong, capture the error code for caller. + mHttpStatus = mHttpRequest->getStatus(); + } return handle; } @@ -929,12 +990,13 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) if (!http_url.empty()) { LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size); - // LL_WARNS("Mesh") << "MESH: Issuing Skin Info Request" << LL_ENDL; LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { - // *TODO: Better error message - llwarns << "HTTP GET request failed for mesh " << mID << llendl; + LL_WARNS(LOG_MESH) << "HTTP GET request failed for skin info on mesh " << mID + << ". Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toHex() << ")" + << LL_ENDL; delete handler; ret = false; } @@ -1019,12 +1081,13 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) if (!http_url.empty()) { LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size); - // LL_WARNS("Mesh") << "MESH: Issuing Decomp Request" << LL_ENDL; LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { - // *TODO: Better error message - llwarns << "HTTP GET request failed for decomposition mesh " << mID << llendl; + LL_WARNS(LOG_MESH) << "HTTP GET request failed for decomposition mesh " << mID + << ". Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toHex() << ")" + << LL_ENDL; delete handler; ret = false; } @@ -1108,12 +1171,13 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) if (!http_url.empty()) { LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size); - // LL_WARNS("Mesh") << "MESH: Issuing Physics Shape Request" << LL_ENDL; LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { - // *TODO: Better error message - llwarns << "HTTP GET request failed for physics shape mesh " << mID << llendl; + LL_WARNS(LOG_MESH) << "HTTP GET request failed for physics shape on mesh " << mID + << ". Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toHex() << ")" + << LL_ENDL; delete handler; ret = false; } @@ -1192,7 +1256,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c } //either cache entry doesn't exist or is corrupt, request header from simulator - bool retval = true ; + bool retval = true; int cap_version(1); std::string http_url = constructUrl(mesh_params.getSculptID(), &cap_version); if (!http_url.empty()) @@ -1202,12 +1266,13 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c //NOTE -- this will break of headers ever exceed 4KB LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params); - // LL_WARNS("Mesh") << "MESH: Issuing Request" << LL_ENDL; LLCore::HttpHandle handle = getByteRange(http_url, cap_version, 0, MESH_HEADER_SIZE, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { - // *TODO: Better error message - llwarns << "HTTP GET request failed for mesh " << mID << llendl; + LL_WARNS(LOG_MESH) << "HTTP GET request failed for mesh header " << mID + << ". Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toHex() << ")" + << LL_ENDL; delete handler; retval = false; } @@ -1216,8 +1281,8 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); ++LLMeshRepository::sHTTPRequestCount; + ++count; } - count++; } return retval; @@ -1283,12 +1348,13 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, if (!http_url.empty()) { LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size); - // LL_WARNS("Mesh") << "MESH: Issuing LOD Request" << LL_ENDL; LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { - // *TODO: Better error message - llwarns << "HTTP GET request failed for LOD mesh " << mID << llendl; + LL_WARNS(LOG_MESH) << "HTTP GET request failed for LOD on mesh " << mID + << ". Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toHex() << ")" + << LL_ENDL; delete handler; retval = false; } @@ -1297,8 +1363,8 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); ++LLMeshRepository::sHTTPRequestCount; + ++count; } - count++; } else { @@ -1320,6 +1386,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) { + const LLUUID mesh_id = mesh_params.getSculptID(); LLSD header; U32 header_size = 0; @@ -1340,7 +1407,8 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat if (!LLSDSerialize::fromBinary(header, stream, data_size)) { - llwarns << "Mesh header parse error. Not a valid mesh asset!" << llendl; + LL_WARNS(LOG_MESH) << "Mesh header parse error. Not a valid mesh asset! ID: " << mesh_id + << LL_ENDL; return false; } @@ -1348,13 +1416,12 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat } else { - llinfos - << "Marking header as non-existent, will not retry." << llendl; + LL_INFOS(LOG_MESH) << "Non-positive data size. Marking header as non-existent, will not retry. ID: " << mesh_id + << LL_ENDL; header["404"] = 1; } { - LLUUID mesh_id = mesh_params.getSculptID(); { LLMutexLock lock(mHeaderMutex); @@ -1416,7 +1483,8 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat if (!unzip_llsd(skin, stream, data_size)) { - llwarns << "Mesh skin info parse error. Not a valid mesh asset!" << llendl; + LL_WARNS(LOG_MESH) << "Mesh skin info parse error. Not a valid mesh asset! ID: " << mesh_id + << LL_ENDL; return false; } } @@ -1444,7 +1512,8 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3 if (!unzip_llsd(decomp, stream, data_size)) { - llwarns << "Mesh decomposition parse error. Not a valid mesh asset!" << llendl; + LL_WARNS(LOG_MESH) << "Mesh decomposition parse error. Not a valid mesh asset! ID: " << mesh_id + << LL_ENDL; return false; } } @@ -1860,7 +1929,8 @@ void LLMeshUploadThread::doWholeModelUpload() if (mWholeModelUploadURL.empty()) { - llinfos << "unable to upload, fee request failed" << llendl; + LL_WARNS(LOG_MESH) << "Missing mesh upload capability, unable to upload, fee request failed." + << LL_ENDL; } else { @@ -1941,6 +2011,12 @@ void LLMeshRepoThread::notifyLoadedMeshes() return; } + if (!mLoadedQ.empty() || !mUnavailableQ.empty()) + { + // Ping time-to-load metrics for mesh download operations. + LLMeshRepository::metricsProgress(0); + } + while (!mLoadedQ.empty()) { mMutex->lock(); @@ -2058,12 +2134,17 @@ void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header) } -LLMeshHandlerBase::~LLMeshHandlerBase() -{} - void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { mProcessed = true; + + // Accumulate retries, we'll use these to offset the HTTP + // count and maybe hold to a throttle better. + unsigned int retries(0U); + response->getRetries(NULL, &retries); + gMeshRepo.mThread->mHttpRetries += retries; + LLMeshRepository::sHTTPRetryCount += retries; + LLCore::HttpStatus status(response->getStatus()); if (! status) { @@ -2090,7 +2171,7 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo // that support BufferArray directly. data = new U8[data_size]; body->read(0, (char *) data, data_size); - LLMeshRepository::sBytesReceived += llmin(data_size, MESH_HEADER_SIZE); + LLMeshRepository::sBytesReceived += data_size; } processData(body, data, data_size); @@ -2100,7 +2181,6 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo // Release handler gMeshRepo.mThread->mHttpRequestSet.erase(this); - delete this; // Must be last statement } @@ -2112,8 +2192,7 @@ LLMeshHeaderHandler::~LLMeshHeaderHandler() if (! mProcessed) { // something went wrong, retry - llwarns << "Timeout or service unavailable, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; + LL_WARNS(LOG_MESH) << "Mesh header fetch canceled unexpectedly, retrying." << LL_ENDL; LLMeshRepoThread::HeaderRequest req(mMeshParams); LLMutexLock lock(gMeshRepo.mThread->mMutex); gMeshRepo.mThread->mHeaderReqQ.push(req); @@ -2124,36 +2203,39 @@ LLMeshHeaderHandler::~LLMeshHeaderHandler() void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) { - LL_WARNS("Mesh") << "MESH: Processing Failure" << LL_ENDL; if (is_retryable(status)) { - llwarns << "Timeout or service unavailable, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; + LL_WARNS(LOG_MESH) << "Error during mesh header handling. Reason: " << status.toString() + << " (" << status.toHex() << "). Retrying." + << LL_ENDL; LLMeshRepoThread::HeaderRequest req(mMeshParams); LLMutexLock lock(gMeshRepo.mThread->mMutex); gMeshRepo.mThread->mHeaderReqQ.push(req); } else { - // *TODO: better error message - llwarns << "Unhandled status." << llendl; + // *TODO: Mark mesh unavailable + LL_WARNS(LOG_MESH) << "Error during mesh header handling. Reason: " << status.toString() + << " (" << status.toHex() << "). Not retrying." + << LL_ENDL; } } void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) { - // LL_WARNS("Mesh") << "MESH: Processing Data" << LL_ENDL; + LLUUID mesh_id = mMeshParams.getSculptID(); bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); llassert(success); if (! success) { + // *TODO: Mark mesh unavailable // *TODO: Get real reason for parse failure here - llwarns << "Unable to parse mesh header: " << llendl; + LL_WARNS(LOG_MESH) << "Unable to parse mesh header. ID: " << mesh_id + << LL_ENDL; } else if (data && data_size > 0) { // header was successfully retrieved from sim, cache in vfs - LLUUID mesh_id = mMeshParams.getSculptID(); LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id]; S32 version = header["version"].asInteger(); @@ -2192,15 +2274,14 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 // zero out the rest of the file U8 block[MESH_HEADER_SIZE]; - memset(block, 0, MESH_HEADER_SIZE); + memset(block, 0, sizeof(block)); - while (bytes-file.tell() > MESH_HEADER_SIZE) + while (bytes-file.tell() > sizeof(block)) { - file.write(block, MESH_HEADER_SIZE); + file.write(block, sizeof(block)); } S32 remaining = bytes-file.tell(); - if (remaining > 0) { file.write(block, remaining); @@ -2216,8 +2297,7 @@ LLMeshLODHandler::~LLMeshLODHandler() { if (! mProcessed) { - llwarns << "Killed without being processed, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; + LL_WARNS(LOG_MESH) << "Mesh LOD fetch canceled unexpectedly, retrying." << LL_ENDL; gMeshRepo.mThread->lockAndLoadMeshLOD(mMeshParams, mLOD); } LLMeshRepoThread::decActiveLODRequests(); @@ -2228,15 +2308,21 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status) { if (is_retryable(status)) { - llwarns << "Timeout or service unavailable, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; - // *FIXME: Is this safe? Does this need locking? - gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD); + LL_WARNS(LOG_MESH) << "Error during mesh header handling. Reason: " << status.toString() + << " (" << status.toHex() << "). Retrying." + << LL_ENDL; + { + LLMutexLock lock(gMeshRepo.mThread->mMutex); + + gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD); + } } else { - // *TODO: better error message - llwarns << "Unhandled status." << llendl; + // *TODO: Mark mesh unavailable + LL_WARNS(LOG_MESH) << "Error during mesh LOD handling. Reason: " << status.toString() + << " (" << status.toHex() << "). Not retrying." + << LL_ENDL; } } @@ -2257,6 +2343,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 da LLMeshRepository::sCacheBytesWritten += size; } } + // *TODO: Mark mesh unavailable on error } LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler() @@ -2268,15 +2355,21 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) { if (is_retryable(status)) { - llwarns << "Timeout or service unavailable, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; - // *FIXME: Is this safe? Does this need locking? - gMeshRepo.mThread->loadMeshSkinInfo(mMeshID); + LL_WARNS(LOG_MESH) << "Error during mesh skin info handling. Reason: " << status.toString() + << " (" << status.toHex() << "). Retrying." + << LL_ENDL; + { + LLMutexLock lock(gMeshRepo.mThread->mMutex); + + gMeshRepo.mThread->loadMeshSkinInfo(mMeshID); + } } else { - // *TODO: better error message - llwarns << "Unhandled status." << llendl; + // *TODO: Mark mesh unavailable on error + LL_WARNS(LOG_MESH) << "Error during mesh skin info handling. Reason: " << status.toString() + << " (" << status.toHex() << "). Not retrying." + << LL_ENDL; } } @@ -2297,6 +2390,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S file.write(data, size); } } + // *TODO: Mark mesh unavailable on error } LLMeshDecompositionHandler::~LLMeshDecompositionHandler() @@ -2308,15 +2402,21 @@ void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status) { if (is_retryable(status)) { - llwarns << "Timeout or service unavailable, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; - // *FIXME: Is this safe? Does this need locking? - gMeshRepo.mThread->loadMeshDecomposition(mMeshID); + LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling. Reason: " << status.toString() + << " (" << status.toHex() << "). Retrying." + << LL_ENDL; + { + LLMutexLock lock(gMeshRepo.mThread->mMutex); + + gMeshRepo.mThread->loadMeshDecomposition(mMeshID); + } } else { - // *TODO: better error message - llwarns << "Unhandled status." << llendl; + // *TODO: Mark mesh unavailable on error + LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling. Reason: " << status.toString() + << " (" << status.toHex() << "). Not retrying." + << LL_ENDL; } } @@ -2337,6 +2437,7 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * da file.write(data, size); } } + // *TODO: Mark mesh unavailable on error } LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler() @@ -2348,15 +2449,21 @@ void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status) { if (is_retryable(status)) { - llwarns << "Timeout or service unavailable, retrying." << llendl; - LLMeshRepository::sHTTPRetryCount++; - // *FIXME: Is this safe? Does this need locking? - gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID); + LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling. Reason: " << status.toString() + << " (" << status.toHex() << "). Retrying." + << LL_ENDL; + { + LLMutexLock lock(gMeshRepo.mThread->mMutex); + + gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID); + } } else { - // *TODO: better error message - llwarns << "Unhandled status." << llendl; + // *TODO: Mark mesh unavailable on error + LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling. Reason: " << status.toString() + << " (" << status.toHex() << "). Not retrying." + << LL_ENDL; } } @@ -2377,6 +2484,7 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * dat file.write(data, size); } } + // *TODO: Mark mesh unavailable on error } LLMeshRepository::LLMeshRepository() @@ -2574,12 +2682,15 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para void LLMeshRepository::notifyLoadedMeshes() { //called from main thread // *FIXME: Scaling down the setting by a factor of 4 for now to reflect - // target goal. May want to rename the setting before release. + // target goal. May want to rename the setting before release. Also + // want/need to get these in a coordinated fashion from llappcorehttp. LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests") / 4; - LLMeshRepoThread::sRequestHighWater = llmax(50 * S32(LLMeshRepoThread::sMaxConcurrentRequests), - REQUEST_HIGH_WATER_MIN); - LLMeshRepoThread::sRequestLowWater = llmax(LLMeshRepoThread::sRequestLowWater / 2, - REQUEST_LOW_WATER_MIN); + LLMeshRepoThread::sRequestHighWater = llclamp(10 * S32(LLMeshRepoThread::sMaxConcurrentRequests), + REQUEST_HIGH_WATER_MIN, + REQUEST_HIGH_WATER_MAX); + LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2, + REQUEST_LOW_WATER_MIN, + REQUEST_LOW_WATER_MAX); //clean up completed upload threads for (std::vector::iterator iter = mUploads.begin(); iter != mUploads.end(); ) @@ -2672,6 +2783,10 @@ void LLMeshRepository::notifyLoadedMeshes() region_name = gAgent.getRegion()->getName(); mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh"); mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2"); + LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name + << "', GetMesh2: " << mGetMesh2Capability + << ", GetMesh: " << mGetMeshCapability + << LL_ENDL; } } @@ -2810,9 +2925,6 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume) { //called from main thread - // Manage time-to-load metrics for mesh download operations. - metricsProgress(0); - S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail()); //get list of objects waiting to be notified this mesh is loaded @@ -2823,7 +2935,8 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol //make sure target volume is still valid if (volume->getNumVolumeFaces() <= 0) { - llwarns << "Mesh loading returned empty volume." << llendl; + LL_WARNS(LOG_MESH) << "Mesh loading returned empty volume. ID: " << mesh_params.getSculptID() + << LL_ENDL; } { //update system volume @@ -2836,7 +2949,8 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol } else { - llwarns << "Couldn't find system volume for given mesh." << llendl; + LL_WARNS(LOG_MESH) << "Couldn't find system volume for mesh " << mesh_params.getSculptID() + << LL_ENDL; } } @@ -2856,9 +2970,6 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod) { //called from main thread - // Manage time-to-load metrics for mesh download operations. - metricsProgress(0); - //get list of objects waiting to be notified this mesh is loaded mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params); @@ -3029,7 +3140,7 @@ LLSD& LLMeshRepoThread::getMeshHeader(const LLUUID& mesh_id) void LLMeshRepository::uploadModel(std::vector& data, LLVector3& scale, bool upload_textures, - bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload, + bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload, LLHandle fee_observer, LLHandle upload_observer) { LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, upload_skin, upload_joints, upload_url, @@ -3058,7 +3169,6 @@ S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) } return -1; - } void LLMeshUploadThread::decomposeMeshMatrix(LLMatrix4& transformation, @@ -3880,7 +3990,7 @@ void LLMeshRepository::metricsUpdate() metrics["scope"] = "Login"; metrics["start"] = started; metrics["stop"] = stopped; - metrics["downloads"] = LLSD::Integer(total_count); + metrics["fetches"] = LLSD::Integer(total_count); metrics["teleports"] = LLSD::Integer(metrics_teleport_start_count); metrics["user_cpu"] = double(user_cpu) / 1.0e6; metrics["sys_cpu"] = double(sys_cpu) / 1.0e6; diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 74690e5a2a..e90ab4dd23 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -221,8 +221,8 @@ class LLMeshRepoThread : public LLThread { public: - static S32 sActiveHeaderRequests; - static S32 sActiveLODRequests; + volatile static S32 sActiveHeaderRequests; + volatile static S32 sActiveLODRequests; static U32 sMaxConcurrentRequests; static S32 sRequestLowWater; static S32 sRequestHighWater; @@ -323,6 +323,8 @@ public: pending_lod_map mPendingLOD; // llcorehttp library interface objects. + LLCore::HttpStatus mHttpStatus; + unsigned int mHttpRetries; LLCore::HttpRequest * mHttpRequest; LLCore::HttpOptions * mHttpOptions; LLCore::HttpOptions * mHttpLargeOptions; -- cgit v1.2.3 From 350e658348431e4b1f0e5038cf999d9722a699aa Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 27 Jun 2013 20:38:18 -0400 Subject: SH-4311 Get highwater limiting of requests into llhttpcore working. Fixed the logic and have it covering all five types of requests now with validation via an assert (when enabled). Should keep things working smoothly and avoid floods of 503s when in debug modes. Also started a round of file-level documentation detailing thread usage and mutex coverage. More to do, more to describe. But the high- water stuff is functioning correctly. --- indra/newview/llmeshrepository.cpp | 189 ++++++++++++++++++++++++++++++++++--- indra/newview/llmeshrepository.h | 6 +- 2 files changed, 177 insertions(+), 18 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index a86f0e86f1..aa10e64af8 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -76,9 +76,140 @@ #include + +// [ Disclaimer: this documentation isn't by one of the original authors +// but by someone coming through later and extracting intent and function. +// Some of this will be wrong so use judgement. ] +// +// Purpose +// +// The purpose of this module is to provide access between the viewer +// and the asset system as regards to mesh objects. +// +// * High-throughput download of mesh assets from servers while +// following best industry practices for network profile. +// * Reliable expensing and upload of new mesh assets. +// * Recovery and retry from errors when appropriate. +// * Decomposition of mesh assets for preview and uploads. +// * And most important: all of the above without exposing the +// main thread to stalls due to deep processing or thread +// locking actions. In particular, the following operations +// on LLMeshRepository are very averse to any stalls: +// * loadMesh +// * getMeshHeader (For structural details, see: +// http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format) +// * notifyLoadedMeshes +// +// Threads +// +// main Main rendering thread, very sensitive to locking and other stalls +// repo Overseeing worker thread associated with the LLMeshRepoThread class +// decom Worker thread for mesh decomposition requests +// core HTTP worker thread: does the work but doesn't intrude here +// uploadN 0-N temporary mesh upload threads +// +// Mutexes +// +// LLMeshRepository::mMeshMutex +// LLMeshRepoThread::mMutex +// LLMeshRepoThread::mHeaderMutex +// LLMeshRepoThread::mSignal (LLCondition) +// LLPhysicsDecomp::mSignal (LLCondition) +// LLPhysicsDecomp::mMutex +// LLMeshUploadThread::mMutex +// +// Mutex Order Rules +// +// 1. LLMeshRepoThread::mMutex before LLMeshRepoThread::mHeaderMutex +// 2. LLMeshRepository::mMeshMutex before LLMeshRepoThread::mMutex +// (There are more rules, haven't been extracted.) +// +// Data Member Access/Locking +// +// Description of how shared access to static and instance data +// members is performed. Each member is followed by the name of +// the mutex, if any, covering the data and then a list of data +// access models each of which is a triplet of the following form: +// +// {ro, wo, rw}.{main, repo, any}.{mutex, none} +// Type of access: read-only, write-only, read-write. +// Accessing thread or 'any' +// Relevant mutex held during access (several may be held) or 'none' +// +// A careful eye will notice some unsafe operations. Many of these +// have an alibi of some form. Several types of alibi are identified +// and listed here: +// +// [0] No alibi. Probably unsafe. +// [1] Single-writer, self-consistent readers. Old data must +// be tolerated by any reader but data will come true eventually. +// [2] Like [1] but provides a hint about thread state. These +// may be unsafe. +// [3] empty() check outside of lock. Can me made safish when +// done in double-check lock style. But this depends on +// std:: implementation and memory model. +// [4] Appears to be covered by a mutex but doesn't need one. +// [5] Read of a double-checked lock. +// +// So, in addition to documentation, take this as a to-do/review +// list and see if you can improve things. +// +// LLMeshRepository: +// +// sBytesReceived +// sHTTPRequestCount +// sHTTPRetryCount +// sLODPending +// sLODProcessing +// sCacheBytesRead +// sCacheBytesWritten +// mLoadingMeshes none rw.main.none, rw.main.mMeshMutex [4] +// mSkinMap none rw.main.none +// mDecompositionMap none rw.main.none +// mPendingRequests mMeshMutex [4] rw.main.mMeshMutex +// mLoadingSkins mMeshMutex [4] rw.main.mMeshMutex +// mPendingSkinRequests mMeshMutex [4] rw.main.mMeshMutex +// mLoadingDecompositions mMeshMutex [4] rw.main.mMeshMutex +// mPendingDecompositionRequests mMeshMutex [4] rw.main.mMeshMutex +// mLoadingPhysicsShapes mMeshMutex [4] rw.main.mMeshMutex +// mPendingPhysicsShapeRequests mMeshMutex [4] rw.main.mMeshMutex +// mUploads none rw.main.none (upload thread accessing objects) +// mUploadWaitList none rw.main.none (upload thread accessing objects) +// mInventoryQ mMeshMutex [4] rw.main.mMeshMutex, ro.main.none [5] +// mUploadErrorQ mMeshMutex rw.main.mMeshMutex, rw.any.mMeshMutex +// mGetMeshCapability none rw.main.none [0], ro.any.none +// mGetMesh2Capability none rw.main.none [0], ro.any.none +// +// LLMeshRepoThread: +// +// sActiveHeaderRequests mMutex rw.any.mMutex, ro.repo.none [1] +// sActiveLODRequests mMutex rw.any.mMutex, ro.repo.none [1] +// sMaxConcurrentRequests mMutex wo.main.none, ro.repo.none, ro.main.mMutex +// mWaiting mMutex rw.repo.none, ro.main.none [2] (race - hint) +// mMeshHeader mHeaderMutex rw.repo.mHeaderMutex, ro.main.mHeaderMutex, ro.main.none [0] +// mMeshHeaderSize mHeaderMutex rw.repo.mHeaderMutex +// mSkinRequests none rw.repo.none, rw.main.none [0] +// mSkinInfoQ none rw.repo.none, rw.main.none [0] +// mDecompositionRequests none rw.repo.none, rw.main.none [0] +// mPhysicsShapeRequests none rw.repo.none, rw.main.none [0] +// mDecompositionQ none rw.repo.none, rw.main.none [0] +// mHeaderReqQ mMutex ro.repo.none [3], rw.repo.mMutex, rw.any.mMutex +// mLODReqQ mMutex ro.repo.none [3], rw.repo.mMutex, rw.any.mMutex +// mUnavailableQ mMutex rw.repo.none [0], ro.main.none [3], rw.main.mMutex +// mLoadedQ mMutex rw.repo.mMutex, ro.main.none [3], rw.main.mMutex +// mPendingLOD mMutex rw.repo.mMutex, rw.any.mMutex +// +// LLPhysicsDecomp: +// +// mRequestQ +// mCurRequest +// mCompletedQ +// + + LLMeshRepository gMeshRepo; -const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space +const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space const U32 MAX_MESH_REQUESTS_PER_SECOND = 100; const S32 REQUEST_HIGH_WATER_MIN = 32; const S32 REQUEST_HIGH_WATER_MAX = 80; @@ -727,12 +858,21 @@ void LLMeshRepoThread::run() } } - { //mSkinRequests is protected by mSignal + // For the final three request lists, if we scan any part of one + // list, we scan the entire thing. This gets us through any requests + // which can be resolved in the cache. It also keeps the request + // set somewhat fresher otherwise items at the end of the set + // order will lose. Keep to the throttle enforcement and pay + // attention to the highwater level (enforced in each fetchXXX() + // method). + if (! mSkinRequests.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater) + { + // *FIXME: this really does need a lock as do the following ones std::set incomplete; for (std::set::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter) { LLUUID mesh_id = *iter; - if (!fetchMeshSkinInfo(mesh_id)) + if (!fetchMeshSkinInfo(mesh_id, count)) { incomplete.insert(mesh_id); } @@ -740,12 +880,13 @@ void LLMeshRepoThread::run() mSkinRequests.swap(incomplete); } - { //mDecompositionRequests is protected by mSignal + if (! mDecompositionRequests.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater) + { std::set incomplete; for (std::set::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter) { LLUUID mesh_id = *iter; - if (!fetchMeshDecomposition(mesh_id)) + if (!fetchMeshDecomposition(mesh_id, count)) { incomplete.insert(mesh_id); } @@ -753,18 +894,23 @@ void LLMeshRepoThread::run() mDecompositionRequests.swap(incomplete); } - { //mPhysicsShapeRequests is protected by mSignal + if (! mPhysicsShapeRequests.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater) + { std::set incomplete; for (std::set::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter) { LLUUID mesh_id = *iter; - if (!fetchMeshPhysicsShape(mesh_id)) + if (!fetchMeshPhysicsShape(mesh_id, count)) { incomplete.insert(mesh_id); } } mPhysicsShapeRequests.swap(incomplete); } + + // For dev purposes, a dynamic change could make this false + // and that shouldn't assert. + // llassert_always(mHttpRequestSet.size() <= sRequestHighWater); } } @@ -927,8 +1073,8 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c } -bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) -{ //protected by mMutex +bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count) +{ if (!mHeaderMutex) { @@ -985,6 +1131,10 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim + if (count >= MAX_MESH_REQUESTS_PER_SECOND || mHttpRequestSet.size() >= sRequestHighWater) + { + return false; + } int cap_version(1); std::string http_url = constructUrl(mesh_id, &cap_version); if (!http_url.empty()) @@ -1018,8 +1168,8 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) return ret; } -bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) -{ //protected by mMutex +bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id, U32& count) +{ if (!mHeaderMutex) { return false; @@ -1076,6 +1226,10 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim + if (count >= MAX_MESH_REQUESTS_PER_SECOND || mHttpRequestSet.size() >= sRequestHighWater) + { + return false; + } int cap_version(1); std::string http_url = constructUrl(mesh_id, &cap_version); if (!http_url.empty()) @@ -1109,8 +1263,8 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) return ret; } -bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) -{ //protected by mMutex +bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id, U32& count) +{ if (!mHeaderMutex) { return false; @@ -1166,6 +1320,10 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim + if (count >= MAX_MESH_REQUESTS_PER_SECOND || mHttpRequestSet.size() >= sRequestHighWater) + { + return false; + } int cap_version(1); std::string http_url = constructUrl(mesh_id, &cap_version); if (!http_url.empty()) @@ -1290,7 +1448,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c //return false if failed to get mesh lod. bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count) -{ //protected by mMutex +{ if (!mHeaderMutex) { return false; @@ -2249,7 +2407,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i) { // figure out how many bytes we'll need to reserve in the file - std::string lod_name = header_lod[i]; + const std::string & lod_name = header_lod[i]; lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger()); } @@ -3047,6 +3205,7 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id) std::set::iterator iter = mLoadingPhysicsShapes.find(mesh_id); if (iter == mLoadingPhysicsShapes.end()) { //no request pending for this skin info + // *FIXME: Nothing ever deletes entries, can't be right mLoadingPhysicsShapes.insert(mesh_id); mPendingPhysicsShapeRequests.push(mesh_id); } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index e90ab4dd23..9bf14a3715 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -365,15 +365,15 @@ public: //send request for skin info, returns true if header info exists // (should hold onto mesh_id and try again later if header info does not exist) - bool fetchMeshSkinInfo(const LLUUID& mesh_id); + bool fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count); //send request for decomposition, returns true if header info exists // (should hold onto mesh_id and try again later if header info does not exist) - bool fetchMeshDecomposition(const LLUUID& mesh_id); + bool fetchMeshDecomposition(const LLUUID& mesh_id, U32& count); //send request for PhysicsShape, returns true if header info exists // (should hold onto mesh_id and try again later if header info does not exist) - bool fetchMeshPhysicsShape(const LLUUID& mesh_id); + bool fetchMeshPhysicsShape(const LLUUID& mesh_id, U32& count); static void incActiveLODRequests(); static void decActiveLODRequests(); -- cgit v1.2.3 From 8c5518f275029094ed42979a0f30d8314afadc3d Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 28 Jun 2013 17:57:57 -0400 Subject: SH-4257 Prepare for new 'GetMesh2' capability. Needed to move the cap from response to region instance. --- indra/newview/llviewerregion.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index b8b53aa6e4..41f63c7ef6 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2010-2013, 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 @@ -1597,6 +1597,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("GetDisplayNames"); capabilityNames.append("GetMesh"); + capabilityNames.append("GetMesh2"); capabilityNames.append("GetObjectCost"); capabilityNames.append("GetObjectPhysicsData"); capabilityNames.append("GetTexture"); -- cgit v1.2.3 From a12d5d7c6d8d816512ec9fc1355b37ce38a89cce Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 28 Jun 2013 20:08:29 -0400 Subject: SH-4312 Clumsy configuration coordination between mesh and corehttp Taught llappcorehttp to register signals on the settings values that chagne behavior. Have initialization and settings changes sweep through settings and change them. Dynamic changes are tried but have no effect (produce a warning message) as dynamic settings still aren't supported but the plumbing is now connected. Just need to change llcorehttp. Bounced the 'teleport started' signal around and it ended up back where it started with some cleanup. This is making me less angry... --- indra/newview/llappcorehttp.cpp | 210 +++++++++++++++++++++++-------------- indra/newview/llappcorehttp.h | 9 +- indra/newview/llmeshrepository.cpp | 30 ++---- 3 files changed, 145 insertions(+), 104 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 2467c02d4d..70e349e33d 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -28,10 +28,52 @@ #include "llappcorehttp.h" +#include "llappviewer.h" #include "llviewercontrol.h" const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0); +static const struct +{ + LLAppCoreHttp::EAppPolicy mPolicy; + U32 mDefault; + U32 mMin; + U32 mMax; + U32 mDivisor; + std::string mKey; + const char * mUsage; +} init_data[] = // Default and dynamic values for classes +{ + { + LLAppCoreHttp::AP_TEXTURE, 8, 1, 12, 1, + "TextureFetchConcurrency", + "texture fetch" + }, + { + LLAppCoreHttp::AP_MESH1, 32, 1, 128, 1, + "MeshMaxConcurrentRequests", + "mesh fetch" + }, + { + LLAppCoreHttp::AP_MESH2, 8, 1, 32, 4, + "MeshMaxConcurrentRequests", + "mesh2 fetch" + }, + { + LLAppCoreHttp::AP_LARGE_MESH, 2, 1, 8, 1, + "", + "large mesh fetch" + }, + { + LLAppCoreHttp::AP_UPLOADS, 2, 1, 8, 1, + "", + "asset upload" + } +}; + +static void teleport_started(); +static void setting_changed(); + LLAppCoreHttp::LLAppCoreHttp() : mRequest(NULL), @@ -42,6 +84,7 @@ LLAppCoreHttp::LLAppCoreHttp() for (int i(0); i < LL_ARRAY_SIZE(mPolicies); ++i) { mPolicies[i] = LLCore::HttpRequest::DEFAULT_POLICY_ID; + mSettings[i] = 0U; } } @@ -55,45 +98,6 @@ LLAppCoreHttp::~LLAppCoreHttp() void LLAppCoreHttp::init() { - static const struct - { - EAppPolicy mPolicy; - U32 mDefault; - U32 mMin; - U32 mMax; - U32 mDivisor; - std::string mKey; - const char * mUsage; - } init_data[] = // Default and dynamic values for classes - { - { - AP_TEXTURE, 8, 1, 12, 1, - "TextureFetchConcurrency", - "texture fetch" - }, - { - // *FIXME: Should become 32, 1, 32, 1 before release - AP_MESH1, 8, 1, 32, 4, - "MeshMaxConcurrentRequests", - "mesh fetch" - }, - { - AP_MESH2, 8, 1, 32, 4, - "MeshMaxConcurrentRequests", - "mesh2 fetch" - }, - { - AP_LARGE_MESH, 2, 1, 8, 1, - "", - "large mesh fetch" - }, - { - AP_UPLOADS, 2, 1, 8, 1, - "", - "asset upload" - } - }; - LLCore::HttpStatus status = LLCore::HttpRequest::createService(); if (! status) { @@ -110,14 +114,12 @@ void LLAppCoreHttp::init() << LL_ENDL; } - // Establish HTTP Proxy. "LLProxy" is a special string which directs - // the code to use LLProxy::applyProxySettings() to establish any - // HTTP or SOCKS proxy for http operations. + // Establish HTTP Proxy, if desired. status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); if (! status) { - LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " << status.toString() - << LL_ENDL; + LL_WARNS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " << status.toString() + << LL_ENDL; } // Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy): @@ -137,7 +139,6 @@ void LLAppCoreHttp::init() mPolicies[AP_DEFAULT] = LLCore::HttpRequest::DEFAULT_POLICY_ID; // Setup additional policies based on table and some special rules - // *TODO: Make these configurations dynamic later for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) { const EAppPolicy policy(init_data[i].mPolicy); @@ -162,40 +163,10 @@ void LLAppCoreHttp::init() continue; } } - - // Get target connection concurrency value - U32 setting(init_data[i].mDefault); - if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey)) - { - U32 new_setting(gSavedSettings.getU32(init_data[i].mKey)); - if (new_setting) - { - // Treat zero settings as an ask for default - setting = new_setting / init_data[i].mDivisor; - setting = llclamp(setting, init_data[i].mMin, init_data[i].mMax); - } - } - - // Set it and report - // *TODO: These are intended to be per-host limits when we can - // support that in llcorehttp/libcurl. - LLCore::HttpStatus status; - status = LLCore::HttpRequest::setPolicyClassOption(mPolicies[policy], - LLCore::HttpRequest::CP_CONNECTION_LIMIT, - setting); - if (! status) - { - LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage - << " concurrency. Reason: " << status.toString() - << LL_ENDL; - } - else if (setting != init_data[i].mDefault) - { - LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage - << " concurrency. New value: " << setting - << LL_ENDL; - } } + + // Apply initial settings + refreshSettings(true); // Kick the thread status = LLCore::HttpRequest::startThread(); @@ -206,6 +177,30 @@ void LLAppCoreHttp::init() } mRequest = new LLCore::HttpRequest; + + // Register signals for settings and state changes + for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) + { + if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey)) + { + LLPointer cntrl_ptr = gSavedSettings.getControl(init_data[i].mKey); + if (cntrl_ptr.isNull()) + { + LL_WARNS("Init") << "Unable to set signal on global setting '" << init_data[i].mKey + << "'" << LL_ENDL; + } + else + { + mSettingsSignal[i] = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed)); + } + } + } +} + + +void setting_changed() +{ + LLAppViewer::instance()->getAppCoreHttp().refreshSettings(false); } @@ -248,6 +243,11 @@ void LLAppCoreHttp::cleanup() } } + for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) + { + mSettingsSignal[i].disconnect(); + } + delete mRequest; mRequest = NULL; @@ -260,6 +260,60 @@ void LLAppCoreHttp::cleanup() } } +void LLAppCoreHttp::refreshSettings(bool initial) +{ + for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) + { + const EAppPolicy policy(init_data[i].mPolicy); + + // Get target connection concurrency value + U32 setting(init_data[i].mDefault); + if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey)) + { + U32 new_setting(gSavedSettings.getU32(init_data[i].mKey)); + if (new_setting) + { + // Treat zero settings as an ask for default + setting = new_setting / init_data[i].mDivisor; + setting = llclamp(setting, init_data[i].mMin, init_data[i].mMax); + } + } + + if (! initial && setting == mSettings[policy]) + { + // Unchanged, try next setting + continue; + } + + // Set it and report + // *TODO: These are intended to be per-host limits when we can + // support that in llcorehttp/libcurl. + LLCore::HttpStatus status; + status = LLCore::HttpRequest::setPolicyClassOption(mPolicies[policy], + LLCore::HttpRequest::CP_CONNECTION_LIMIT, + setting); + if (! status) + { + LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage + << " concurrency. Reason: " << status.toString() + << LL_ENDL; + } + else + { + LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage + << " concurrency. New value: " << setting + << LL_ENDL; + mSettings[policy] = 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; + } + } + } +} + void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *) { diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h index 4a14c35966..6dc3bb2130 100755 --- a/indra/newview/llappcorehttp.h +++ b/indra/newview/llappcorehttp.h @@ -85,16 +85,21 @@ public: { return mPolicies[policy]; } + + // Apply initial or new settings from the environment. + void refreshSettings(bool initial); private: static const F64 MAX_THREAD_WAIT_TIME; private: - LLCore::HttpRequest * mRequest; + LLCore::HttpRequest * mRequest; // Request queue to issue shutdowns LLCore::HttpHandle mStopHandle; F64 mStopRequested; bool mStopped; - policy_t mPolicies[AP_COUNT]; + policy_t mPolicies[AP_COUNT]; // Policy class id for each connection set + U32 mSettings[AP_COUNT]; + boost::signals2::connection mSettingsSignal[AP_COUNT]; // Signals to global settings that affect us }; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index aa10e64af8..3af9ce7342 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -259,10 +259,9 @@ const char * const LOG_MESH = "Mesh"; // Static data and functions to measure mesh load // time metrics for a new region scene. -static bool metrics_inited(false); -static boost::signals2::connection metrics_teleport_connection; static unsigned int metrics_teleport_start_count(0); -static void metrics_teleport_started(); +boost::signals2::connection metrics_teleport_started_signal; +static void teleport_started(); static bool is_retryable(LLCore::HttpStatus status); //get the number of bytes resident in memory for given volume @@ -2667,29 +2666,18 @@ void LLMeshRepository::init() apr_sleep(100); } - + metrics_teleport_started_signal = LLViewerMessage::getInstance()->setTeleportStartedCallback(teleport_started); mThread = new LLMeshRepoThread(); mThread->start(); - if (! metrics_inited) - { - // Get teleport started signals to restart timings. - metrics_teleport_connection = LLViewerMessage::getInstance()-> - setTeleportStartedCallback(metrics_teleport_started); - metrics_inited = true; - } } void LLMeshRepository::shutdown() { llinfos << "Shutting down mesh repository." << llendl; - if (metrics_inited) - { - metrics_teleport_connection.disconnect(); - metrics_inited = false; - } + metrics_teleport_started_signal.disconnect(); for (U32 i = 0; i < mUploads.size(); ++i) { @@ -4109,6 +4097,7 @@ bool LLMeshRepository::meshRezEnabled() // static void LLMeshRepository::metricsStart() { + ++metrics_teleport_start_count; sQuiescentTimer.start(0); } @@ -4127,7 +4116,6 @@ void LLMeshRepository::metricsProgress(unsigned int this_count) if (first_start) { - ++metrics_teleport_start_count; metricsStart(); first_start = false; } @@ -4157,19 +4145,13 @@ void LLMeshRepository::metricsUpdate() } } -// Will use a request to start a teleport as a signal to -// restart a timing sequence. We don't get one of these -// for login so initial start is done above. -// // Threading: main thread only // static -void metrics_teleport_started() +void teleport_started() { LLMeshRepository::metricsStart(); - ++metrics_teleport_start_count; } - // This comes from an edit in viewer-cat. Unify this once that's // available everywhere. bool is_retryable(LLCore::HttpStatus status) -- cgit v1.2.3 From f42d17f6c1d206a3396d9b4629246c4778ae1ca8 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 29 Jun 2013 03:08:32 +0000 Subject: Orphaned declaration preventing compilation. --- indra/newview/llappcorehttp.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 70e349e33d..0c242e57db 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -71,7 +71,6 @@ static const struct } }; -static void teleport_started(); static void setting_changed(); -- cgit v1.2.3 From eff651cffca60f2b69f6c596a8e9aa9e1ab44d3c Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 12 Jul 2013 15:00:24 -0400 Subject: SH-4312 Configuration data between viewer and llcorehttp is clumsy. Much improved. Unified the global and class options into a single option list. Implemented static and dynamic setting paths as much as possible. Dynamic path does require packet/RPC but otherwise there's near unification. Dynamic modes can't get values back yet due to the response/notifier scheme but this doesn't bother me. Flatten global and class options into simpler struct-like entities. Setter/getter available on these when needed (external APIs) but code can otherwise fiddle directly when it knows what to do. Much duplicated options/state removed from HttpPolicy. Comments cleaned up. Threads better described and consistently mentioned in API docs. Integration test extended for 503 responses with Reply-After headers. --- indra/newview/llappcorehttp.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 0c242e57db..104debe023 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -105,8 +105,9 @@ void LLAppCoreHttp::init() } // Point to our certs or SSH/https: will fail on connect - status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, - gDirUtilp->getCAFile()); + status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE, + LLCore::HttpRequest::GLOBAL_POLICY_ID, + gDirUtilp->getCAFile(), NULL); if (! status) { LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " << status.toString() @@ -114,7 +115,9 @@ void LLAppCoreHttp::init() } // Establish HTTP Proxy, if desired. - status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); + status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_LLPROXY, + LLCore::HttpRequest::GLOBAL_POLICY_ID, + 1, NULL); if (! status) { LL_WARNS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " << status.toString() @@ -131,7 +134,9 @@ void LLAppCoreHttp::init() { long trace_level(0L); trace_level = long(gSavedSettings.getU32(http_trace)); - status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, trace_level); + status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_TRACE, + LLCore::HttpRequest::GLOBAL_POLICY_ID, + trace_level, NULL); } // Setup default policy and constrain if directed to @@ -164,6 +169,9 @@ void LLAppCoreHttp::init() } } + // Need a request object to handle dynamic options before setting them + mRequest = new LLCore::HttpRequest; + // Apply initial settings refreshSettings(true); @@ -175,8 +183,6 @@ void LLAppCoreHttp::init() << LL_ENDL; } - mRequest = new LLCore::HttpRequest; - // Register signals for settings and state changes for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) { @@ -287,12 +293,13 @@ void LLAppCoreHttp::refreshSettings(bool initial) // Set it and report // *TODO: These are intended to be per-host limits when we can // support that in llcorehttp/libcurl. - LLCore::HttpStatus status; - status = LLCore::HttpRequest::setPolicyClassOption(mPolicies[policy], - LLCore::HttpRequest::CP_CONNECTION_LIMIT, - setting); - if (! status) + LLCore::HttpHandle handle; + handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT, + mPolicies[policy], + setting, NULL); + if (LLCORE_HTTP_HANDLE_INVALID == handle) { + LLCore::HttpStatus status(mRequest->getStatus()); LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage << " concurrency. Reason: " << status.toString() << LL_ENDL; -- cgit v1.2.3 From d027db3cfc7c757cc8846e1a5187d1a816f63d9a Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 18 Jul 2013 15:14:55 -0400 Subject: SH-4325 Viewer working mixed grid with GetMesh and GetMesh2 caps Viewer modified for preference for GetMesh2 caps. When found, uses this cap and uses 1/4 the connection concurrency specified by MeshMaxConcurrentRequests. Also uses a modified calculation for high/low water feeding into the llcorehttp library. --- indra/newview/llmeshrepository.cpp | 68 ++++++++++++++++++++++---------------- indra/newview/llmeshrepository.h | 3 +- 2 files changed, 41 insertions(+), 30 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 3af9ce7342..913841ab71 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -152,7 +152,11 @@ // [5] Read of a double-checked lock. // // So, in addition to documentation, take this as a to-do/review -// list and see if you can improve things. +// list and see if you can improve things. For porters to non-x86 +// architectures, including amd64, the weaker memory models will +// make these platforms probabilistically more susceptible to hitting +// race conditions. True here and in other multi-thread code such +// as texture fetching. // // LLMeshRepository: // @@ -237,8 +241,6 @@ U32 LLMeshRepository::sCacheBytesWritten = 0; LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true); // true -> gather cpu metrics -const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5; - static S32 dump_num = 0; std::string make_dump_name(std::string prefix, S32 num) { @@ -797,7 +799,6 @@ void LLMeshRepoThread::run() } mWaiting = true; - ms_sleep(5); mSignal->wait(); mWaiting = false; @@ -982,20 +983,17 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } // Constructs a Cap URL for the mesh. Prefers a GetMesh2 cap -// over a GetMesh cap and returns what it finds to the caller -// as an int ([1..2]). +// over a GetMesh cap. // //static -std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id, int * cap_version) +std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) { - int version(1); std::string http_url; if (gAgent.getRegion()) { if (! gMeshRepo.mGetMesh2Capability.empty()) { - version = 2; http_url = gMeshRepo.mGetMesh2Capability; } else @@ -1015,7 +1013,6 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id, int * cap_version) << mesh_id << ".mesh" << LL_ENDL; } - *cap_version = version; return http_url; } @@ -1134,8 +1131,8 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count) { return false; } - int cap_version(1); - std::string http_url = constructUrl(mesh_id, &cap_version); + int cap_version(gMeshRepo.mGetMeshVersion); + std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size); @@ -1229,8 +1226,8 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id, U32& count) { return false; } - int cap_version(1); - std::string http_url = constructUrl(mesh_id, &cap_version); + int cap_version(gMeshRepo.mGetMeshVersion); + std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size); @@ -1323,8 +1320,8 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id, U32& count) { return false; } - int cap_version(1); - std::string http_url = constructUrl(mesh_id, &cap_version); + int cap_version(gMeshRepo.mGetMeshVersion); + std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size); @@ -1414,8 +1411,8 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c //either cache entry doesn't exist or is corrupt, request header from simulator bool retval = true; - int cap_version(1); - std::string http_url = constructUrl(mesh_params.getSculptID(), &cap_version); + int cap_version(gMeshRepo.mGetMeshVersion); + std::string http_url = constructUrl(mesh_params.getSculptID()); if (!http_url.empty()) { //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits @@ -1500,8 +1497,8 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, } //reading from VFS failed for whatever reason, fetch from sim - int cap_version(1); - std::string http_url = constructUrl(mesh_id, &cap_version); + int cap_version(gMeshRepo.mGetMeshVersion); + std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size); @@ -2647,7 +2644,8 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * dat LLMeshRepository::LLMeshRepository() : mMeshMutex(NULL), mMeshThreadCount(0), - mThread(NULL) + mThread(NULL), + mGetMeshVersion(2) { } @@ -2827,13 +2825,24 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para void LLMeshRepository::notifyLoadedMeshes() { //called from main thread - // *FIXME: Scaling down the setting by a factor of 4 for now to reflect - // target goal. May want to rename the setting before release. Also - // want/need to get these in a coordinated fashion from llappcorehttp. - LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests") / 4; - LLMeshRepoThread::sRequestHighWater = llclamp(10 * S32(LLMeshRepoThread::sMaxConcurrentRequests), - REQUEST_HIGH_WATER_MIN, - REQUEST_HIGH_WATER_MAX); + if (1 == mGetMeshVersion) + { + // Legacy GetMesh operation with high connection concurrency + LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests"); + LLMeshRepoThread::sRequestHighWater = llclamp(2 * S32(LLMeshRepoThread::sMaxConcurrentRequests), + REQUEST_HIGH_WATER_MIN, + REQUEST_HIGH_WATER_MAX); + } + else + { + // GetMesh2 operation with keepalives, etc. + // *TODO: Logic here is replicated from llappcorehttp.cpp, should really + // unify this and keep it in one place only. + LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests") / 4; + LLMeshRepoThread::sRequestHighWater = llclamp(5 * S32(LLMeshRepoThread::sMaxConcurrentRequests), + REQUEST_HIGH_WATER_MIN, + REQUEST_HIGH_WATER_MAX); + } LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2, REQUEST_LOW_WATER_MIN, REQUEST_LOW_WATER_MAX); @@ -2929,6 +2938,7 @@ void LLMeshRepository::notifyLoadedMeshes() region_name = gAgent.getRegion()->getName(); mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh"); mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2"); + mGetMeshVersion = mGetMesh2Capability.empty() ? 1 : 2; LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name << "', GetMesh2: " << mGetMesh2Capability << ", GetMesh: " << mGetMeshCapability @@ -4152,7 +4162,7 @@ void teleport_started() LLMeshRepository::metricsStart(); } -// This comes from an edit in viewer-cat. Unify this once that's +// *TODO: This comes from an edit in viewer-cat. Unify this once that's // available everywhere. bool is_retryable(LLCore::HttpStatus status) { diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 9bf14a3715..96e9feea9d 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -337,7 +337,7 @@ public: typedef std::set http_request_set; http_request_set mHttpRequestSet; // Outstanding HTTP requests - static std::string constructUrl(LLUUID mesh_id, int * cap_version); + static std::string constructUrl(LLUUID mesh_id); LLMeshRepoThread(); ~LLMeshRepoThread(); @@ -600,6 +600,7 @@ public: std::string mGetMeshCapability; std::string mGetMesh2Capability; + int mGetMeshVersion; }; extern LLMeshRepository gMeshRepo; -- cgit v1.2.3 From b7f14a7b39610ac1cb6db5fafeab568aa9b662c5 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 25 Jul 2013 18:36:08 -0400 Subject: SH-4365 SH-4367 Conversion of mesh uploaders to llcorehttp. With this checkin, legacy LLCurl is out of the mesh code. Uploaders and responders are converted and functioning. Logging has been cleaned up throughout the code to use the macro form with tag/subtag capability. DEBUGS-level logging added for some upload path milestones. Better error information flow from failed responses to viewer alert boxes but I'd really, really like to do better here. Mesh upload problems are completely opaque as a user. Minor cleanups (removed dead members, method signatures tidied, less data conversion). Could almost call this complete, will likely have platform cleanups, however. --- indra/newview/llmeshrepository.cpp | 514 +++++++++++++++++++++---------------- indra/newview/llmeshrepository.h | 48 ++-- 2 files changed, 328 insertions(+), 234 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 2d003dd6d7..1f40026e80 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -68,6 +68,7 @@ #include "llviewerparcelmgr.h" #include "lluploadfloaterobservers.h" #include "bufferarray.h" +#include "bufferstream.h" #include "boost/lexical_cast.hpp" @@ -203,6 +204,7 @@ // mUnavailableQ mMutex rw.repo.none [0], ro.main.none [3], rw.main.mMutex // mLoadedQ mMutex rw.repo.mMutex, ro.main.none [3], rw.main.mMutex // mPendingLOD mMutex rw.repo.mMutex, rw.any.mMutex +// mHttp* none rw.repo.none // // LLPhysicsDecomp: // @@ -210,7 +212,19 @@ // mCurRequest // mCompletedQ // - +// +// QA/Development Testing +// +// Debug variable 'MeshUploadFakeErrors' takes a mask of bits that will +// simulate an error on fee query or upload. Defined bits are: +// +// 0x01 Simulate application error on fee check reading +// response body from file "fake_upload_error.xml" +// 0x02 Same as 0x01 but for actual upload attempt. +// 0x04 Simulate a transport problem on fee check with a +// locally-generated 500 status. +// 0x08 As with 0x04 but for the upload operation. +// LLMeshRepository gMeshRepo; @@ -362,6 +376,14 @@ S32 LLMeshRepoThread::sRequestHighWater = REQUEST_HIGH_WATER_MIN; // processFailure() methods to customize handling and // error messages. // +// LLCore::HttpHandler +// LLMeshHandlerBase +// LLMeshHeaderHandler +// LLMeshLODHandler +// LLMeshSkinInfoHandler +// LLMeshDecompositionHandler +// LLMeshPhysicsShapeHandler + class LLMeshHandlerBase : public LLCore::HttpHandler { public: @@ -534,28 +556,31 @@ public: }; -void log_upload_error(S32 status, const LLSD& content, std::string stage, std::string model_name) +void log_upload_error(LLCore::HttpStatus status, const LLSD& content, + const char * const stage, const std::string & model_name) { // Add notification popup. LLSD args; - std::string message = content["error"]["message"]; - std::string identifier = content["error"]["identifier"]; + std::string message = content["error"]["message"].asString(); + std::string identifier = content["error"]["identifier"].asString(); args["MESSAGE"] = message; args["IDENTIFIER"] = identifier; args["LABEL"] = model_name; gMeshRepo.uploadError(args); // Log details. - llwarns << "stage: " << stage << " http status: " << status << llendl; + LL_WARNS(LOG_MESH) << "Error in stage: " << stage + << ", Reason: " << status.toString() + << " (" << status.toHex() << ")" << LL_ENDL; if (content.has("error")) { const LLSD& err = content["error"]; - llwarns << "err: " << err << llendl; - llwarns << "mesh upload failed, stage '" << stage - << "' error '" << err["error"].asString() - << "', message '" << err["message"].asString() - << "', id '" << err["identifier"].asString() - << "'" << llendl; + 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() + << "'" << LL_ENDL; if (err.has("errors")) { S32 error_num = 0; @@ -565,13 +590,13 @@ void log_upload_error(S32 status, const LLSD& content, std::string stage, std::s ++it) { const LLSD& err_entry = *it; - llwarns << "error[" << error_num << "]:" << llendl; + LL_WARNS(LOG_MESH) << " error[" << error_num << "]:" << LL_ENDL; for (LLSD::map_const_iterator map_it = err_entry.beginMap(); map_it != err_entry.endMap(); ++map_it) { - llwarns << "\t" << map_it->first << ": " - << map_it->second << llendl; + LL_WARNS(LOG_MESH) << " " << map_it->first << ": " + << map_it->second << LL_ENDL; } error_num++; } @@ -579,141 +604,10 @@ void log_upload_error(S32 status, const LLSD& content, std::string stage, std::s } else { - llwarns << "bad mesh, no error information available" << llendl; + LL_WARNS(LOG_MESH) << "Bad response to mesh request, no additional error information available." << LL_ENDL; } } -class LLWholeModelFeeResponder: public LLCurl::Responder -{ - LLMeshUploadThread* mThread; - LLSD mModelData; - LLHandle mObserverHandle; -public: - LLWholeModelFeeResponder(LLMeshUploadThread* thread, LLSD& model_data, LLHandle observer_handle): - mThread(thread), - mModelData(model_data), - mObserverHandle(observer_handle) - { - if (mThread) - { - mThread->startRequest(); - } - } - - ~LLWholeModelFeeResponder() - { - if (mThread) - { - mThread->stopRequest(); - } - } - - virtual void completed(U32 status, - const std::string& reason, - const LLSD& content) - { - LLSD cc = content; - if (gSavedSettings.getS32("MeshUploadFakeErrors")&1) - { - cc = llsd_from_file("fake_upload_error.xml"); - } - - dump_llsd_to_file(cc,make_dump_name("whole_model_fee_response_",dump_num)); - - LLWholeModelFeeObserver* observer = mObserverHandle.get(); - - if (isGoodStatus(status) && - cc["state"].asString() == "upload") - { - mThread->mWholeModelUploadURL = cc["uploader"].asString(); - - if (observer) - { - cc["data"]["upload_price"] = cc["upload_price"]; - observer->onModelPhysicsFeeReceived(cc["data"], mThread->mWholeModelUploadURL); - } - } - else - { - llwarns << "fee request failed" << llendl; - log_upload_error(status,cc,"fee",mModelData["name"]); - mThread->mWholeModelUploadURL = ""; - - if (observer) - { - observer->setModelPhysicsFeeErrorStatus(status, reason); - } - } - } - -}; - -class LLWholeModelUploadResponder: public LLCurl::Responder -{ - LLMeshUploadThread* mThread; - LLSD mModelData; - LLHandle mObserverHandle; - -public: - LLWholeModelUploadResponder(LLMeshUploadThread* thread, LLSD& model_data, LLHandle observer_handle): - mThread(thread), - mModelData(model_data), - mObserverHandle(observer_handle) - { - if (mThread) - { - mThread->startRequest(); - } - } - - ~LLWholeModelUploadResponder() - { - if (mThread) - { - mThread->stopRequest(); - } - } - - virtual void completed(U32 status, - const std::string& reason, - const LLSD& content) - { - LLSD cc = content; - if (gSavedSettings.getS32("MeshUploadFakeErrors")&2) - { - cc = llsd_from_file("fake_upload_error.xml"); - } - - dump_llsd_to_file(cc,make_dump_name("whole_model_upload_response_",dump_num)); - - LLWholeModelUploadObserver* observer = mObserverHandle.get(); - - // requested "mesh" asset type isn't actually the type - // of the resultant object, fix it up here. - if (isGoodStatus(status) && - cc["state"].asString() == "complete") - { - mModelData["asset_type"] = "object"; - gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mModelData,cc)); - - if (observer) - { - doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadSuccess, observer)); - } - } - else - { - llwarns << "upload failed" << llendl; - std::string model_name = mModelData["name"].asString(); - log_upload_error(status,cc,"upload",model_name); - - if (observer) - { - doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer)); - } - } - } -}; LLMeshRepoThread::LLMeshRepoThread() : LLThread("mesh repo"), @@ -788,7 +682,7 @@ void LLMeshRepoThread::run() LLCDResult res = LLConvexDecomposition::initThread(); if (res != LLCD_OK) { - llwarns << "convex decomposition unable to be loaded" << llendl; + LL_WARNS(LOG_MESH) << "Convex decomposition unable to be loaded. Expect severe problems." << LL_ENDL; } while (!LLApp::isQuitting()) @@ -923,7 +817,7 @@ void LLMeshRepoThread::run() res = LLConvexDecomposition::quitThread(); if (res != LLCD_OK) { - llwarns << "convex decomposition unable to be quit" << llendl; + LL_WARNS(LOG_MESH) << "Convex decomposition unable to be quit." << LL_ENDL; } } @@ -951,7 +845,6 @@ void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 } - void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { //could be called from any thread LLMutexLock lock(mMutex); @@ -1648,7 +1541,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat LLMeshSkinInfo info(skin); info.mMeshID = mesh_id; - //llinfos<<"info pelvis offset"< fee_observer, LLHandle upload_observer) -: LLThread("mesh upload"), + bool upload_skin, bool upload_joints, const std::string & upload_url, bool do_upload, + LLHandle fee_observer, + LLHandle upload_observer) + : LLThread("mesh upload"), + LLCore::HttpHandler(), mDiscarded(FALSE), mDoUpload(do_upload), mWholeModelUploadURL(upload_url), @@ -1754,7 +1650,6 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, mUploadSkin = upload_skin; mUploadJoints = upload_joints; mMutex = new LLMutex(NULL); - mCurlRequest = NULL; mPendingUploads = 0; mFinished = false; mOrigin = gAgent.getPositionAgent(); @@ -1764,12 +1659,31 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, mOrigin += gAgent.getAtAxis() * scale.magVec(); - mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut") ; + mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut"); + + mHttpRequest = new LLCore::HttpRequest; + mHttpOptions = new LLCore::HttpOptions; + mHttpOptions->setTransferTimeout(mMeshUploadTimeOut); + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->append("Content-Type", "application/llsd+xml"); + mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS); + mHttpPriority = 0; } LLMeshUploadThread::~LLMeshUploadThread() { - + if (mHttpHeaders) + { + mHttpHeaders->release(); + mHttpHeaders = NULL; + } + if (mHttpOptions) + { + mHttpOptions->release(); + mHttpOptions = NULL; + } + delete mHttpRequest; + mHttpRequest = NULL; } LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread) @@ -1811,14 +1725,14 @@ void LLMeshUploadThread::preStart() void LLMeshUploadThread::discard() { - LLMutexLock lock(mMutex) ; - mDiscarded = TRUE ; + LLMutexLock lock(mMutex); + mDiscarded = TRUE; } -BOOL LLMeshUploadThread::isDiscarded() +BOOL LLMeshUploadThread::isDiscarded() const { - LLMutexLock lock(mMutex) ; - return mDiscarded ; + LLMutexLock lock(mMutex); + return mDiscarded; } void LLMeshUploadThread::run() @@ -2069,7 +1983,7 @@ void LLMeshUploadThread::generateHulls() } } - if(has_valid_requests) + if (has_valid_requests) { while (!mPhysicsComplete) { @@ -2080,7 +1994,7 @@ void LLMeshUploadThread::generateHulls() void LLMeshUploadThread::doWholeModelUpload() { - mCurlRequest = new LLCurlRequest(); + LL_DEBUGS(LOG_MESH) << "Starting model upload. Instances: " << mInstance.size() << LL_ENDL; if (mWholeModelUploadURL.empty()) { @@ -2090,75 +2004,240 @@ void LLMeshUploadThread::doWholeModelUpload() else { generateHulls(); - - LLSD full_model_data; - wholeModelToLLSD(full_model_data, true); - LLSD body = full_model_data["asset_resources"]; - dump_llsd_to_file(body,make_dump_name("whole_model_body_",dump_num)); - LLCurlRequest::headers_t headers; - + LL_DEBUGS(LOG_MESH) << "Hull generation completed." << LL_ENDL; + + 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); // <- This will generate a convenient upload error + LLCore::HttpHandle handle = mHttpRequest->requestPost(mHttpPolicyClass, + mHttpPriority, + mWholeModelUploadURL, + ba, + mHttpOptions, + mHttpHeaders, + this); + ba->release(); + + if (LLCORE_HTTP_HANDLE_INVALID == handle) { - LLCurl::ResponderPtr responder = new LLWholeModelUploadResponder(this, full_model_data, mUploadObserverHandle) ; - - while(!mCurlRequest->post(mWholeModelUploadURL, headers, body, responder, mMeshUploadTimeOut)) + mHttpStatus = mHttpRequest->getStatus(); + + LL_WARNS(LOG_MESH) << "Couldn't issue request for full model upload. Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toHex() << ")" + << LL_ENDL; + } + else + { + U32 sleep_time(10); + + LL_DEBUGS(LOG_MESH) << "POST request issued." << LL_ENDL; + + mHttpRequest->update(0); + while (! LLApp::isQuitting() && ! mFinished) { - //sleep for 10ms to prevent eating a whole core - apr_sleep(10000); + ms_sleep(sleep_time); + sleep_time = llmin(250U, sleep_time + sleep_time); + mHttpRequest->update(0); } + LL_DEBUGS(LOG_MESH) << "Mesh upload operation completed." << LL_ENDL; } - - do - { - mCurlRequest->process(); - //sleep for 10ms to prevent eating a whole core - apr_sleep(10000); - } while (!LLAppViewer::isQuitting() && mPendingUploads > 0); } - - delete mCurlRequest; - mCurlRequest = NULL; - - // Currently a no-op. - mFinished = true; } void LLMeshUploadThread::requestWholeModelFee() { dump_num++; - mCurlRequest = new LLCurlRequest(); - generateHulls(); - LLSD model_data; - wholeModelToLLSD(model_data,false); - dump_llsd_to_file(model_data,make_dump_name("whole_model_fee_request_",dump_num)); - - LLCurlRequest::headers_t headers; + 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(); + if (LLCORE_HTTP_HANDLE_INVALID == handle) { - LLCurl::ResponderPtr responder = new LLWholeModelFeeResponder(this,model_data, mFeeObserverHandle) ; - while(!mCurlRequest->post(mWholeModelFeeCapability, headers, model_data, responder, mMeshUploadTimeOut)) + mHttpStatus = mHttpRequest->getStatus(); + + LL_WARNS(LOG_MESH) << "Couldn't issue request for model fee. Reason: " << mHttpStatus.toString() + << " (" << mHttpStatus.toHex() << ")" + << LL_ENDL; + } + else + { + U32 sleep_time(10); + + mHttpRequest->update(0); + while (! LLApp::isQuitting() && ! mFinished) { - //sleep for 10ms to prevent eating a whole core - apr_sleep(10000); + ms_sleep(sleep_time); + sleep_time = llmin(250U, sleep_time + sleep_time); + mHttpRequest->update(0); } } +} - do - { - mCurlRequest->process(); - //sleep for 10ms to prevent eating a whole core - apr_sleep(10000); - } while (!LLApp::isQuitting() && mPendingUploads > 0); - delete mCurlRequest; - mCurlRequest = NULL; +// Does completion duty for both fee queries and actual uploads. +void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + // QA/Devel: 0x2 to enable fake error import on upload, 0x1 on fee check + const S32 fake_error(gSavedSettings.getS32("MeshUploadFakeErrors") & (mDoUpload ? 0xa : 0x5)); + LLCore::HttpStatus status(response->getStatus()); + if (fake_error) + { + status = (fake_error & 0x0c) ? LLCore::HttpStatus(500) : LLCore::HttpStatus(200); + } + std::string reason(status.toString()); + LLSD body; - // Currently a no-op. mFinished = true; + + if (mDoUpload) + { + // model upload case + LLWholeModelUploadObserver * observer(mUploadObserverHandle.get()); + + if (! status) + { + LL_WARNS(LOG_MESH) << "Upload failed. Reason: " << reason + << " (" << status.toHex() << ")" + << LL_ENDL; + + // Build a fake body for the alert generator + body["error"] = LLSD::emptyMap(); + body["error"]["message"] = reason; + body["error"]["identifier"] = "NetworkError"; // from asset-upload/upload_util.py + log_upload_error(status, body, "upload", mModelData["name"].asString()); + + if (observer) + { + doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer)); + } + } + else + { + if (fake_error & 0x2) + { + 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_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"; + gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mModelData, body)); + + if (observer) + { + doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadSuccess, observer)); + } + } + else + { + LL_WARNS(LOG_MESH) << "Upload failed. Not in expected 'complete' state." << LL_ENDL; + log_upload_error(status, body, "upload", mModelData["name"].asString()); + + if (observer) + { + doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer)); + } + } + } + } + else + { + // model fee case + LLWholeModelFeeObserver* observer(mFeeObserverHandle.get()); + mWholeModelUploadURL.clear(); + + if (! status) + { + LL_WARNS(LOG_MESH) << "Fee request failed. Reason: " << reason + << " (" << status.toHex() << ")" + << LL_ENDL; + + // Build a fake body for the alert generator + body["error"] = LLSD::emptyMap(); + body["error"]["message"] = reason; + body["error"]["identifier"] = "NetworkError"; // from asset-upload/upload_util.py + log_upload_error(status, body, "fee", mModelData["name"].asString()); + + if (observer) + { + observer->setModelPhysicsFeeErrorStatus(status.toULong(), reason); + } + } + else + { + if (fake_error & 0x1) + { + 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) + { + body["data"]["upload_price"] = body["upload_price"]; + observer->onModelPhysicsFeeReceived(body["data"], mWholeModelUploadURL); + } + } + else + { + LL_WARNS(LOG_MESH) << "Fee request failed. Not in expected 'upload' state." << LL_ENDL; + log_upload_error(status, body, "fee", mModelData["name"].asString()); + + if (observer) + { + observer->setModelPhysicsFeeErrorStatus(status.toULong(), reason); + } + } + } + } } + void LLMeshRepoThread::notifyLoadedMeshes() { if (!mMutex) @@ -2674,13 +2753,13 @@ void LLMeshRepository::init() void LLMeshRepository::shutdown() { - llinfos << "Shutting down mesh repository." << llendl; + LL_INFOS(LOG_MESH) << "Shutting down mesh repository." << LL_ENDL; metrics_teleport_started_signal.disconnect(); for (U32 i = 0; i < mUploads.size(); ++i) { - llinfos << "Discard the pending mesh uploads " << llendl; + LL_INFOS(LOG_MESH) << "Discard the pending mesh uploads." << LL_ENDL; mUploads[i]->discard() ; //discard the uploading requests. } @@ -2695,7 +2774,7 @@ void LLMeshRepository::shutdown() for (U32 i = 0; i < mUploads.size(); ++i) { - llinfos << "Waiting for pending mesh upload " << i << "/" << mUploads.size() << llendl; + LL_INFOS(LOG_MESH) << "Waiting for pending mesh upload " << i << "/" << mUploads.size() << LL_ENDL; while (!mUploads[i]->isStopped()) { apr_sleep(10); @@ -2708,7 +2787,7 @@ void LLMeshRepository::shutdown() delete mMeshMutex; mMeshMutex = NULL; - llinfos << "Shutting down decomposition system." << llendl; + LL_INFOS(LOG_MESH) << "Shutting down decomposition system." << LL_ENDL; if (mDecompThread) { @@ -3591,7 +3670,7 @@ void LLPhysicsDecomp::setMeshData(LLCDMeshData& mesh, bool vertex_based) if (ret) { - llerrs << "Convex Decomposition thread valid but could not set mesh data" << llendl; + LL_ERRS(LOG_MESH) << "Convex Decomposition thread valid but could not set mesh data." << LL_ENDL; } } } @@ -3667,7 +3746,8 @@ void LLPhysicsDecomp::doDecomposition() if (ret) { - llwarns << "Convex Decomposition thread valid but could not execute stage " << stage << llendl; + LL_WARNS(LOG_MESH) << "Convex Decomposition thread valid but could not execute stage " << stage << "." + << LL_ENDL; LLMutexLock lock(mMutex); mCurRequest->mHull.clear(); @@ -3796,9 +3876,9 @@ void LLPhysicsDecomp::doDecompositionSingleHull() setMeshData(mesh, true); LLCDResult ret = decomp->buildSingleHull() ; - if(ret) + if (ret) { - llwarns << "Could not execute decomposition stage when attempting to create single hull." << llendl; + LL_WARNS(LOG_MESH) << "Could not execute decomposition stage when attempting to create single hull." << LL_ENDL; make_box(mCurRequest); } else @@ -4152,7 +4232,7 @@ void LLMeshRepository::metricsUpdate() metrics["teleports"] = LLSD::Integer(metrics_teleport_start_count); metrics["user_cpu"] = double(user_cpu) / 1.0e6; metrics["sys_cpu"] = double(sys_cpu) / 1.0e6; - llinfos << "EventMarker " << metrics << llendl; + LL_INFOS(LOG_MESH) << "EventMarker " << metrics << LL_ENDL; } } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 96e9feea9d..70079eed23 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -45,8 +45,6 @@ #include "lluploadfloaterobservers.h" class LLVOVolume; -class LLMeshResponder; -class LLCurlRequest; class LLMutex; class LLCondition; class LLVFS; @@ -396,7 +394,14 @@ private: U32 mHttpLargeGetCount; }; -class LLMeshUploadThread : public LLThread + +// Class whose instances represent a single upload-type request for +// meshes: one fee query or one actual upload attempt. Yes, it creates +// a unique thread for that single request. As it is 1:1, it can also +// trivially serve as the HttpHandler object for request completion +// notifications. + +class LLMeshUploadThread : public LLThread, public LLCore::HttpHandler { private: S32 mMeshUploadTimeOut ; //maximum time in seconds to execute an uploading request. @@ -417,44 +422,41 @@ public: }; LLPointer mFinalDecomp; - bool mPhysicsComplete; + volatile bool mPhysicsComplete; typedef std::map, std::vector > hull_map; - hull_map mHullMap; + hull_map mHullMap; typedef std::vector instance_list; - instance_list mInstanceList; + instance_list mInstanceList; typedef std::map, instance_list> instance_map; - instance_map mInstance; + instance_map mInstance; - LLMutex* mMutex; - LLCurlRequest* mCurlRequest; + LLMutex* mMutex; S32 mPendingUploads; LLVector3 mOrigin; bool mFinished; bool mUploadTextures; bool mUploadSkin; bool mUploadJoints; - BOOL mDiscarded ; + BOOL mDiscarded; LLHost mHost; std::string mWholeModelFeeCapability; std::string mWholeModelUploadURL; LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures, - bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload = true, - LLHandle fee_observer= (LLHandle()), LLHandle upload_observer = (LLHandle())); + bool upload_skin, bool upload_joints, const std::string & upload_url, bool do_upload = true, + LLHandle fee_observer = (LLHandle()), + LLHandle upload_observer = (LLHandle())); ~LLMeshUploadThread(); - void startRequest() { ++mPendingUploads; } - void stopRequest() { --mPendingUploads; } - - bool finished() { return mFinished; } + bool finished() const { return mFinished; } virtual void run(); void preStart(); void discard() ; - BOOL isDiscarded(); + BOOL isDiscarded() const; void generateHulls(); @@ -471,11 +473,23 @@ public: void setFeeObserverHandle(LLHandle observer_handle) { mFeeObserverHandle = observer_handle; } void setUploadObserverHandle(LLHandle observer_handle) { mUploadObserverHandle = observer_handle; } + // Inherited from LLCore::HttpHandler + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + private: LLHandle mFeeObserverHandle; LLHandle mUploadObserverHandle; bool mDoUpload; // if FALSE only model data will be requested, otherwise the model will be uploaded + LLSD mModelData; + + // llcorehttp library interface objects. + LLCore::HttpStatus mHttpStatus; + LLCore::HttpRequest * mHttpRequest; + LLCore::HttpOptions * mHttpOptions; + LLCore::HttpHeaders * mHttpHeaders; + LLCore::HttpRequest::policy_t mHttpPolicyClass; + LLCore::HttpRequest::priority_t mHttpPriority; }; class LLMeshRepository -- cgit v1.2.3 From c9e64823c05a493e6c926deebff1b0aaf0fb50be Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 29 Jul 2013 12:42:27 -0400 Subject: SH-4368 Adjust upload timeout parameters for slow networks. Generally sorted the mesh timeout parameters for maximum transport time (staying with default 30 for connect). 60S for normal meshes, 600S for large. Also documented default option values in httpoptions.h. Useful to have these. In the future, the timeouts might go into standard llsd options where they can be tracked a bit more. --- indra/newview/llmeshrepository.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 1f40026e80..d02384f87c 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -235,7 +235,8 @@ const S32 REQUEST_HIGH_WATER_MAX = 80; const S32 REQUEST_LOW_WATER_MIN = 16; const S32 REQUEST_LOW_WATER_MAX = 40; const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes to narrow/slow queue -const long LARGE_MESH_XFER_TIMEOUT = 240L; // Seconds to complete xfer +const long SMALL_MESH_XFER_TIMEOUT = 60L; // Seconds to complete xfer, small mesh downloads +const long LARGE_MESH_XFER_TIMEOUT = 600L; // Seconds to complete xfer, large downloads // Maximum mesh version to support. Three least significant digits are reserved for the minor version, // with major version changes indicating a format change that is not backwards compatible and should not @@ -629,6 +630,7 @@ LLMeshRepoThread::LLMeshRepoThread() mSignal = new LLCondition(NULL); mHttpRequest = new LLCore::HttpRequest; mHttpOptions = new LLCore::HttpOptions; + mHttpOptions->setTransferTimeout(SMALL_MESH_XFER_TIMEOUT); mHttpLargeOptions = new LLCore::HttpOptions; mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT); mHttpHeaders = new LLCore::HttpHeaders; -- cgit v1.2.3 From f3927c6ca2aad757fe88fdd59b87986ca8b207a8 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 30 Jul 2013 15:21:31 -0400 Subject: SH-4371 Reduce 22mS inter-connection latency. This really extended into the client-side request throttling. Moved this from llmeshrepository (which doesn't really want to do connection management) into llcorehttp. It's now a class option with configurable rate. This still isn't the right thing to do as it creates coupling between viewer and services. When we get to pipelining, this notion becomes invalid. --- indra/newview/llappcorehttp.cpp | 31 ++++++++++++++++---- indra/newview/llmeshrepository.cpp | 59 ++++++++++++-------------------------- indra/newview/llmeshrepository.h | 11 ++++--- 3 files changed, 49 insertions(+), 52 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 104debe023..b90b9749f9 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -40,32 +40,33 @@ static const struct U32 mMin; U32 mMax; U32 mDivisor; + U32 mRate; std::string mKey; const char * mUsage; } init_data[] = // Default and dynamic values for classes { { - LLAppCoreHttp::AP_TEXTURE, 8, 1, 12, 1, + LLAppCoreHttp::AP_TEXTURE, 8, 1, 12, 1, 0, "TextureFetchConcurrency", "texture fetch" }, { - LLAppCoreHttp::AP_MESH1, 32, 1, 128, 1, + LLAppCoreHttp::AP_MESH1, 32, 1, 128, 1, 100, "MeshMaxConcurrentRequests", "mesh fetch" }, { - LLAppCoreHttp::AP_MESH2, 8, 1, 32, 4, + LLAppCoreHttp::AP_MESH2, 8, 1, 32, 4, 100, "MeshMaxConcurrentRequests", "mesh2 fetch" }, { - LLAppCoreHttp::AP_LARGE_MESH, 2, 1, 8, 1, + LLAppCoreHttp::AP_LARGE_MESH, 2, 1, 8, 1, 0, "", "large mesh fetch" }, { - LLAppCoreHttp::AP_UPLOADS, 2, 1, 8, 1, + LLAppCoreHttp::AP_UPLOADS, 2, 1, 8, 1, 0, "", "asset upload" } @@ -267,10 +268,28 @@ void LLAppCoreHttp::cleanup() void LLAppCoreHttp::refreshSettings(bool initial) { + LLCore::HttpStatus status; + for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) { const EAppPolicy policy(init_data[i].mPolicy); + // Set any desired throttle + if (initial && init_data[i].mRate) + { + // Init-time only, can use the static setters here + status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_THROTTLE_RATE, + mPolicies[policy], + init_data[i].mRate, + NULL); + if (! status) + { + LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage + << " throttle rate. Reason: " << status.toString() + << LL_ENDL; + } + } + // Get target connection concurrency value U32 setting(init_data[i].mDefault); if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey)) @@ -299,7 +318,7 @@ void LLAppCoreHttp::refreshSettings(bool initial) setting, NULL); if (LLCORE_HTTP_HANDLE_INVALID == handle) { - LLCore::HttpStatus status(mRequest->getStatus()); + status = mRequest->getStatus(); LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage << " concurrency. Reason: " << status.toString() << LL_ENDL; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index d02384f87c..e9b1a10e73 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -229,7 +229,6 @@ LLMeshRepository gMeshRepo; const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space -const U32 MAX_MESH_REQUESTS_PER_SECOND = 100; const S32 REQUEST_HIGH_WATER_MIN = 32; const S32 REQUEST_HIGH_WATER_MAX = 80; const S32 REQUEST_LOW_WATER_MIN = 16; @@ -613,7 +612,6 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content, LLMeshRepoThread::LLMeshRepoThread() : LLThread("mesh repo"), mWaiting(false), - mHttpRetries(0U), mHttpRequest(NULL), mHttpOptions(NULL), mHttpLargeOptions(NULL), @@ -701,23 +699,9 @@ void LLMeshRepoThread::run() if (! LLApp::isQuitting()) { - static U32 count = 0; - static F32 last_hundred = gFrameTimeSeconds; - - if (gFrameTimeSeconds - last_hundred > 1.f) - { //a second has gone by, clear count - last_hundred = gFrameTimeSeconds; - count = 0; - } - else - { - count += mHttpRetries; - } - mHttpRetries = 0U; - - // NOTE: throttling intentionally favors LOD requests over header requests + // NOTE: order of queue processing intentionally favors LOD requests over header requests - while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater) + while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) { if (! mMutex) { @@ -728,7 +712,7 @@ void LLMeshRepoThread::run() mLODReqQ.pop(); LLMeshRepository::sLODProcessing--; mMutex->unlock(); - if (!fetchMeshLOD(req.mMeshParams, req.mLOD, count))//failed, resubmit + if (!fetchMeshLOD(req.mMeshParams, req.mLOD))//failed, resubmit { mMutex->lock(); mLODReqQ.push(req) ; @@ -737,7 +721,7 @@ void LLMeshRepoThread::run() } } - while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater) + while (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) { if (! mMutex) { @@ -747,7 +731,7 @@ void LLMeshRepoThread::run() HeaderRequest req = mHeaderReqQ.front(); mHeaderReqQ.pop(); mMutex->unlock(); - if (!fetchMeshHeader(req.mMeshParams, count))//failed, resubmit + if (!fetchMeshHeader(req.mMeshParams))//failed, resubmit { mMutex->lock(); mHeaderReqQ.push(req) ; @@ -762,14 +746,14 @@ void LLMeshRepoThread::run() // order will lose. Keep to the throttle enforcement and pay // attention to the highwater level (enforced in each fetchXXX() // method). - if (! mSkinRequests.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater) + if (! mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) { // *FIXME: this really does need a lock as do the following ones std::set incomplete; for (std::set::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter) { LLUUID mesh_id = *iter; - if (!fetchMeshSkinInfo(mesh_id, count)) + if (!fetchMeshSkinInfo(mesh_id)) { incomplete.insert(mesh_id); } @@ -777,13 +761,13 @@ void LLMeshRepoThread::run() mSkinRequests.swap(incomplete); } - if (! mDecompositionRequests.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater) + if (! mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) { std::set incomplete; for (std::set::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter) { LLUUID mesh_id = *iter; - if (!fetchMeshDecomposition(mesh_id, count)) + if (!fetchMeshDecomposition(mesh_id)) { incomplete.insert(mesh_id); } @@ -791,13 +775,13 @@ void LLMeshRepoThread::run() mDecompositionRequests.swap(incomplete); } - if (! mPhysicsShapeRequests.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater) + if (! mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) { std::set incomplete; for (std::set::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter) { LLUUID mesh_id = *iter; - if (!fetchMeshPhysicsShape(mesh_id, count)) + if (!fetchMeshPhysicsShape(mesh_id)) { incomplete.insert(mesh_id); } @@ -965,7 +949,7 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c } -bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count) +bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) { if (!mHeaderMutex) @@ -1023,7 +1007,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count) } //reading from VFS failed for whatever reason, fetch from sim - if (count >= MAX_MESH_REQUESTS_PER_SECOND || mHttpRequestSet.size() >= sRequestHighWater) + if (mHttpRequestSet.size() >= sRequestHighWater) { return false; } @@ -1060,7 +1044,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count) return ret; } -bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id, U32& count) +bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) { if (!mHeaderMutex) { @@ -1118,7 +1102,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id, U32& count) } //reading from VFS failed for whatever reason, fetch from sim - if (count >= MAX_MESH_REQUESTS_PER_SECOND || mHttpRequestSet.size() >= sRequestHighWater) + if (mHttpRequestSet.size() >= sRequestHighWater) { return false; } @@ -1155,7 +1139,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id, U32& count) return ret; } -bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id, U32& count) +bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) { if (!mHeaderMutex) { @@ -1212,7 +1196,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id, U32& count) } //reading from VFS failed for whatever reason, fetch from sim - if (count >= MAX_MESH_REQUESTS_PER_SECOND || mHttpRequestSet.size() >= sRequestHighWater) + if (mHttpRequestSet.size() >= sRequestHighWater) { return false; } @@ -1282,7 +1266,7 @@ void LLMeshRepoThread::decActiveHeaderRequests() } //return false if failed to get header -bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& count) +bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) { { //look for mesh in asset in vfs @@ -1331,7 +1315,6 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); ++LLMeshRepository::sHTTPRequestCount; - ++count; } } @@ -1339,7 +1322,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c } //return false if failed to get mesh lod. -bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count) +bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { if (!mHeaderMutex) { @@ -1413,7 +1396,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); ++LLMeshRepository::sHTTPRequestCount; - ++count; } } else @@ -2374,11 +2356,8 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo { mProcessed = true; - // Accumulate retries, we'll use these to offset the HTTP - // count and maybe hold to a throttle better. unsigned int retries(0U); response->getRetries(NULL, &retries); - gMeshRepo.mThread->mHttpRetries += retries; LLMeshRepository::sHTTPRetryCount += retries; LLCore::HttpStatus status(response->getStatus()); diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 70079eed23..400ceb4ad7 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -322,7 +322,6 @@ public: // llcorehttp library interface objects. LLCore::HttpStatus mHttpStatus; - unsigned int mHttpRetries; LLCore::HttpRequest * mHttpRequest; LLCore::HttpOptions * mHttpOptions; LLCore::HttpOptions * mHttpLargeOptions; @@ -345,8 +344,8 @@ public: void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); - bool fetchMeshHeader(const LLVolumeParams& mesh_params, U32& count); - bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count); + bool fetchMeshHeader(const LLVolumeParams& mesh_params); + bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod); bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); bool lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size); bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size); @@ -363,15 +362,15 @@ public: //send request for skin info, returns true if header info exists // (should hold onto mesh_id and try again later if header info does not exist) - bool fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count); + bool fetchMeshSkinInfo(const LLUUID& mesh_id); //send request for decomposition, returns true if header info exists // (should hold onto mesh_id and try again later if header info does not exist) - bool fetchMeshDecomposition(const LLUUID& mesh_id, U32& count); + bool fetchMeshDecomposition(const LLUUID& mesh_id); //send request for PhysicsShape, returns true if header info exists // (should hold onto mesh_id and try again later if header info does not exist) - bool fetchMeshPhysicsShape(const LLUUID& mesh_id, U32& count); + bool fetchMeshPhysicsShape(const LLUUID& mesh_id); static void incActiveLODRequests(); static void decActiveLODRequests(); -- cgit v1.2.3 From 549e20cf7766dc7fba2f42883659a6bcac863d91 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 5 Aug 2013 13:54:14 -0400 Subject: Change the setting for GetMesh2 meshes to Mesh2MaxConcurrentRequests. While linking GetMesh2 to the old setting was simpler from a user point-of-view, they really shouldn't be linked and the old one will go away. This one may be renamed to AssetMaxConcurrentRequests or something similar if we get to the mesh/texture unification step. --- indra/newview/app_settings/settings.xml | 14 ++++++++++++-- indra/newview/llappcorehttp.cpp | 23 ++++++++++++++--------- indra/newview/llmeshrepository.cpp | 4 +--- 3 files changed, 27 insertions(+), 14 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 344079b640..770a8529f1 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9708,11 +9708,21 @@ Value 16 - + Mesh2MaxConcurrentRequests + + Comment + Number of connections to use for loading meshes. + Persist + 1 + Type + U32 + Value + 8 + MeshMaxConcurrentRequests Comment - Number of threads to use for loading meshes. + Number of connections to use for loading meshes (legacy system). Persist 1 Type diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index b90b9749f9..01317fe32f 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -32,6 +32,13 @@ #include "llviewercontrol.h" +// Here is where we begin to get our connection usage under control. +// This establishes llcorehttp policy classes that, among other +// things, limit the maximum number of connections to outside +// services. Each of the entries below maps to a policy class and +// has a limit, sometimes configurable, of how many connections can +// be open at a time. + const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0); static const struct { @@ -39,34 +46,33 @@ static const struct U32 mDefault; U32 mMin; U32 mMax; - U32 mDivisor; U32 mRate; std::string mKey; const char * mUsage; } init_data[] = // Default and dynamic values for classes { { - LLAppCoreHttp::AP_TEXTURE, 8, 1, 12, 1, 0, + LLAppCoreHttp::AP_TEXTURE, 8, 1, 12, 0, "TextureFetchConcurrency", "texture fetch" }, { - LLAppCoreHttp::AP_MESH1, 32, 1, 128, 1, 100, + LLAppCoreHttp::AP_MESH1, 32, 1, 128, 100, "MeshMaxConcurrentRequests", "mesh fetch" }, { - LLAppCoreHttp::AP_MESH2, 8, 1, 32, 4, 100, - "MeshMaxConcurrentRequests", + LLAppCoreHttp::AP_MESH2, 8, 1, 32, 100, + "Mesh2MaxConcurrentRequests", "mesh2 fetch" }, { - LLAppCoreHttp::AP_LARGE_MESH, 2, 1, 8, 1, 0, + LLAppCoreHttp::AP_LARGE_MESH, 2, 1, 8, 0, "", "large mesh fetch" }, { - LLAppCoreHttp::AP_UPLOADS, 2, 1, 8, 1, 0, + LLAppCoreHttp::AP_UPLOADS, 2, 1, 8, 0, "", "asset upload" } @@ -298,8 +304,7 @@ void LLAppCoreHttp::refreshSettings(bool initial) if (new_setting) { // Treat zero settings as an ask for default - setting = new_setting / init_data[i].mDivisor; - setting = llclamp(setting, init_data[i].mMin, init_data[i].mMax); + setting = llclamp(new_setting, init_data[i].mMin, init_data[i].mMax); } } diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index e9b1a10e73..7146c7f4f5 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -2897,9 +2897,7 @@ void LLMeshRepository::notifyLoadedMeshes() else { // GetMesh2 operation with keepalives, etc. - // *TODO: Logic here is replicated from llappcorehttp.cpp, should really - // unify this and keep it in one place only. - LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests") / 4; + LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("Mesh2MaxConcurrentRequests"); LLMeshRepoThread::sRequestHighWater = llclamp(5 * S32(LLMeshRepoThread::sMaxConcurrentRequests), REQUEST_HIGH_WATER_MIN, REQUEST_HIGH_WATER_MAX); -- cgit v1.2.3 From 43788d612042deb6f9329746a101774370f7c67b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 5 Aug 2013 19:04:08 -0400 Subject: Added some simple counters to the mesh repository code and then added a Mesh status line to the texture fetch console. Mesh is often in competition with textures and so the mesh information seems appropriate there. Do get a nice feel for progress and you definitely see when the throttles kick in. --- indra/newview/llmeshrepository.cpp | 134 ++++++++++++++++++++++++------------- indra/newview/llmeshrepository.h | 11 +-- indra/newview/lltextureview.cpp | 41 ++++++++---- 3 files changed, 123 insertions(+), 63 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 7146c7f4f5..97d6c57a78 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -155,20 +155,26 @@ // // So, in addition to documentation, take this as a to-do/review // list and see if you can improve things. For porters to non-x86 -// architectures, including amd64, the weaker memory models will -// make these platforms probabilistically more susceptible to hitting -// race conditions. True here and in other multi-thread code such -// as texture fetching. +// architectures, the weaker memory models will make these platforms +// probabilistically more susceptible to hitting race conditions. +// True here and in other multi-thread code such as texture fetching. +// (Strong memory models make weak programmers. Weak memory models +// make strong programmers. Ref: arm, ppc, mips, alpha) // // LLMeshRepository: // // sBytesReceived +// sMeshRequestCount // sHTTPRequestCount +// sHTTPLargeRequestCount // sHTTPRetryCount +// sHTTPErrorCount // sLODPending // sLODProcessing // sCacheBytesRead // sCacheBytesWritten +// sCacheReads +// sCacheWrites // mLoadingMeshes none rw.main.none, rw.main.mMeshMutex [4] // mSkinMap none rw.main.none // mDecompositionMap none rw.main.none @@ -246,13 +252,19 @@ const long LARGE_MESH_XFER_TIMEOUT = 600L; // Seconds to complete xfer, large const S32 MAX_MESH_VERSION = 999; U32 LLMeshRepository::sBytesReceived = 0; +U32 LLMeshRepository::sMeshRequestCount = 0; U32 LLMeshRepository::sHTTPRequestCount = 0; +U32 LLMeshRepository::sHTTPLargeRequestCount = 0; U32 LLMeshRepository::sHTTPRetryCount = 0; +U32 LLMeshRepository::sHTTPErrorCount = 0; U32 LLMeshRepository::sLODProcessing = 0; U32 LLMeshRepository::sLODPending = 0; U32 LLMeshRepository::sCacheBytesRead = 0; U32 LLMeshRepository::sCacheBytesWritten = 0; +U32 LLMeshRepository::sCacheReads = 0; +U32 LLMeshRepository::sCacheWrites = 0; + LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true); // true -> gather cpu metrics @@ -366,6 +378,7 @@ volatile S32 LLMeshRepoThread::sActiveLODRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; S32 LLMeshRepoThread::sRequestLowWater = REQUEST_LOW_WATER_MIN; S32 LLMeshRepoThread::sRequestHighWater = REQUEST_HIGH_WATER_MIN; +S32 LLMeshRepoThread::sRequestWaterLevel = 0; // Base handler class for all mesh users of llcorehttp. // This is roughly equivalent to a Responder class in @@ -619,9 +632,7 @@ LLMeshRepoThread::LLMeshRepoThread() mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpLegacyPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), - mHttpPriority(0), - mHttpGetCount(0U), - mHttpLargeGetCount(0U) + mHttpPriority(0) { mMutex = new LLMutex(NULL); mHeaderMutex = new LLMutex(NULL); @@ -641,8 +652,8 @@ LLMeshRepoThread::LLMeshRepoThread() LLMeshRepoThread::~LLMeshRepoThread() { - LL_INFOS(LOG_MESH) << "Small GETs issued: " << mHttpGetCount - << ", Large GETs issued: " << mHttpLargeGetCount + LL_INFOS(LOG_MESH) << "Small GETs issued: " << LLMeshRepository::sHTTPRequestCount + << ", Large GETs issued: " << LLMeshRepository::sHTTPLargeRequestCount << LL_ENDL; for (http_request_set::iterator iter(mHttpRequestSet.begin()); @@ -700,7 +711,8 @@ void LLMeshRepoThread::run() if (! LLApp::isQuitting()) { // NOTE: order of queue processing intentionally favors LOD requests over header requests - + + sRequestWaterLevel = mHttpRequestSet.size(); while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) { if (! mMutex) @@ -743,19 +755,26 @@ void LLMeshRepoThread::run() // list, we scan the entire thing. This gets us through any requests // which can be resolved in the cache. It also keeps the request // set somewhat fresher otherwise items at the end of the set - // order will lose. Keep to the throttle enforcement and pay - // attention to the highwater level (enforced in each fetchXXX() - // method). + // order will lose. if (! mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) { // *FIXME: this really does need a lock as do the following ones std::set incomplete; for (std::set::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter) { - LLUUID mesh_id = *iter; - if (!fetchMeshSkinInfo(mesh_id)) + if (mHttpRequestSet.size() < sRequestHighWater) + { + LLUUID mesh_id = *iter; + if (!fetchMeshSkinInfo(mesh_id)) + { + incomplete.insert(mesh_id); + } + } + else { - incomplete.insert(mesh_id); + // Hit high-water mark, copy remaining to incomplete. + incomplete.insert(iter, mSkinRequests.end()); + break; } } mSkinRequests.swap(incomplete); @@ -766,10 +785,19 @@ void LLMeshRepoThread::run() std::set incomplete; for (std::set::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter) { - LLUUID mesh_id = *iter; - if (!fetchMeshDecomposition(mesh_id)) + if (mHttpRequestSet.size() < sRequestHighWater) { - incomplete.insert(mesh_id); + LLUUID mesh_id = *iter; + if (!fetchMeshDecomposition(mesh_id)) + { + incomplete.insert(mesh_id); + } + } + else + { + // Hit high-water mark, copy remaining to incomplete. + incomplete.insert(iter, mDecompositionRequests.end()); + break; } } mDecompositionRequests.swap(incomplete); @@ -780,10 +808,19 @@ void LLMeshRepoThread::run() std::set incomplete; for (std::set::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter) { - LLUUID mesh_id = *iter; - if (!fetchMeshPhysicsShape(mesh_id)) + if (mHttpRequestSet.size() < sRequestHighWater) + { + LLUUID mesh_id = *iter; + if (!fetchMeshPhysicsShape(mesh_id)) + { + incomplete.insert(mesh_id); + } + } + else { - incomplete.insert(mesh_id); + // Hit high-water mark, copy remaining to incomplete. + incomplete.insert(iter, mPhysicsShapeRequests.end()); + break; } } mPhysicsShapeRequests.swap(incomplete); @@ -926,7 +963,10 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c mHttpOptions, mHttpHeaders, handler); - ++mHttpGetCount; + if (LLCORE_HTTP_HANDLE_INVALID != handle) + { + ++LLMeshRepository::sHTTPRequestCount; + } } else { @@ -938,7 +978,10 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c mHttpLargeOptions, mHttpHeaders, handler); - ++mHttpLargeGetCount; + if (LLCORE_HTTP_HANDLE_INVALID != handle) + { + ++LLMeshRepository::sHTTPLargeRequestCount; + } } if (LLCORE_HTTP_HANDLE_INVALID == handle) { @@ -965,7 +1008,8 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) return false; } - bool ret = true ; + ++LLMeshRepository::sMeshRequestCount; + bool ret = true; U32 header_size = mMeshHeaderSize[mesh_id]; if (header_size > 0) @@ -983,6 +1027,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesRead += size; + ++LLMeshRepository::sCacheReads; file.seek(offset); U8* buffer = new U8[size]; file.read(buffer, size); @@ -1007,10 +1052,6 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - if (mHttpRequestSet.size() >= sRequestHighWater) - { - return false; - } int cap_version(gMeshRepo.mGetMeshVersion); std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) @@ -1025,12 +1066,12 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) << LL_ENDL; delete handler; ret = false; + } else { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - ++LLMeshRepository::sHTTPRequestCount; } } } @@ -1059,8 +1100,9 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) return false; } + ++LLMeshRepository::sMeshRequestCount; U32 header_size = mMeshHeaderSize[mesh_id]; - bool ret = true ; + bool ret = true; if (header_size > 0) { @@ -1077,6 +1119,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesRead += size; + ++LLMeshRepository::sCacheReads; file.seek(offset); U8* buffer = new U8[size]; @@ -1102,10 +1145,6 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - if (mHttpRequestSet.size() >= sRequestHighWater) - { - return false; - } int cap_version(gMeshRepo.mGetMeshVersion); std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) @@ -1125,7 +1164,6 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - ++LLMeshRepository::sHTTPRequestCount; } } } @@ -1154,8 +1192,9 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) return false; } + ++LLMeshRepository::sMeshRequestCount; U32 header_size = mMeshHeaderSize[mesh_id]; - bool ret = true ; + bool ret = true; if (header_size > 0) { @@ -1172,6 +1211,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesRead += size; + ++LLMeshRepository::sCacheReads; file.seek(offset); U8* buffer = new U8[size]; file.read(buffer, size); @@ -1196,10 +1236,6 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - if (mHttpRequestSet.size() >= sRequestHighWater) - { - return false; - } int cap_version(gMeshRepo.mGetMeshVersion); std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) @@ -1219,7 +1255,6 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - ++LLMeshRepository::sHTTPRequestCount; } } } @@ -1268,6 +1303,8 @@ void LLMeshRepoThread::decActiveHeaderRequests() //return false if failed to get header bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) { + ++LLMeshRepository::sMeshRequestCount; + { //look for mesh in asset in vfs LLVFile file(gVFS, mesh_params.getSculptID(), LLAssetType::AT_MESH); @@ -1280,6 +1317,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) U8 buffer[MESH_HEADER_SIZE]; S32 bytes = llmin(size, MESH_HEADER_SIZE); LLMeshRepository::sCacheBytesRead += bytes; + ++LLMeshRepository::sCacheReads; file.read(buffer, bytes); if (headerReceived(mesh_params, buffer, bytes)) { @@ -1314,7 +1352,6 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - ++LLMeshRepository::sHTTPRequestCount; } } @@ -1331,6 +1368,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) mHeaderMutex->lock(); + ++LLMeshRepository::sMeshRequestCount; bool retval = true; LLUUID mesh_id = mesh_params.getSculptID(); @@ -1352,6 +1390,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesRead += size; + ++LLMeshRepository::sCacheReads; file.seek(offset); U8* buffer = new U8[size]; file.read(buffer, size); @@ -1395,7 +1434,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - ++LLMeshRepository::sHTTPRequestCount; } } else @@ -2364,6 +2402,7 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo if (! status) { processFailure(status); + ++LLMeshRepository::sHTTPErrorCount; } else { @@ -2484,6 +2523,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) { LLMeshRepository::sCacheBytesWritten += data_size; + ++LLMeshRepository::sCacheWrites; file.write(data, data_size); @@ -2556,6 +2596,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 da file.seek(offset); file.write(data, size); LLMeshRepository::sCacheBytesWritten += size; + ++LLMeshRepository::sCacheWrites; } } // *TODO: Mark mesh unavailable on error @@ -2601,6 +2642,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesWritten += size; + ++LLMeshRepository::sCacheWrites; file.seek(offset); file.write(data, size); } @@ -2648,6 +2690,7 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * da if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesWritten += size; + ++LLMeshRepository::sCacheWrites; file.seek(offset); file.write(data, size); } @@ -2695,6 +2738,7 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * dat if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesWritten += size; + ++LLMeshRepository::sCacheWrites; file.seek(offset); file.write(data, size); } @@ -4204,7 +4248,7 @@ void LLMeshRepository::metricsUpdate() LLSD metrics; metrics["reason"] = "Mesh Download Quiescent"; - metrics["scope"] = "Login"; + metrics["scope"] = metrics_teleport_start_count > 1 ? "Teleport" : "Login"; metrics["start"] = started; metrics["stop"] = stopped; metrics["fetches"] = LLSD::Integer(total_count); diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 400ceb4ad7..7e89f60bc3 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -224,6 +224,7 @@ public: static U32 sMaxConcurrentRequests; static S32 sRequestLowWater; static S32 sRequestHighWater; + static S32 sRequestWaterLevel; // Stats-use only, may read outside of thread LLMutex* mMutex; LLMutex* mHeaderMutex; @@ -387,10 +388,6 @@ private: LLCore::HttpHandle getByteRange(const std::string & url, int cap_version, size_t offset, size_t len, LLCore::HttpHandler * handler); - -private: - U32 mHttpGetCount; - U32 mHttpLargeGetCount; }; @@ -497,12 +494,18 @@ public: //metrics static U32 sBytesReceived; + static U32 sMeshRequestCount; static U32 sHTTPRequestCount; + static U32 sHTTPLargeRequestCount; static U32 sHTTPRetryCount; + static U32 sHTTPErrorCount; static U32 sLODPending; static U32 sLODProcessing; static U32 sCacheBytesRead; static U32 sCacheBytesWritten; + static U32 sCacheReads; + static U32 sCacheWrites; + static LLDeadmanTimer sQuiescentTimer; // time-to-complete-mesh-downloads after significant events static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL); diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index e80136b286..50edbb61a8 100755 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -49,6 +49,7 @@ #include "llviewertexturelist.h" #include "llvovolume.h" #include "llviewerstats.h" +#include "llmeshrepository.h" // For avatar texture view #include "llvoavatarself.h" @@ -517,6 +518,8 @@ void LLGLTexMemBar::draw() F32 total_texture_downloaded = (F32)gTotalTextureBytes / (1024 * 1024); F32 total_object_downloaded = (F32)gTotalObjectBytes / (1024 * 1024); U32 total_http_requests = LLAppViewer::getTextureFetch()->getTotalNumHTTPRequests(); + F32 x_right = 0.0; + //---------------------------------------------------------------------------- LLGLSUIDefault gls_ui; LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); @@ -543,7 +546,7 @@ void LLGLTexMemBar::draw() cache_max_usage); //, cache_entries, cache_max_entries - LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4, + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*5, text_color, LLFontGL::LEFT, LLFontGL::TOP); U32 cache_read(0U), cache_write(0U), res_wait(0U); @@ -557,13 +560,12 @@ void LLGLTexMemBar::draw() cache_write, res_wait); - LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4, text_color, LLFontGL::LEFT, LLFontGL::TOP); - S32 left = 0 ; //---------------------------------------------------------------------------- - text = llformat("Textures: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d RAW:%d HTP:%d DEC:%d CRE:%d", + text = llformat("Textures: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d RAW:%d HTP:%d DEC:%d CRE:%d ", gTextureList.getNumImages(), LLAppViewer::getTextureFetch()->getNumRequests(), LLAppViewer::getTextureFetch()->getNumDeletes(), LLAppViewer::getTextureFetch()->mPacketCount, LLAppViewer::getTextureFetch()->mBadPacketCount, @@ -574,19 +576,30 @@ void LLGLTexMemBar::draw() LLAppViewer::getImageDecodeThread()->getPending(), gTextureList.mCreateTextureList.size()); - LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*2, - text_color, LLFontGL::LEFT, LLFontGL::TOP); - + x_right = 550.0; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, + text_color, LLFontGL::LEFT, LLFontGL::TOP, + LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, + &x_right, FALSE); - left = 550; F32 bandwidth = LLAppViewer::getTextureFetch()->getTextureBandwidth(); F32 max_bandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); - color = bandwidth > max_bandwidth ? LLColor4::red : bandwidth > max_bandwidth*.75f ? LLColor4::yellow : text_color; + color = bandwidth > max_bandwidth ? LLColor4::red : bandwidth > max_bandwidth * .75f ? LLColor4::yellow : text_color; color[VALPHA] = text_color[VALPHA]; - text = llformat("BW:%.0f/%.0f",bandwidth, max_bandwidth); - LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, v_offset + line_height*2, + text = llformat("BW:%.0f/%.0f", bandwidth, max_bandwidth); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, x_right, v_offset + line_height*3, color, LLFontGL::LEFT, LLFontGL::TOP); - + + // Mesh status line + text = llformat("Mesh: Reqs(Tot/Htp/Big): %u/%u/%u Rtr/Err: %u/%u Cread/Cwrite: %u/%u Low/At/High: %d/%d/%d", + LLMeshRepository::sMeshRequestCount, LLMeshRepository::sHTTPRequestCount, LLMeshRepository::sHTTPLargeRequestCount, + LLMeshRepository::sHTTPRetryCount, LLMeshRepository::sHTTPErrorCount, + LLMeshRepository::sCacheReads, LLMeshRepository::sCacheWrites, + LLMeshRepoThread::sRequestLowWater, LLMeshRepoThread::sRequestWaterLevel, LLMeshRepoThread::sRequestHighWater); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*2, + text_color, LLFontGL::LEFT, LLFontGL::TOP); + + // Header for texture table columns S32 dx1 = 0; if (LLAppViewer::getTextureFetch()->mDebugPause) { @@ -629,7 +642,7 @@ BOOL LLGLTexMemBar::handleMouseDown(S32 x, S32 y, MASK mask) LLRect LLGLTexMemBar::getRequiredRect() { LLRect rect; - rect.mTop = 50; //LLFontGL::getFontMonospace()->getLineHeight() * 6; + rect.mTop = 68; //LLFontGL::getFontMonospace()->getLineHeight() * 6; return rect; } -- cgit v1.2.3 From 0d93247359531388f88f22f85326e1142d801e85 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 6 Aug 2013 18:05:34 -0400 Subject: SH-4411 Thread/mutex rework between main and worker thread Have the ::notifyLoadedMeshes() method doing correct locking and stall avoidance at the same time. This method now does lazy mutex lock acquisition (trylock()) and if it fails on either, it gives up and comes back later. Capture the maximum number of sequential failures and report this at the end of the run in the log. (So far, with big mesh regions, I've only seen 1s and 2s.) Locking/mutex requirements sorted in other locations as well. LLMutex gets trylock() method as well as new LLMutexTrylock scoped locking class. Clean up some documentation, more to do. --- indra/newview/llmeshrepository.cpp | 292 ++++++++++++++++++++++--------------- indra/newview/llmeshrepository.h | 17 +-- 2 files changed, 182 insertions(+), 127 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 97d6c57a78..59100a68f9 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -197,7 +197,6 @@ // sActiveHeaderRequests mMutex rw.any.mMutex, ro.repo.none [1] // sActiveLODRequests mMutex rw.any.mMutex, ro.repo.none [1] // sMaxConcurrentRequests mMutex wo.main.none, ro.repo.none, ro.main.mMutex -// mWaiting mMutex rw.repo.none, ro.main.none [2] (race - hint) // mMeshHeader mHeaderMutex rw.repo.mHeaderMutex, ro.main.mHeaderMutex, ro.main.none [0] // mMeshHeaderSize mHeaderMutex rw.repo.mHeaderMutex // mSkinRequests none rw.repo.none, rw.main.none [0] @@ -264,6 +263,7 @@ U32 LLMeshRepository::sCacheBytesRead = 0; U32 LLMeshRepository::sCacheBytesWritten = 0; U32 LLMeshRepository::sCacheReads = 0; U32 LLMeshRepository::sCacheWrites = 0; +U32 LLMeshRepository::sMaxLockHoldoffs = 0; LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true); // true -> gather cpu metrics @@ -288,7 +288,7 @@ const char * const LOG_MESH = "Mesh"; // Static data and functions to measure mesh load // time metrics for a new region scene. -static unsigned int metrics_teleport_start_count(0); +static unsigned int metrics_teleport_start_count = 0; boost::signals2::connection metrics_teleport_started_signal; static void teleport_started(); static bool is_retryable(LLCore::HttpStatus status); @@ -396,6 +396,7 @@ S32 LLMeshRepoThread::sRequestWaterLevel = 0; // LLMeshSkinInfoHandler // LLMeshDecompositionHandler // LLMeshPhysicsShapeHandler +// LLMeshUploadThread class LLMeshHandlerBase : public LLCore::HttpHandler { @@ -624,7 +625,6 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content, LLMeshRepoThread::LLMeshRepoThread() : LLThread("mesh repo"), - mWaiting(false), mHttpRequest(NULL), mHttpOptions(NULL), mHttpLargeOptions(NULL), @@ -654,6 +654,7 @@ LLMeshRepoThread::~LLMeshRepoThread() { LL_INFOS(LOG_MESH) << "Small GETs issued: " << LLMeshRepository::sHTTPRequestCount << ", Large GETs issued: " << LLMeshRepository::sHTTPLargeRequestCount + << ", Max Lock Holdoffs: " << LLMeshRepository::sMaxLockHoldoffs << LL_ENDL; for (http_request_set::iterator iter(mHttpRequestSet.begin()); @@ -698,138 +699,171 @@ void LLMeshRepoThread::run() while (!LLApp::isQuitting()) { + // *TODO: Revise sleep/wake strategy and try to move away' + // from polling operations in this thread. We can sleep + // this thread hard when: + // * All Http requests are serviced + // * LOD request queue empty + // * Header request queue empty + // * Skin info request queue empty + // * Decomposition request queue empty + // * Physics shape request queue empty + // We wake the thread when any of the above become untrue. + // Will likely need a correctly-implemented condition variable to do this. + + mSignal->wait(); + + if (LLApp::isQuitting()) + { + break; + } + if (! mHttpRequestSet.empty()) { // Dispatch all HttpHandler notifications mHttpRequest->update(0L); } + sRequestWaterLevel = mHttpRequestSet.size(); // Stats data update + + // NOTE: order of queue processing intentionally favors LOD requests over header requests - mWaiting = true; - mSignal->wait(); - mWaiting = false; - - if (! LLApp::isQuitting()) + while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) { - // NOTE: order of queue processing intentionally favors LOD requests over header requests - - sRequestWaterLevel = mHttpRequestSet.size(); - while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) + if (! mMutex) + { + break; + } + mMutex->lock(); + LODRequest req = mLODReqQ.front(); + mLODReqQ.pop(); + LLMeshRepository::sLODProcessing--; + mMutex->unlock(); + if (!fetchMeshLOD(req.mMeshParams, req.mLOD))//failed, resubmit { - if (! mMutex) - { - break; - } mMutex->lock(); - LODRequest req = mLODReqQ.front(); - mLODReqQ.pop(); - LLMeshRepository::sLODProcessing--; + mLODReqQ.push(req) ; + ++LLMeshRepository::sLODProcessing; mMutex->unlock(); - if (!fetchMeshLOD(req.mMeshParams, req.mLOD))//failed, resubmit - { - mMutex->lock(); - mLODReqQ.push(req) ; - ++LLMeshRepository::sLODProcessing; - mMutex->unlock(); - } } + } - while (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) + while (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) + { + if (! mMutex) + { + break; + } + mMutex->lock(); + HeaderRequest req = mHeaderReqQ.front(); + mHeaderReqQ.pop(); + mMutex->unlock(); + if (!fetchMeshHeader(req.mMeshParams))//failed, resubmit { - if (! mMutex) - { - break; - } mMutex->lock(); - HeaderRequest req = mHeaderReqQ.front(); - mHeaderReqQ.pop(); + mHeaderReqQ.push(req) ; mMutex->unlock(); - if (!fetchMeshHeader(req.mMeshParams))//failed, resubmit - { - mMutex->lock(); - mHeaderReqQ.push(req) ; - mMutex->unlock(); - } } + } - // For the final three request lists, if we scan any part of one - // list, we scan the entire thing. This gets us through any requests - // which can be resolved in the cache. It also keeps the request - // set somewhat fresher otherwise items at the end of the set - // order will lose. + // For the final three request lists, similar goal to above but + // slightly different queue structures. Stay off the mutex when + // performing long-duration actions. + + if (mHttpRequestSet.size() < sRequestHighWater + && (! mSkinRequests.empty() + || ! mDecompositionRequests.empty() + || ! mPhysicsShapeRequests.empty())) + { + // Something to do probably, lock and double-check. We don't want + // to hold the lock long here. That will stall main thread activities + // so we bounce it. + + mMutex->lock(); if (! mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) { - // *FIXME: this really does need a lock as do the following ones std::set incomplete; - for (std::set::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter) + std::set::iterator iter(mSkinRequests.begin()); + while (iter != mSkinRequests.end() && mHttpRequestSet.size() < sRequestHighWater) { - if (mHttpRequestSet.size() < sRequestHighWater) - { - LLUUID mesh_id = *iter; - if (!fetchMeshSkinInfo(mesh_id)) - { - incomplete.insert(mesh_id); - } - } - else + LLUUID mesh_id = *iter; + mSkinRequests.erase(iter); + mMutex->unlock(); + + if (! fetchMeshSkinInfo(mesh_id)) { - // Hit high-water mark, copy remaining to incomplete. - incomplete.insert(iter, mSkinRequests.end()); - break; + incomplete.insert(mesh_id); } + + mMutex->lock(); + iter = mSkinRequests.begin(); + } + + if (! incomplete.empty()) + { + mSkinRequests.insert(incomplete.begin(), incomplete.end()); } - mSkinRequests.swap(incomplete); } + // holding lock, try next list + // *TODO: For UI/debug-oriented lists, we might drop the fine- + // grained locking as there's lowered expectations of smoothness + // in these cases. if (! mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) { std::set incomplete; - for (std::set::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter) + std::set::iterator iter(mDecompositionRequests.begin()); + while (iter != mDecompositionRequests.end() && mHttpRequestSet.size() < sRequestHighWater) { - if (mHttpRequestSet.size() < sRequestHighWater) - { - LLUUID mesh_id = *iter; - if (!fetchMeshDecomposition(mesh_id)) - { - incomplete.insert(mesh_id); - } - } - else + LLUUID mesh_id = *iter; + mDecompositionRequests.erase(iter); + mMutex->unlock(); + + if (! fetchMeshDecomposition(mesh_id)) { - // Hit high-water mark, copy remaining to incomplete. - incomplete.insert(iter, mDecompositionRequests.end()); - break; + incomplete.insert(mesh_id); } + + mMutex->lock(); + iter = mDecompositionRequests.begin(); + } + + if (! incomplete.empty()) + { + mDecompositionRequests.insert(incomplete.begin(), incomplete.end()); } - mDecompositionRequests.swap(incomplete); } + // holding lock, final list if (! mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) { std::set incomplete; - for (std::set::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter) + std::set::iterator iter(mPhysicsShapeRequests.begin()); + while (iter != mPhysicsShapeRequests.end() && mHttpRequestSet.size() < sRequestHighWater) { - if (mHttpRequestSet.size() < sRequestHighWater) - { - LLUUID mesh_id = *iter; - if (!fetchMeshPhysicsShape(mesh_id)) - { - incomplete.insert(mesh_id); - } - } - else + LLUUID mesh_id = *iter; + mPhysicsShapeRequests.erase(iter); + mMutex->unlock(); + + if (! fetchMeshPhysicsShape(mesh_id)) { - // Hit high-water mark, copy remaining to incomplete. - incomplete.insert(iter, mPhysicsShapeRequests.end()); - break; + incomplete.insert(mesh_id); } + + mMutex->lock(); + iter = mPhysicsShapeRequests.begin(); } - mPhysicsShapeRequests.swap(incomplete); - } - // For dev purposes, a dynamic change could make this false - // and that shouldn't assert. - // llassert_always(mHttpRequestSet.size() <= sRequestHighWater); + if (! incomplete.empty()) + { + mPhysicsShapeRequests.insert(incomplete.begin(), incomplete.end()); + } + } + mMutex->unlock(); } + + // For dev purposes only. A dynamic change could make this false + // and that shouldn't assert. + // llassert_always(mHttpRequestSet.size() <= sRequestHighWater); } if (mSignal->isLocked()) @@ -844,18 +878,21 @@ void LLMeshRepoThread::run() } } +// Mutex: LLMeshRepoThread::mMutex must be held on entry void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id) -{ //protected by mSignal, no locking needed here +{ mSkinRequests.insert(mesh_id); } +// Mutex: LLMeshRepoThread::mMutex must be held on entry void LLMeshRepoThread::loadMeshDecomposition(const LLUUID& mesh_id) -{ //protected by mSignal, no locking needed here +{ mDecompositionRequests.insert(mesh_id); } +// Mutex: LLMeshRepoThread::mMutex must be held on entry void LLMeshRepoThread::loadMeshPhysicsShape(const LLUUID& mesh_id) -{ //protected by mSignal, no locking needed here +{ mPhysicsShapeRequests.insert(mesh_id); } @@ -2406,13 +2443,18 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo } else { - // From texture fetch code and applies here: + // From texture fetch code and may apply here: // // A warning about partial (HTTP 206) data. Some grid services // do *not* return a 'Content-Range' header in the response to // Range requests with a 206 status. We're forced to assume // we get what we asked for in these cases until we can fix // the services. + // + // May also need to deal with 200 status (full asset returned + // 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()); @@ -2422,7 +2464,9 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo if (data_size > 0) { // *TODO: Try to get rid of data copying and add interfaces - // that support BufferArray directly. + // 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); LLMeshRepository::sBytesReceived += data_size; @@ -2459,6 +2503,10 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) { if (is_retryable(status)) { + // *TODO: This and the other processFailure() methods should + // probably just fail hard (as llcorehttp has done the retries). + // Or we could implement a slow/forever retry class. + LL_WARNS(LOG_MESH) << "Error during mesh header handling. Reason: " << status.toString() << " (" << status.toHex() << "). Retrying." << LL_ENDL; @@ -3026,32 +3074,40 @@ void LLMeshRepository::notifyLoadedMeshes() //call completed callbacks on finished decompositions mDecompThread->notifyCompleted(); - - if (!mThread->mWaiting && mPendingRequests.empty()) - { //curl thread is churning, wait for it to go idle - return; - } - static std::string region_name("never name a region this"); + // For major operations, attempt to get the required locks + // without blocking and punt if they're not available. + { + LLMutexTrylock lock1(mMeshMutex); + LLMutexTrylock lock2(mThread->mMutex); - if (gAgent.getRegion()) - { //update capability url - if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) + static U32 hold_offs(0); + if (! lock1.isLocked() || ! lock2.isLocked()) { - region_name = gAgent.getRegion()->getName(); - mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh"); - mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2"); - mGetMeshVersion = mGetMesh2Capability.empty() ? 1 : 2; - LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name - << "', GetMesh2: " << mGetMesh2Capability - << ", GetMesh: " << mGetMeshCapability - << LL_ENDL; + // If we can't get the locks, skip and pick this up later. + ++hold_offs; + sMaxLockHoldoffs = llmax(sMaxLockHoldoffs, hold_offs); + return; + } + hold_offs = 0; + + if (gAgent.getRegion()) + { + // Update capability urls + static std::string region_name("never name a region this"); + + if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) + { + region_name = gAgent.getRegion()->getName(); + mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh"); + mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2"); + mGetMeshVersion = mGetMesh2Capability.empty() ? 1 : 2; + LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name + << "', GetMesh2: " << mGetMesh2Capability + << ", GetMesh: " << mGetMeshCapability + << LL_ENDL; + } } - } - - { - LLMutexLock lock1(mMeshMutex); - LLMutexLock lock2(mThread->mMutex); //popup queued error messages from background threads while (!mUploadErrorQ.empty()) diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 7e89f60bc3..c79278da1a 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -230,8 +230,6 @@ public: LLMutex* mHeaderMutex; LLCondition* mSignal; - volatile bool mWaiting; - //map of known mesh headers typedef std::map mesh_header_map; mesh_header_map mMeshHeader; @@ -494,19 +492,20 @@ public: //metrics static U32 sBytesReceived; - static U32 sMeshRequestCount; - static U32 sHTTPRequestCount; - static U32 sHTTPLargeRequestCount; - static U32 sHTTPRetryCount; - static U32 sHTTPErrorCount; + static U32 sMeshRequestCount; // Total request count, http or cached, all component types + static U32 sHTTPRequestCount; // Http GETs issued (not large) + static U32 sHTTPLargeRequestCount; // Http GETs issued for large requests + static U32 sHTTPRetryCount; // Total request retries whether successful or failed + static U32 sHTTPErrorCount; // Requests ending in error static U32 sLODPending; static U32 sLODProcessing; static U32 sCacheBytesRead; static U32 sCacheBytesWritten; - static U32 sCacheReads; + static U32 sCacheReads; static U32 sCacheWrites; + static U32 sMaxLockHoldoffs; // Maximum sequential locking failures - static LLDeadmanTimer sQuiescentTimer; // time-to-complete-mesh-downloads after significant events + static LLDeadmanTimer sQuiescentTimer; // Time-to-complete-mesh-downloads after significant events static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL); -- cgit v1.2.3 From 8b78642a475d9a1095970a3be39de47117b35d9f Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 7 Aug 2013 21:31:41 -0400 Subject: Create separate high/low water level limits for GetMesh and GetMesh2 capabilities. They should be independent now. --- indra/newview/llmeshrepository.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 59100a68f9..b19f6281e7 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -235,9 +235,13 @@ LLMeshRepository gMeshRepo; const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space const S32 REQUEST_HIGH_WATER_MIN = 32; -const S32 REQUEST_HIGH_WATER_MAX = 80; +const S32 REQUEST_HIGH_WATER_MAX = 200; const S32 REQUEST_LOW_WATER_MIN = 16; -const S32 REQUEST_LOW_WATER_MAX = 40; +const S32 REQUEST_LOW_WATER_MAX = 100; +const S32 REQUEST2_HIGH_WATER_MIN = 32; +const S32 REQUEST2_HIGH_WATER_MAX = 80; +const S32 REQUEST2_LOW_WATER_MIN = 16; +const S32 REQUEST2_LOW_WATER_MAX = 40; const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes to narrow/slow queue const long SMALL_MESH_XFER_TIMEOUT = 60L; // Seconds to complete xfer, small mesh downloads const long LARGE_MESH_XFER_TIMEOUT = 600L; // Seconds to complete xfer, large downloads @@ -376,8 +380,8 @@ void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res, volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0; volatile S32 LLMeshRepoThread::sActiveLODRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; -S32 LLMeshRepoThread::sRequestLowWater = REQUEST_LOW_WATER_MIN; -S32 LLMeshRepoThread::sRequestHighWater = REQUEST_HIGH_WATER_MIN; +S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN; +S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN; S32 LLMeshRepoThread::sRequestWaterLevel = 0; // Base handler class for all mesh users of llcorehttp. @@ -2985,18 +2989,21 @@ void LLMeshRepository::notifyLoadedMeshes() LLMeshRepoThread::sRequestHighWater = llclamp(2 * S32(LLMeshRepoThread::sMaxConcurrentRequests), REQUEST_HIGH_WATER_MIN, REQUEST_HIGH_WATER_MAX); + LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2, + REQUEST_LOW_WATER_MIN, + REQUEST_LOW_WATER_MAX); } else { // GetMesh2 operation with keepalives, etc. LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("Mesh2MaxConcurrentRequests"); LLMeshRepoThread::sRequestHighWater = llclamp(5 * S32(LLMeshRepoThread::sMaxConcurrentRequests), - REQUEST_HIGH_WATER_MIN, - REQUEST_HIGH_WATER_MAX); + REQUEST2_HIGH_WATER_MIN, + REQUEST2_HIGH_WATER_MAX); + LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2, + REQUEST2_LOW_WATER_MIN, + REQUEST2_LOW_WATER_MAX); } - LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2, - REQUEST_LOW_WATER_MIN, - REQUEST_LOW_WATER_MAX); //clean up completed upload threads for (std::vector::iterator iter = mUploads.begin(); iter != mUploads.end(); ) -- cgit v1.2.3 From f84a8104b80eacad78b30c09cb016774e5c459c5 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 15 Aug 2013 19:00:43 -0400 Subject: SH-4410 Internal Documentation. Update and correct the mutex/data lists to reflect current. Describe the functional flow of things for a single LOD request. Put together to-do list for follow on work. Knock down the low/high water limits for GetMesh a bit, 100/200 too high, 75/150 should be better, vis-a-vis pathological failures. --- indra/newview/llmeshrepository.cpp | 178 ++++++++++++++++++++++++++----------- 1 file changed, 127 insertions(+), 51 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index b19f6281e7..7d64a9f63f 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -79,10 +79,6 @@ #include -// [ Disclaimer: this documentation isn't by one of the original authors -// but by someone coming through later and extracting intent and function. -// Some of this will be wrong so use judgement. ] -// // Purpose // // The purpose of this module is to provide access between the viewer @@ -101,6 +97,7 @@ // * getMeshHeader (For structural details, see: // http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format) // * notifyLoadedMeshes +// * getSkinInfo // // Threads // @@ -108,7 +105,54 @@ // repo Overseeing worker thread associated with the LLMeshRepoThread class // decom Worker thread for mesh decomposition requests // core HTTP worker thread: does the work but doesn't intrude here -// uploadN 0-N temporary mesh upload threads +// uploadN 0-N temporary mesh upload threads (0-1 in practice) +// +// Sequence of Operations +// +// What follows is a description of the retrieval of one LOD for +// a new mesh object. Work is performed by a series of short, quick +// actions distributed over a number of threads. Each is meant +// to proceed without stalling and the whole forms a deep request +// pipeline to achieve throughput. Ellipsis indicates a return +// or break in processing which is resumed elsewhere. +// +// main thread repo thread (run() method) +// +// loadMesh() invoked to request LOD +// append LODRequest to mPendingRequests +// ... +// other mesh requests may be made +// ... +// notifyLoadedMeshes() invoked to stage work +// append HeaderRequest to mHeaderReqQ +// ... +// scan mHeaderReqQ +// issue 4096-byte GET for header +// ... +// onCompleted() invoked for GET +// data copied +// headerReceived() invoked +// LLSD parsed +// mMeshHeader, mMeshHeaderSize updated +// scan mPendingLOD for LOD request +// push LODRequest to mLODReqQ +// ... +// scan mLODReqQ +// fetchMeshLOD() invoked +// issue Byte-Range GET for LOD +// ... +// onCompleted() invoked for GET +// data copied +// lodReceived() invoked +// unpack data into LLVolume +// append LoadedMesh to mLoadedQ +// ... +// notifyLoadedMeshes() invoked again +// scan mLoadedQ +// notifyMeshLoaded() for LOD +// setMeshAssetLoaded() invoked for system volume +// notifyMeshLoaded() invoked for each interested object +// ... // // Mutexes // @@ -163,19 +207,19 @@ // // LLMeshRepository: // -// sBytesReceived -// sMeshRequestCount -// sHTTPRequestCount -// sHTTPLargeRequestCount -// sHTTPRetryCount -// sHTTPErrorCount -// sLODPending -// sLODProcessing -// sCacheBytesRead -// sCacheBytesWritten -// sCacheReads -// sCacheWrites -// mLoadingMeshes none rw.main.none, rw.main.mMeshMutex [4] +// sBytesReceived none rw.repo.none, ro.main.none [1] +// sMeshRequestCount " +// sHTTPRequestCount " +// sHTTPLargeRequestCount " +// sHTTPRetryCount " +// sHTTPErrorCount " +// sLODPending mMeshMutex [4] rw.main.mMeshMutex +// sLODProcessing Repo::mMutex rw.any.Repo::mMutex +// sCacheBytesRead none rw.repo.none, ro.main.none [1] +// sCacheBytesWritten " +// sCacheReads " +// sCacheWrites " +// mLoadingMeshes mMeshMutex [4] rw.main.none, rw.any.mMeshMutex // mSkinMap none rw.main.none // mDecompositionMap none rw.main.none // mPendingRequests mMeshMutex [4] rw.main.mMeshMutex @@ -199,25 +243,18 @@ // sMaxConcurrentRequests mMutex wo.main.none, ro.repo.none, ro.main.mMutex // mMeshHeader mHeaderMutex rw.repo.mHeaderMutex, ro.main.mHeaderMutex, ro.main.none [0] // mMeshHeaderSize mHeaderMutex rw.repo.mHeaderMutex -// mSkinRequests none rw.repo.none, rw.main.none [0] -// mSkinInfoQ none rw.repo.none, rw.main.none [0] -// mDecompositionRequests none rw.repo.none, rw.main.none [0] -// mPhysicsShapeRequests none rw.repo.none, rw.main.none [0] -// mDecompositionQ none rw.repo.none, rw.main.none [0] -// mHeaderReqQ mMutex ro.repo.none [3], rw.repo.mMutex, rw.any.mMutex -// mLODReqQ mMutex ro.repo.none [3], rw.repo.mMutex, rw.any.mMutex -// mUnavailableQ mMutex rw.repo.none [0], ro.main.none [3], rw.main.mMutex -// mLoadedQ mMutex rw.repo.mMutex, ro.main.none [3], rw.main.mMutex +// mSkinRequests mMutex rw.repo.mMutex, ro.repo.none [5] +// mSkinInfoQ none rw.repo.none, rw.main.mMutex [0] +// mDecompositionRequests mMutex rw.repo.mMutex, ro.repo.none [5] +// mPhysicsShapeRequests mMutex rw.repo.mMutex, ro.repo.none [5] +// mDecompositionQ none rw.repo.none, rw.main.mMutex [0] +// mHeaderReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex +// mLODReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex +// mUnavailableQ mMutex rw.repo.none [0], ro.main.none [5], rw.main.mMutex +// mLoadedQ mMutex rw.repo.mMutex, ro.main.none [5], rw.main.mMutex // mPendingLOD mMutex rw.repo.mMutex, rw.any.mMutex // mHttp* none rw.repo.none // -// LLPhysicsDecomp: -// -// mRequestQ -// mCurRequest -// mCompletedQ -// -// // QA/Development Testing // // Debug variable 'MeshUploadFakeErrors' takes a mask of bits that will @@ -230,15 +267,27 @@ // locally-generated 500 status. // 0x08 As with 0x04 but for the upload operation. // +// *TODO: Work list for followup actions: +// * Review anything marked as unsafe above, verify if there are real issues. +// * See if we can put ::run() into a hard sleep. May not actually perform better +// than the current scheme so be prepared for disappointment. You'll likely +// need to introduce a condition variable class that references a mutex in +// methods rather than derives from mutex which isn't correct. +// * On upload failures, make more information available to the alerting +// dialog. Get the structured information going into the log into a +// tree there. +// * Header parse failures come without much explanation. Elaborate. +// * Need a final failure state for requests that are retried and just won't +// complete. We can fail a LOD request, others we don't. LLMeshRepository gMeshRepo; const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space -const S32 REQUEST_HIGH_WATER_MIN = 32; -const S32 REQUEST_HIGH_WATER_MAX = 200; +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 = 100; -const S32 REQUEST2_HIGH_WATER_MIN = 32; +const S32 REQUEST_LOW_WATER_MAX = 75; +const S32 REQUEST2_HIGH_WATER_MIN = 32; // Limits for GetMesh2 regions const S32 REQUEST2_HIGH_WATER_MAX = 80; const S32 REQUEST2_LOW_WATER_MIN = 16; const S32 REQUEST2_LOW_WATER_MAX = 40; @@ -269,7 +318,7 @@ U32 LLMeshRepository::sCacheReads = 0; U32 LLMeshRepository::sCacheWrites = 0; U32 LLMeshRepository::sMaxLockHoldoffs = 0; -LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true); // true -> gather cpu metrics +LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, false); // true -> gather cpu metrics static S32 dump_num = 0; @@ -703,7 +752,7 @@ void LLMeshRepoThread::run() while (!LLApp::isQuitting()) { - // *TODO: Revise sleep/wake strategy and try to move away' + // *TODO: Revise sleep/wake strategy and try to move away // from polling operations in this thread. We can sleep // this thread hard when: // * All Http requests are serviced @@ -714,7 +763,8 @@ void LLMeshRepoThread::run() // * Physics shape request queue empty // We wake the thread when any of the above become untrue. // Will likely need a correctly-implemented condition variable to do this. - + // On the other hand, this may actually be an effective and efficient scheme... + mSignal->wait(); if (LLApp::isQuitting()) @@ -810,7 +860,7 @@ void LLMeshRepoThread::run() // holding lock, try next list // *TODO: For UI/debug-oriented lists, we might drop the fine- - // grained locking as there's lowered expectations of smoothness + // grained locking as there's a lowered expectation of smoothness // in these cases. if (! mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) { @@ -2303,24 +2353,26 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp void LLMeshRepoThread::notifyLoadedMeshes() { + bool update_metrics(false); + if (!mMutex) { return; } - if (!mLoadedQ.empty() || !mUnavailableQ.empty()) - { - // Ping time-to-load metrics for mesh download operations. - LLMeshRepository::metricsProgress(0); - } - while (!mLoadedQ.empty()) { mMutex->lock(); + if (mLoadedQ.empty()) + { + mMutex->unlock(); + break; + } LoadedMesh mesh = mLoadedQ.front(); mLoadedQ.pop(); mMutex->unlock(); + update_metrics = true; if (mesh.mVolume && mesh.mVolume->getNumVolumeFaces() > 0) { gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume); @@ -2335,10 +2387,17 @@ void LLMeshRepoThread::notifyLoadedMeshes() while (!mUnavailableQ.empty()) { mMutex->lock(); + if (mUnavailableQ.empty()) + { + mMutex->unlock(); + break; + } + LODRequest req = mUnavailableQ.front(); mUnavailableQ.pop(); mMutex->unlock(); - + + update_metrics = true; gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); } @@ -2353,6 +2412,13 @@ void LLMeshRepoThread::notifyLoadedMeshes() gMeshRepo.notifyDecompositionReceived(mDecompositionQ.front()); mDecompositionQ.pop(); } + + if (update_metrics) + { + // Ping time-to-load metrics for mesh download operations. + LLMeshRepository::metricsProgress(0); + } + } S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod) @@ -2461,6 +2527,12 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo // speculative loads aren't done. static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + if (par_status != status) + { + LL_WARNS_ONCE(LOG_MESH) << "Non-206 successful status received for fetch: " + << status.toHex() << LL_ENDL; + } + LLCore::BufferArray * body(response->getBody()); S32 data_size(body ? body->size() : 0); U8 * data(NULL); @@ -2995,7 +3067,8 @@ void LLMeshRepository::notifyLoadedMeshes() } else { - // GetMesh2 operation with keepalives, etc. + // GetMesh2 operation with keepalives, etc. With pipelining, + // we'll increase this. LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("Mesh2MaxConcurrentRequests"); LLMeshRepoThread::sRequestHighWater = llclamp(5 * S32(LLMeshRepoThread::sMaxConcurrentRequests), REQUEST2_HIGH_WATER_MIN, @@ -3083,7 +3156,10 @@ void LLMeshRepository::notifyLoadedMeshes() mDecompThread->notifyCompleted(); // For major operations, attempt to get the required locks - // without blocking and punt if they're not available. + // without blocking and punt if they're not available. The + // longest run of holdoffs is kept in sMaxLockHoldoffs just + // to collect the data. In testing, I've never seen a value + // greater than 2 (written to log on exit). { LLMutexTrylock lock1(mMeshMutex); LLMutexTrylock lock2(mThread->mMutex); -- cgit v1.2.3 From 42e3d55fb2d0579b968ff8df20e9a814229b3e72 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 28 Aug 2013 18:55:29 -0400 Subject: Add conditional LLFastTimer blocks to stallable main thread methods --- indra/newview/llmeshrepository.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 6dc834e852..0599fcfd2d 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -67,6 +67,7 @@ #include "llfoldertype.h" #include "llviewerparcelmgr.h" #include "lluploadfloaterobservers.h" +#include "llfasttimer.h" #include "boost/lexical_cast.hpp" @@ -125,6 +126,16 @@ static bool metrics_inited(false); static boost::signals2::connection metrics_teleport_connection; static unsigned int metrics_teleport_start_count(0); static void metrics_teleport_started(); +static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch"); + +// Enable here or in build environment to get fasttimer data on mesh fetches. +#define LL_FASTTIMER_MESH_ENABLE 1 +#if LL_FASTTIMER_MESH_ENABLE +#define MESH_FASTTIMER_DEFBLOCK LLFastTimer meshtimer(FTM_MESH_FETCH) +#else +#define MESH_FASTTIMER_DEFBLOCK +#endif // LL_FASTTIMER_MESH_ENABLE + //get the number of bytes resident in memory for given volume U32 get_volume_memory_size(const LLVolume* volume) @@ -2403,6 +2414,8 @@ S32 LLMeshRepository::update() S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod) { + MESH_FASTTIMER_DEFBLOCK; + // Manage time-to-load metrics for mesh download operations. metricsProgress(1); @@ -2484,6 +2497,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para void LLMeshRepository::notifyLoadedMeshes() { //called from main thread + MESH_FASTTIMER_DEFBLOCK; LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests"); @@ -2791,6 +2805,8 @@ S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj) { + MESH_FASTTIMER_DEFBLOCK; + if (mesh_id.notNull()) { skin_map::iterator iter = mSkinMap.find(mesh_id); @@ -2817,6 +2833,8 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id) { + MESH_FASTTIMER_DEFBLOCK; + if (mesh_id.notNull()) { LLModel::Decomposition* decomp = NULL; @@ -2844,6 +2862,8 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id) LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id) { + MESH_FASTTIMER_DEFBLOCK; + LLModel::Decomposition* ret = NULL; if (mesh_id.notNull()) @@ -2906,6 +2926,8 @@ bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id) LLSD& LLMeshRepository::getMeshHeader(const LLUUID& mesh_id) { + MESH_FASTTIMER_DEFBLOCK; + return mThread->getMeshHeader(mesh_id); } -- cgit v1.2.3 From d706b5717785a318c053055c49589b16f9633681 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 29 Aug 2013 13:04:05 -0400 Subject: Document the fast timer testing apparatus a bit. --- indra/newview/llmeshrepository.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 2f43ee3ccb..3c964160b5 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -268,6 +268,9 @@ // locally-generated 500 status. // 0x08 As with 0x04 but for the upload operation. // +// For major changes, see the LL_MESH_FASTTIMER_ENABLE below and +// instructions for looking for frame stalls using fast timers. +// // *TODO: Work list for followup actions: // * Review anything marked as unsafe above, verify if there are real issues. // * See if we can put ::run() into a hard sleep. May not actually perform better @@ -346,15 +349,25 @@ static unsigned int metrics_teleport_start_count = 0; boost::signals2::connection metrics_teleport_started_signal; static void teleport_started(); static bool is_retryable(LLCore::HttpStatus status); -static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch"); // Enable here or in build environment to get fasttimer data on mesh fetches. -#define LL_FASTTIMER_MESH_ENABLE 1 -#if LL_FASTTIMER_MESH_ENABLE +// +// Typically, this is used to perform A/B testing using the +// fasttimer console (shift-ctrl-9). This is done by looking +// for stalls due to lock contention between the main thread +// and the repository and HTTP code. In a release viewer, +// these appear as ping-time or worse spikes in frame time. +// With this instrumentation enabled, a stall will appear +// under the 'Mesh Fetch' timer which will be either top-level +// or under 'Render' time. +#define LL_MESH_FASTTIMER_ENABLE 1 +#if LL_MESH_FASTTIMER_ENABLE +static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch"); + #define MESH_FASTTIMER_DEFBLOCK LLFastTimer meshtimer(FTM_MESH_FETCH) #else #define MESH_FASTTIMER_DEFBLOCK -#endif // LL_FASTTIMER_MESH_ENABLE +#endif // LL_MESH_FASTTIMER_ENABLE //get the number of bytes resident in memory for given volume U32 get_volume_memory_size(const LLVolume* volume) -- cgit v1.2.3 From 6a1f91fa3e0f82ad58fd13add8a093d88eff2c70 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 6 Sep 2013 16:37:31 -0400 Subject: SH-4478 Corrected/updated error handling for all retrieval operations. In case of HTTP errors or parsing/processing errors, fail the fetch request rather than do a retry spin. Add logging for all such failure paths. Added a development/debug flag to create probabilistic failures to test these modes and general error recovery by higher-level layers. --- indra/newview/llmeshrepository.cpp | 277 +++++++++++++++++-------------------- 1 file changed, 126 insertions(+), 151 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 3c964160b5..f6a85ac94f 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -44,6 +44,7 @@ #include "lleconomy.h" #include "llimagej2c.h" #include "llhost.h" +#include "llmath.h" #include "llnotificationsutil.h" #include "llsd.h" #include "llsdutil_math.h" @@ -284,6 +285,52 @@ // * Need a final failure state for requests that are retried and just won't // complete. We can fail a LOD request, others we don't. + +// -------------------------------------------------------------------------- +// Development/Debug/QA Tools +// +// Enable here or in build environment to get fasttimer data on mesh fetches. +// +// Typically, this is used to perform A/B testing using the +// fasttimer console (shift-ctrl-9). This is done by looking +// for stalls due to lock contention between the main thread +// and the repository and HTTP code. In a release viewer, +// these appear as ping-time or worse spikes in frame time. +// With this instrumentation enabled, a stall will appear +// under the 'Mesh Fetch' timer which will be either top-level +// or under 'Render' time. +#define LL_MESH_FASTTIMER_ENABLE 1 +#if LL_MESH_FASTTIMER_ENABLE +static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch"); + +#define MESH_FASTTIMER_DEFBLOCK LLFastTimer meshtimer(FTM_MESH_FETCH) +#else +#define MESH_FASTTIMER_DEFBLOCK +#endif // LL_MESH_FASTTIMER_ENABLE + + +// Random failure testing for development/QA. +// +// Set the MESH_*_FAILED macros to either 'false' or to +// an invocation of MESH_RANDOM_NTH_TRUE() with some +// suitable number. In production, all must be false. +// +// Example: +// #define MESH_HTTP_RESPONSE_FAILED MESH_RANDOM_NTH_TRUE(9) + +// 1-in-N calls will test true +#define MESH_RANDOM_NTH_TRUE(_N) ( ll_rand(S32(_N)) == 0 ) + +#define MESH_HTTP_RESPONSE_FAILED false +#define MESH_HEADER_PROCESS_FAILED false +#define MESH_LOD_PROCESS_FAILED false +#define MESH_SKIN_INFO_PROCESS_FAILED false +#define MESH_DECOMP_PROCESS_FAILED false +#define MESH_PHYS_SHAPE_PROCESS_FAILED false + +// -------------------------------------------------------------------------- + + LLMeshRepository gMeshRepo; const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space @@ -348,27 +395,7 @@ const char * const LOG_MESH = "Mesh"; static unsigned int metrics_teleport_start_count = 0; boost::signals2::connection metrics_teleport_started_signal; static void teleport_started(); -static bool is_retryable(LLCore::HttpStatus status); -// Enable here or in build environment to get fasttimer data on mesh fetches. -// -// Typically, this is used to perform A/B testing using the -// fasttimer console (shift-ctrl-9). This is done by looking -// for stalls due to lock contention between the main thread -// and the repository and HTTP code. In a release viewer, -// these appear as ping-time or worse spikes in frame time. -// With this instrumentation enabled, a stall will appear -// under the 'Mesh Fetch' timer which will be either top-level -// or under 'Render' time. -#define LL_MESH_FASTTIMER_ENABLE 1 -#if LL_MESH_FASTTIMER_ENABLE -static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch"); - -#define MESH_FASTTIMER_DEFBLOCK LLFastTimer meshtimer(FTM_MESH_FETCH) -#else -#define MESH_FASTTIMER_DEFBLOCK -#endif // LL_MESH_FASTTIMER_ENABLE - //get the number of bytes resident in memory for given volume U32 get_volume_memory_size(const LLVolume* volume) { @@ -815,7 +842,7 @@ void LLMeshRepoThread::run() mLODReqQ.pop(); LLMeshRepository::sLODProcessing--; mMutex->unlock(); - if (!fetchMeshLOD(req.mMeshParams, req.mLOD))//failed, resubmit + if (!fetchMeshLOD(req.mMeshParams, req.mLOD)) // failed, resubmit { mMutex->lock(); mLODReqQ.push(req) ; @@ -1548,6 +1575,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); + // *NOTE: Allowing a re-request, not marking as unavailable. Is that correct? } } else @@ -2529,7 +2557,7 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo LLMeshRepository::sHTTPRetryCount += retries; LLCore::HttpStatus status(response->getStatus()); - if (! status) + if (! status || MESH_HTTP_RESPONSE_FAILED) { processFailure(status); ++LLMeshRepository::sHTTPErrorCount; @@ -2600,39 +2628,37 @@ LLMeshHeaderHandler::~LLMeshHeaderHandler() void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) { - if (is_retryable(status)) - { - // *TODO: This and the other processFailure() methods should - // probably just fail hard (as llcorehttp has done the retries). - // Or we could implement a slow/forever retry class. - - LL_WARNS(LOG_MESH) << "Error during mesh header handling. Reason: " << status.toString() - << " (" << status.toHex() << "). Retrying." - << LL_ENDL; - LLMeshRepoThread::HeaderRequest req(mMeshParams); - LLMutexLock lock(gMeshRepo.mThread->mMutex); - gMeshRepo.mThread->mHeaderReqQ.push(req); - } - else + LL_WARNS(LOG_MESH) << "Error during mesh header handling. ID: " << mMeshParams.getSculptID() + << ", Reason: " << status.toString() + << " (" << status.toHex() << "). Not retrying." + << LL_ENDL; + + // 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) { - // *TODO: Mark mesh unavailable - LL_WARNS(LOG_MESH) << "Error during mesh header handling. Reason: " << status.toString() - << " (" << status.toHex() << "). Not retrying." - << LL_ENDL; + gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i)); } } void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) { LLUUID mesh_id = mMeshParams.getSculptID(); - bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); + bool success = (! MESH_HEADER_PROCESS_FAILED) && gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); llassert(success); if (! success) { - // *TODO: Mark mesh unavailable - // *TODO: Get real reason for parse failure here + // *TODO: Get real reason for parse failure here. Might we want to retry? LL_WARNS(LOG_MESH) << "Unable to parse mesh header. ID: " << mesh_id + << ", Unknown reason. Not retrying." << LL_ENDL; + + // 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) { @@ -2708,29 +2734,18 @@ LLMeshLODHandler::~LLMeshLODHandler() void LLMeshLODHandler::processFailure(LLCore::HttpStatus status) { - if (is_retryable(status)) - { - LL_WARNS(LOG_MESH) << "Error during mesh header handling. Reason: " << status.toString() - << " (" << status.toHex() << "). Retrying." - << LL_ENDL; - { - LLMutexLock lock(gMeshRepo.mThread->mMutex); + LL_WARNS(LOG_MESH) << "Error during mesh LOD handling. ID: " << mMeshParams.getSculptID() + << ", Reason: " << status.toString() + << " (" << status.toHex() << "). Not retrying." + << LL_ENDL; - gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD); - } - } - else - { - // *TODO: Mark mesh unavailable - LL_WARNS(LOG_MESH) << "Error during mesh LOD handling. Reason: " << status.toString() - << " (" << status.toHex() << "). Not retrying." - << LL_ENDL; - } + LLMutexLock lock(gMeshRepo.mThread->mMutex); + gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); } void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) { - if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size)) + if ((! MESH_LOD_PROCESS_FAILED) && gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size)) { // good fetch from sim, write to VFS for caching LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE); @@ -2746,7 +2761,14 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 da ++LLMeshRepository::sCacheWrites; } } - // *TODO: Mark mesh unavailable on error + else + { + LL_WARNS(LOG_MESH) << "Error during mesh LOD processing. ID: " << mMeshParams.getSculptID() + << ", Unknown reason. Not retrying." + << LL_ENDL; + LLMutexLock lock(gMeshRepo.mThread->mMutex); + gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); + } } LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler() @@ -2756,29 +2778,18 @@ LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler() void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) { - if (is_retryable(status)) - { - LL_WARNS(LOG_MESH) << "Error during mesh skin info handling. Reason: " << status.toString() - << " (" << status.toHex() << "). Retrying." - << LL_ENDL; - { - LLMutexLock lock(gMeshRepo.mThread->mMutex); + LL_WARNS(LOG_MESH) << "Error during mesh skin info handling. ID: " << mMeshID + << ", Reason: " << status.toString() + << " (" << status.toHex() << "). Not retrying." + << LL_ENDL; - gMeshRepo.mThread->loadMeshSkinInfo(mMeshID); - } - } - else - { - // *TODO: Mark mesh unavailable on error - LL_WARNS(LOG_MESH) << "Error during mesh skin info handling. Reason: " << status.toString() - << " (" << status.toHex() << "). Not retrying." - << LL_ENDL; - } + // *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) { - if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) + if ((! MESH_SKIN_INFO_PROCESS_FAILED) && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) { // good fetch from sim, write to VFS for caching LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); @@ -2794,7 +2805,13 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S file.write(data, size); } } - // *TODO: Mark mesh unavailable on error + else + { + LL_WARNS(LOG_MESH) << "Error during mesh skin info processing. ID: " << mMeshID + << ", Unknown reason. Not retrying." + << LL_ENDL; + // *TODO: Mark mesh unavailable on error + } } LLMeshDecompositionHandler::~LLMeshDecompositionHandler() @@ -2804,29 +2821,17 @@ LLMeshDecompositionHandler::~LLMeshDecompositionHandler() void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status) { - if (is_retryable(status)) - { - LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling. Reason: " << status.toString() - << " (" << status.toHex() << "). Retrying." - << LL_ENDL; - { - LLMutexLock lock(gMeshRepo.mThread->mMutex); - - gMeshRepo.mThread->loadMeshDecomposition(mMeshID); - } - } - else - { - // *TODO: Mark mesh unavailable on error - LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling. Reason: " << status.toString() - << " (" << status.toHex() << "). Not retrying." - << LL_ENDL; - } + LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling. ID: " << mMeshID + << ", Reason: " << status.toString() + << " (" << status.toHex() << "). 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) { - if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) + if ((! MESH_DECOMP_PROCESS_FAILED) && gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) { // good fetch from sim, write to VFS for caching LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); @@ -2842,7 +2847,13 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * da file.write(data, size); } } - // *TODO: Mark mesh unavailable on error + 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() @@ -2852,29 +2863,16 @@ LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler() void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status) { - if (is_retryable(status)) - { - LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling. Reason: " << status.toString() - << " (" << status.toHex() << "). Retrying." - << LL_ENDL; - { - LLMutexLock lock(gMeshRepo.mThread->mMutex); - - gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID); - } - } - else - { - // *TODO: Mark mesh unavailable on error - LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling. Reason: " << status.toString() - << " (" << status.toHex() << "). Not retrying." - << LL_ENDL; - } + LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling. ID: " << mMeshID + << ", Reason: " << status.toString() + << " (" << status.toHex() << "). Not retrying." + << LL_ENDL; + // *TODO: Mark mesh unavailable on error } void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) { - if (gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, 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); @@ -2890,7 +2888,13 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * dat file.write(data, size); } } - // *TODO: Mark mesh unavailable on error + else + { + LL_WARNS(LOG_MESH) << "Error during mesh physics shape processing. ID: " << mMeshID + << ", Unknown reason. Not retrying." + << LL_ENDL; + // *TODO: Mark mesh unavailable on error + } } LLMeshRepository::LLMeshRepository() @@ -3000,7 +3004,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para // Manage time-to-load metrics for mesh download operations. metricsProgress(1); - if (detail < 0 || detail > 4) + if (detail < 0 || detail >= 4) { return detail; } @@ -4439,32 +4443,3 @@ void teleport_started() LLMeshRepository::metricsStart(); } -// *TODO: This comes from an edit in viewer-cat. Unify this once that's -// available everywhere. -bool is_retryable(LLCore::HttpStatus status) -{ - static const LLCore::HttpStatus cant_connect(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); - static const LLCore::HttpStatus cant_res_proxy(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); - static const LLCore::HttpStatus cant_res_host(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); - static const LLCore::HttpStatus send_error(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR); - static const LLCore::HttpStatus recv_error(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR); - static const LLCore::HttpStatus upload_failed(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_UPLOAD_FAILED); - static const LLCore::HttpStatus op_timedout(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); - static const LLCore::HttpStatus post_error(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_HTTP_POST_ERROR); - static const LLCore::HttpStatus partial_file(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_PARTIAL_FILE); - static const LLCore::HttpStatus inv_cont_range(LLCore::HttpStatus::LLCORE, LLCore::HE_INV_CONTENT_RANGE_HDR); - - return ((! status) && - ((status.isHttpStatus() && status.mType >= 499 && status.mType <= 599) || // Include special 499 in retryables - status == cant_connect || // Connection reset/endpoint problems - status == cant_res_proxy || // DNS problems - status == cant_res_host || // DNS problems - status == send_error || // General socket problems - status == recv_error || // General socket problems - status == upload_failed || // Transport problem - status == op_timedout || // Timer expired - status == post_error || // Transport problem - status == partial_file || // Data inconsistency in response - status == inv_cont_range)); // Short data read disagrees with content-range -} - -- cgit v1.2.3 From 2e8e40cf7974a4ab6ca13d264104dbb8b80419b7 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 11 Sep 2013 18:00:55 -0400 Subject: SH-4489 New debug/dev settings for control over new mesh behavior Added 'MeshUseGetMesh1' and 'MeshUseHttpRetryAfter' debug settings to control mesh transport behavior. First forces the use of the legacy mesh fetch style with high concurrency and connection churn. The second, on by default, honors Retry-After values if they are reasonable. If off or unreasonable, internal delay times are used. --- indra/newview/app_settings/settings.xml | 22 ++++++++++++++++++++++ indra/newview/llmeshrepository.cpp | 8 +++++++- 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 47a2f402af..9948f9974a 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9979,6 +9979,28 @@ U32 Value 32 + + MeshUseHttpRetryAfter + + Comment + If TRUE, use Retry-After response headers when rescheduling a mesh request that fails with an HTTP 503 status. + Persist + 1 + Type + Boolean + Value + 1 + + MeshUseGetMesh1 + + Comment + If TRUE, use the legacy GetMesh capability for mesh download requests. + Persist + 1 + Type + Boolean + Value + 0 RunMultipleThreads diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index f6a85ac94f..507797a85d 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -743,8 +743,10 @@ LLMeshRepoThread::LLMeshRepoThread() mHttpRequest = new LLCore::HttpRequest; mHttpOptions = new LLCore::HttpOptions; mHttpOptions->setTransferTimeout(SMALL_MESH_XFER_TIMEOUT); + mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); mHttpLargeOptions = new LLCore::HttpOptions; mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT); + mHttpLargeOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); mHttpHeaders = new LLCore::HttpHeaders; mHttpHeaders->append("Accept", "application/vnd.ll.mesh"); mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH2); @@ -1828,6 +1830,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, mHttpRequest = new LLCore::HttpRequest; mHttpOptions = new LLCore::HttpOptions; mHttpOptions->setTransferTimeout(mMeshUploadTimeOut); + mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); mHttpHeaders = new LLCore::HttpHeaders; mHttpHeaders->append("Content-Type", "application/llsd+xml"); mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS); @@ -3211,13 +3214,16 @@ void LLMeshRepository::notifyLoadedMeshes() if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) { + const bool use_v1(gSavedSettings.getBOOL("MeshUseGetMesh1")); + region_name = gAgent.getRegion()->getName(); mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh"); mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2"); - mGetMeshVersion = mGetMesh2Capability.empty() ? 1 : 2; + mGetMeshVersion = (mGetMesh2Capability.empty() || use_v1) ? 1 : 2; LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name << "', GetMesh2: " << mGetMesh2Capability << ", GetMesh: " << mGetMeshCapability + << ", using version: " << mGetMeshVersion << LL_ENDL; } } -- cgit v1.2.3 From 622eae65551df9a4ca6843a6a657777ff5e2140e Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 11 Sep 2013 19:21:31 -0400 Subject: SH-4490 More 'humane' error code presentation from llcorehttp callers Added toTerseString() conversion on HttpStatus to generate a string that's more descriptive than the hex value of the HttpStatus value but still forms a short, searchable token (e.g. "Http_503" or "Core_7"). Using this throughout the viewer now, no live cases of toHex(), I believe. --- indra/newview/llmeshrepository.cpp | 32 ++++++++++++++++---------------- indra/newview/lltexturefetch.cpp | 12 ++++++------ 2 files changed, 22 insertions(+), 22 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 507797a85d..353e7e9a7f 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -688,7 +688,7 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content, // Log details. LL_WARNS(LOG_MESH) << "Error in stage: " << stage << ", Reason: " << status.toString() - << " (" << status.toHex() << ")" << LL_ENDL; + << " (" << status.toTerseString() << ")" << LL_ENDL; if (content.has("error")) { const LLSD& err = content["error"]; @@ -1205,7 +1205,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) { LL_WARNS(LOG_MESH) << "HTTP GET request failed for skin info on mesh " << mID << ". Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toHex() << ")" + << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; delete handler; ret = false; @@ -1298,7 +1298,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) { LL_WARNS(LOG_MESH) << "HTTP GET request failed for decomposition mesh " << mID << ". Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toHex() << ")" + << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; delete handler; ret = false; @@ -1389,7 +1389,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) { LL_WARNS(LOG_MESH) << "HTTP GET request failed for physics shape on mesh " << mID << ". Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toHex() << ")" + << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; delete handler; ret = false; @@ -1486,7 +1486,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) { LL_WARNS(LOG_MESH) << "HTTP GET request failed for mesh header " << mID << ". Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toHex() << ")" + << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; delete handler; retval = false; @@ -1568,7 +1568,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { LL_WARNS(LOG_MESH) << "HTTP GET request failed for LOD on mesh " << mID << ". Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toHex() << ")" + << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; delete handler; retval = false; @@ -2196,7 +2196,7 @@ void LLMeshUploadThread::doWholeModelUpload() mHttpStatus = mHttpRequest->getStatus(); LL_WARNS(LOG_MESH) << "Couldn't issue request for full model upload. Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toHex() << ")" + << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; } else @@ -2244,7 +2244,7 @@ void LLMeshUploadThread::requestWholeModelFee() mHttpStatus = mHttpRequest->getStatus(); LL_WARNS(LOG_MESH) << "Couldn't issue request for model fee. Reason: " << mHttpStatus.toString() - << " (" << mHttpStatus.toHex() << ")" + << " (" << mHttpStatus.toTerseString() << ")" << LL_ENDL; } else @@ -2285,7 +2285,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp if (! status) { LL_WARNS(LOG_MESH) << "Upload failed. Reason: " << reason - << " (" << status.toHex() << ")" + << " (" << status.toTerseString() << ")" << LL_ENDL; // Build a fake body for the alert generator @@ -2349,7 +2349,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp if (! status) { LL_WARNS(LOG_MESH) << "Fee request failed. Reason: " << reason - << " (" << status.toHex() << ")" + << " (" << status.toTerseString() << ")" << LL_ENDL; // Build a fake body for the alert generator @@ -2584,7 +2584,7 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo if (par_status != status) { LL_WARNS_ONCE(LOG_MESH) << "Non-206 successful status received for fetch: " - << status.toHex() << LL_ENDL; + << status.toTerseString() << LL_ENDL; } LLCore::BufferArray * body(response->getBody()); @@ -2633,7 +2633,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) { LL_WARNS(LOG_MESH) << "Error during mesh header handling. ID: " << mMeshParams.getSculptID() << ", Reason: " << status.toString() - << " (" << status.toHex() << "). Not retrying." + << " (" << status.toTerseString() << "). Not retrying." << LL_ENDL; // Can't get the header so none of the LODs will be available @@ -2739,7 +2739,7 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status) { LL_WARNS(LOG_MESH) << "Error during mesh LOD handling. ID: " << mMeshParams.getSculptID() << ", Reason: " << status.toString() - << " (" << status.toHex() << "). Not retrying." + << " (" << status.toTerseString() << "). Not retrying." << LL_ENDL; LLMutexLock lock(gMeshRepo.mThread->mMutex); @@ -2783,7 +2783,7 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) { LL_WARNS(LOG_MESH) << "Error during mesh skin info handling. ID: " << mMeshID << ", Reason: " << status.toString() - << " (" << status.toHex() << "). Not retrying." + << " (" << status.toTerseString() << "). Not retrying." << LL_ENDL; // *TODO: Mark mesh unavailable on error. For now, simply leave @@ -2826,7 +2826,7 @@ void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status) { LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling. ID: " << mMeshID << ", Reason: " << status.toString() - << " (" << status.toHex() << "). Not retrying." + << " (" << status.toTerseString() << "). Not retrying." << LL_ENDL; // *TODO: Mark mesh unavailable on error. For now, simply leave // request unfulfilled rather than retry forever. @@ -2868,7 +2868,7 @@ void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status) { LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling. ID: " << mMeshID << ", Reason: " << status.toString() - << " (" << status.toHex() << "). Not retrying." + << " (" << status.toTerseString() << "). Not retrying." << LL_ENDL; // *TODO: Mark mesh unavailable on error } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 141198bc16..bcb55c4bbe 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1552,7 +1552,7 @@ bool LLTextureFetchWorker::doWork(S32 param) else { llinfos << "HTTP GET failed for: " << mUrl - << " Status: " << mGetStatus.toHex() + << " Status: " << mGetStatus.toTerseString() << " Reason: '" << mGetReason << "'" << llendl; } @@ -1896,7 +1896,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe LLCore::HttpStatus status(response->getStatus()); LL_DEBUGS("Texture") << "HTTP COMPLETE: " << mID - << " status: " << status.toHex() + << " status: " << status.toTerseString() << " '" << status.toString() << "'" << llendl; // unsigned int offset(0), length(0), full_length(0); @@ -1912,7 +1912,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe success = false; std::string reason(status.toString()); setGetStatus(status, reason); - llwarns << "CURL GET FAILED, status: " << status.toHex() + llwarns << "CURL GET FAILED, status: " << status.toTerseString() << " reason: " << reason << llendl; } else @@ -3781,7 +3781,7 @@ public: else { LL_WARNS("Texture") << "Error delivering asset metrics to grid. Status: " - << status.toHex() + << status.toTerseString() << ", Reason: " << status.toString() << LL_ENDL; } } @@ -4470,7 +4470,7 @@ S32 LLTextureFetchDebugger::fillCurlQueue() LL_WARNS("Texture") << "Couldn't issue HTTP request in debugger for texture " << mFetchingHistory[i].mID - << ", status: " << status.toHex() + << ", status: " << status.toTerseString() << " reason: " << status.toString() << LL_ENDL; mFetchingHistory[i].mCurlState = FetchEntry::CURL_DONE; @@ -4863,7 +4863,7 @@ void LLTextureFetchDebugger::callbackHTTP(FetchEntry & fetch, LLCore::HttpRespon else //failed { llinfos << "Fetch Debugger : CURL GET FAILED, ID = " << fetch.mID - << ", status: " << status.toHex() + << ", status: " << status.toTerseString() << " reason: " << status.toString() << llendl; } } -- cgit v1.2.3 From 99c60b83f15a3c40ecf0e5f144cfc060b37c4cf8 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 12 Sep 2013 20:15:20 -0400 Subject: SH-4489 New debug/dev settings for control over new mesh behavior While giving myself a full project code review, found a bug in the MeshUseGetMesh1 setting. Mostly defaulted to old configuration but used the GetMesh2 caps which would have been a huge DoS resource sink. Did some documentation maintenance as well while I was in there. More for the to-do list, etc. --- indra/newview/app_settings/settings.xml | 4 ++-- indra/newview/llmeshrepository.cpp | 17 +++++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 9948f9974a..60fc2d349d 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9983,7 +9983,7 @@ MeshUseHttpRetryAfter Comment - If TRUE, use Retry-After response headers when rescheduling a mesh request that fails with an HTTP 503 status. + If TRUE, use Retry-After response headers when rescheduling a mesh request that fails with an HTTP 503 status. Static. Persist 1 Type @@ -9994,7 +9994,7 @@ MeshUseGetMesh1 Comment - If TRUE, use the legacy GetMesh capability for mesh download requests. + If TRUE, use the legacy GetMesh capability for mesh download requests. Semi-dynamic (read at region crossings). Persist 1 Type diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 353e7e9a7f..c3d149db9a 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -282,10 +282,15 @@ // dialog. Get the structured information going into the log into a // tree there. // * Header parse failures come without much explanation. Elaborate. -// * Need a final failure state for requests that are retried and just won't -// complete. We can fail a LOD request, others we don't. - - +// * Work queue for uploads? Any need for this or is the current scheme good +// enough? +// * Various temp buffers used in VFS I/O might be allocated once or even +// statically. Look for some wins here. +// * Move data structures holding mesh data used by main thread into main- +// thread-only access so that no locking is needed. May require duplication +// of some data so that worker thread has a minimal data set to guide +// operations. +// // -------------------------------------------------------------------------- // Development/Debug/QA Tools // @@ -1052,7 +1057,7 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) if (gAgent.getRegion()) { - if (! gMeshRepo.mGetMesh2Capability.empty()) + if (! gMeshRepo.mGetMesh2Capability.empty() && gMeshRepo.mGetMeshVersion > 1) { http_url = gMeshRepo.mGetMesh2Capability; } @@ -2181,7 +2186,7 @@ void LLMeshUploadThread::doWholeModelUpload() LLCore::BufferArray * ba = new LLCore::BufferArray; LLCore::BufferArrayStream bas(ba); LLSDSerialize::toXML(body, bas); - // LLSDSerialize::toXML(mModelData, bas); // <- This will generate a convenient upload error + // LLSDSerialize::toXML(mModelData, bas); // <- Enabling this will generate a convenient upload error LLCore::HttpHandle handle = mHttpRequest->requestPost(mHttpPolicyClass, mHttpPriority, mWholeModelUploadURL, -- cgit v1.2.3 From 195d319f65239238577ae15c22188da3839ae5cf Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 18 Sep 2013 18:44:41 -0400 Subject: SH-4492 Create a useful README for llcorehttp Last bit for this release. Describe stream adapters and how to select a policy class. Slight changes to setup code to make reality reflect documentation. --- indra/newview/llappcorehttp.cpp | 38 +++++++++------- indra/newview/llappcorehttp.h | 97 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 16 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 01317fe32f..70dcffefb2 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -51,6 +51,11 @@ static const struct const char * mUsage; } init_data[] = // Default and dynamic values for classes { + { + LLAppCoreHttp::AP_DEFAULT, 8, 8, 8, 0, + "", + "other" + }, { LLAppCoreHttp::AP_TEXTURE, 8, 1, 12, 0, "TextureFetchConcurrency", @@ -75,6 +80,11 @@ static const struct LLAppCoreHttp::AP_UPLOADS, 2, 1, 8, 0, "", "asset upload" + }, + { + LLAppCoreHttp::AP_LONG_POLL, 32, 32, 32, 0, + "", + "long poll" } }; @@ -154,25 +164,21 @@ void LLAppCoreHttp::init() { const EAppPolicy policy(init_data[i].mPolicy); - // Create a policy class but use default for texture for now. - // This also has the side-effect of initializing the default - // class to desired values. - if (AP_TEXTURE == policy) + if (AP_DEFAULT == policy) { - mPolicies[policy] = mPolicies[AP_DEFAULT]; + // Pre-created + continue; } - else + + mPolicies[policy] = LLCore::HttpRequest::createPolicyClass(); + if (! mPolicies[policy]) { - mPolicies[policy] = LLCore::HttpRequest::createPolicyClass(); - if (! mPolicies[policy]) - { - // Use default policy (but don't accidentally modify default) - LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage - << ". Using default policy." - << LL_ENDL; - mPolicies[policy] = mPolicies[AP_DEFAULT]; - continue; - } + // Use default policy (but don't accidentally modify default) + LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage + << ". Using default policy." + << LL_ENDL; + mPolicies[policy] = mPolicies[AP_DEFAULT]; + continue; } } diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h index 6dc3bb2130..40e3042b84 100755 --- a/indra/newview/llappcorehttp.h +++ b/indra/newview/llappcorehttp.h @@ -45,12 +45,109 @@ public: enum EAppPolicy { + /// Catchall policy class. Not used yet + /// but will have a generous concurrency + /// limit. Deep queueing possible by having + /// a chatty HTTP user. + /// + /// Destination: anywhere + /// Protocol: http: or https: + /// Transfer size: KB-MB + /// Long poll: no + /// Concurrency: high + /// Request rate: unknown + /// Pipelined: no AP_DEFAULT, + + /// Texture fetching policy class. Used to + /// download textures via capability or SSA + /// baking service. Deep queueing of requests. + /// Do not share. + /// + /// Destination: simhost:12046 & bake-texture:80 + /// Protocol: http: + /// Transfer size: KB-MB + /// Long poll: no + /// Concurrency: high + /// Request rate: high + /// Pipelined: soon AP_TEXTURE, + + /// Legacy mesh fetching policy class. Used to + /// download textures via 'GetMesh' capability. + /// To be deprecated. Do not share. + /// + /// Destination: simhost:12046 + /// Protocol: http: + /// Transfer size: KB-MB + /// Long poll: no + /// Concurrency: dangerously high + /// Request rate: high + /// Pipelined: no AP_MESH1, + + /// New mesh fetching policy class. Used to + /// download textures via 'GetMesh2' capability. + /// Used when fetch request (typically one LOD) + /// is 'small', currently defined as 2MB. + /// Very deeply queued. Do not share. + /// + /// Destination: simhost:12046 + /// Protocol: http: + /// Transfer size: KB-MB + /// Long poll: no + /// Concurrency: high + /// Request rate: high + /// Pipelined: soon AP_MESH2, + + /// Large mesh fetching policy class. Used to + /// download textures via 'GetMesh' or 'GetMesh2' + /// capability. Used when fetch request + /// is not small to avoid head-of-line problem + /// when large requests block a sequence of small, + /// fast requests. Can be shared with similar + /// traffic that can wait for longish stalls + /// (default timeout 600S). + /// + /// Destination: simhost:12046 + /// Protocol: http: + /// Transfer size: MB + /// Long poll: no + /// Concurrency: low + /// Request rate: low + /// Pipelined: soon AP_LARGE_MESH, + + /// Asset upload policy class. Used to store + /// assets (mesh only at the moment) via + /// changeable URL. Responses may take some + /// time (default timeout 240S). + /// + /// Destination: simhost:12043 + /// Protocol: https: + /// Transfer size: KB-MB + /// Long poll: no + /// Concurrency: low + /// Request rate: low + /// Pipelined: no AP_UPLOADS, + + /// Long-poll-type HTTP requests. Not + /// bound by a connection limit. Requests + /// will typically hang around for a long + /// time (~30S). Only shareable with other + /// long-poll requests. + /// + /// Destination: simhost:12043 + /// Protocol: https: + /// Transfer size: KB + /// Long poll: yes + /// Concurrency: unlimited but low in practice + /// Request rate: low + /// Pipelined: no + AP_LONG_POLL, + AP_COUNT // Must be last }; -- cgit v1.2.3 From a232fa4d9f4bf8f29d2783da8c2e01f7c9f80fd9 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 19 Sep 2013 19:20:31 -0400 Subject: SH-4516 Fix a subset of thread races in llmeshrepository Much earlier identified some thread races including two on mskininfoq and mdecompositionq. I actually hit one in testing the other day so I'm fixing them now. Put them under the mMutex lock and use the mutex in such a way that main thread stalls are not added. --- indra/newview/llmeshrepository.cpp | 49 ++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 13 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index c3d149db9a..5174a7af00 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -246,10 +246,10 @@ // mMeshHeader mHeaderMutex rw.repo.mHeaderMutex, ro.main.mHeaderMutex, ro.main.none [0] // mMeshHeaderSize mHeaderMutex rw.repo.mHeaderMutex // mSkinRequests mMutex rw.repo.mMutex, ro.repo.none [5] -// mSkinInfoQ none rw.repo.none, rw.main.mMutex [0] +// mSkinInfoQ mMutex rw.repo.mMutex, rw.main.mMutex [5] (was: [0]) // mDecompositionRequests mMutex rw.repo.mMutex, ro.repo.none [5] // mPhysicsShapeRequests mMutex rw.repo.mMutex, ro.repo.none [5] -// mDecompositionQ none rw.repo.none, rw.main.mMutex [0] +// mDecompositionQ mMutex rw.repo.mMutex, rw.main.mMutex [5] (was: [0]) // mHeaderReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex // mLODReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex // mUnavailableQ mMutex rw.repo.none [0], ro.main.none [5], rw.main.mMutex @@ -1713,7 +1713,10 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat info.mMeshID = mesh_id; // LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL; - mSkinInfoQ.push(info); + { + LLMutexLock lock(mMutex); + mSkinInfoQ.push(info); + } } return true; @@ -1740,7 +1743,10 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3 { LLModel::Decomposition* d = new LLModel::Decomposition(decomp); d->mMeshID = mesh_id; - mDecompositionQ.push(d); + { + LLMutexLock lock(mMutex); + mDecompositionQ.push(d); + } } return true; @@ -1799,7 +1805,10 @@ bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 } } - mDecompositionQ.push(d); + { + LLMutexLock lock(mMutex); + mDecompositionQ.push(d); + } return true; } @@ -2460,16 +2469,30 @@ void LLMeshRepoThread::notifyLoadedMeshes() gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); } - while (!mSkinInfoQ.empty()) + if (! mSkinInfoQ.empty() || ! mDecompositionQ.empty()) { - gMeshRepo.notifySkinInfoReceived(mSkinInfoQ.front()); - mSkinInfoQ.pop(); - } + std::queue skin_info_q; + std::queue decomp_q; - while (!mDecompositionQ.empty()) - { - gMeshRepo.notifyDecompositionReceived(mDecompositionQ.front()); - mDecompositionQ.pop(); + if (mMutex->trylock()) + { + // Make thread-shared data private with swap under lock. + skin_info_q.swap(mSkinInfoQ); + decomp_q.swap(mDecompositionQ); + mMutex->unlock(); + + while (! skin_info_q.empty()) + { + gMeshRepo.notifySkinInfoReceived(skin_info_q.front()); + skin_info_q.pop(); + } + + while (! decomp_q.empty()) + { + gMeshRepo.notifyDecompositionReceived(decomp_q.front()); + decomp_q.pop(); + } + } } if (update_metrics) -- cgit v1.2.3 From cbf7e90954e69f86cbf530676e165f2feb6501bb Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 20 Sep 2013 05:58:32 +0000 Subject: Used a c++11 feature unintentionally. Use a more traditional swap-less object transfer. --- indra/newview/llmeshrepository.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 5174a7af00..23a9640761 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -2471,16 +2471,25 @@ void LLMeshRepoThread::notifyLoadedMeshes() if (! mSkinInfoQ.empty() || ! mDecompositionQ.empty()) { - std::queue skin_info_q; - std::queue decomp_q; - if (mMutex->trylock()) { - // Make thread-shared data private with swap under lock. - skin_info_q.swap(mSkinInfoQ); - decomp_q.swap(mDecompositionQ); + std::queue skin_info_q; + std::queue decomp_q; + + // swap() comes to std::queue in c++11 so copy manually for now + while (! mSkinInfoQ.empty()) + { + skin_info_q.push(mSkinInfoQ.front()); + mSkinInfoQ.pop(); + } + while (! mDecompositionQ.empty()) + { + decomp_q.push(mDecompositionQ.front()); + mDecompositionQ.pop(); + } mMutex->unlock(); + // Process the elements free of the lock while (! skin_info_q.empty()) { gMeshRepo.notifySkinInfoReceived(skin_info_q.front()); -- cgit v1.2.3 From 2462162539053e8cbf26aea68940f300bca5aa87 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 20 Sep 2013 07:13:14 +0000 Subject: Move from std::queue to std::list which has better behavior and swap() as well. Should probably do this for the other queues at some point. --- indra/newview/llmeshrepository.cpp | 26 ++++++++++++-------------- indra/newview/llmeshrepository.h | 8 ++++---- 2 files changed, 16 insertions(+), 18 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 23a9640761..b55ba758e1 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1715,7 +1715,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat // LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL; { LLMutexLock lock(mMutex); - mSkinInfoQ.push(info); + mSkinInfoQ.push_back(info); } } @@ -1745,7 +1745,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3 d->mMeshID = mesh_id; { LLMutexLock lock(mMutex); - mDecompositionQ.push(d); + mDecompositionQ.push_back(d); } } @@ -1807,7 +1807,7 @@ bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 { LLMutexLock lock(mMutex); - mDecompositionQ.push(d); + mDecompositionQ.push_back(d); } return true; } @@ -2473,33 +2473,31 @@ void LLMeshRepoThread::notifyLoadedMeshes() { if (mMutex->trylock()) { - std::queue skin_info_q; - std::queue decomp_q; + std::list skin_info_q; + std::list decomp_q; - // swap() comes to std::queue in c++11 so copy manually for now - while (! mSkinInfoQ.empty()) + if (! mSkinInfoQ.empty()) { - skin_info_q.push(mSkinInfoQ.front()); - mSkinInfoQ.pop(); + skin_info_q.swap(mSkinInfoQ); } - while (! mDecompositionQ.empty()) + if (! mDecompositionQ.empty()) { - decomp_q.push(mDecompositionQ.front()); - mDecompositionQ.pop(); + 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(); + skin_info_q.pop_front(); } while (! decomp_q.empty()) { gMeshRepo.notifyDecompositionReceived(decomp_q.front()); - decomp_q.pop(); + decomp_q.pop_front(); } } } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index c79278da1a..e09f39f7a8 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -291,8 +291,8 @@ public: //set of requested skin info std::set mSkinRequests; - //queue of completed skin info requests - std::queue mSkinInfoQ; + // list of completed skin info requests + std::list mSkinInfoQ; //set of requested decompositions std::set mDecompositionRequests; @@ -300,8 +300,8 @@ public: //set of requested physics shapes std::set mPhysicsShapeRequests; - //queue of completed Decomposition info requests - std::queue mDecompositionQ; + // list of completed Decomposition info requests + std::list mDecompositionQ; //queue of requested headers std::queue mHeaderReqQ; -- cgit v1.2.3 From 200bea5b418a3dc61431d26271932f8c489f0d18 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 24 Sep 2013 14:49:26 -0400 Subject: SH-3690 SH-4505 Cleanup pass through code. Start using DNS cache in legacy LLCurl code. Go to 15 seconds particularly as we're using threaded resolver at this point. Documentation cleanup. Add libcurl status checking and logging for curl_easy_setopt() operations that fail. Shouldn't happen and we'll just continue anyway but there's info in the logs to track these down now. Cleaned up logic around FASTTIMER enable defines used to evaluate pipeline stalls in main thread. Removed long-standing thread race around caps strings and URL construction. Not a significant risk but refactoring the code to get rid of them removed one huge eyesore. It can be made even slicker if desired (see notes). --- indra/newview/llmeshrepository.cpp | 101 ++++++++++++++++++++++++------------- indra/newview/llmeshrepository.h | 19 +++++-- 2 files changed, 82 insertions(+), 38 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index b55ba758e1..42952909d7 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -78,8 +78,6 @@ #include "netdb.h" #endif -#include - // Purpose // @@ -235,8 +233,7 @@ // mUploadWaitList none rw.main.none (upload thread accessing objects) // mInventoryQ mMeshMutex [4] rw.main.mMeshMutex, ro.main.none [5] // mUploadErrorQ mMeshMutex rw.main.mMeshMutex, rw.any.mMeshMutex -// mGetMeshCapability none rw.main.none [0], ro.any.none -// mGetMesh2Capability none rw.main.none [0], ro.any.none +// mGetMeshVersion none rw.main.none // // LLMeshRepoThread: // @@ -255,6 +252,9 @@ // mUnavailableQ mMutex rw.repo.none [0], ro.main.none [5], rw.main.mMutex // mLoadedQ mMutex rw.repo.mMutex, ro.main.none [5], rw.main.mMutex // mPendingLOD mMutex rw.repo.mMutex, rw.any.mMutex +// mGetMeshCapability mMutex rw.main.mMutex, ro.repo.mMutex (was: [0]) +// mGetMesh2Capability mMutex rw.main.mMutex, ro.repo.mMutex (was: [0]) +// mGetMeshVersion mMutex rw.main.mMutex, ro.repo.mMutex // mHttp* none rw.repo.none // // QA/Development Testing @@ -304,7 +304,10 @@ // With this instrumentation enabled, a stall will appear // under the 'Mesh Fetch' timer which will be either top-level // or under 'Render' time. + +#ifndef LL_MESH_FASTTIMER_ENABLE #define LL_MESH_FASTTIMER_ENABLE 1 +#endif #if LL_MESH_FASTTIMER_ENABLE static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch"); @@ -381,7 +384,6 @@ static S32 dump_num = 0; std::string make_dump_name(std::string prefix, S32 num) { return prefix + boost::lexical_cast(num) + std::string(".xml"); - } void dump_llsd_to_file(const LLSD& content, std::string filename); LLSD llsd_from_file(std::string filename); @@ -740,7 +742,8 @@ LLMeshRepoThread::LLMeshRepoThread() mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpLegacyPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), - mHttpPriority(0) + mHttpPriority(0), + mGetMeshVersion(2) { mMutex = new LLMutex(NULL); mHeaderMutex = new LLMutex(NULL); @@ -1047,30 +1050,50 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } } +// Mutex: must be holding mMutex when called +void LLMeshRepoThread::setGetMeshCaps(const std::string & get_mesh1, + const std::string & get_mesh2, + int pref_version) +{ + mGetMeshCapability = get_mesh1; + mGetMesh2Capability = get_mesh2; + mGetMeshVersion = pref_version; +} + + // Constructs a Cap URL for the mesh. Prefers a GetMesh2 cap // over a GetMesh cap. // -//static -std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) +// Mutex: acquires mMutex +void LLMeshRepoThread::constructUrl(LLUUID mesh_id, std::string * url, int * version) { - std::string http_url; + std::string res_url; + int res_version(2); if (gAgent.getRegion()) { - if (! gMeshRepo.mGetMesh2Capability.empty() && gMeshRepo.mGetMeshVersion > 1) + LLMutexLock lock(mMutex); + + // Get a consistent pair of (cap string, version). The + // locking could be eliminated here without loss of safety + // by using a set of staging values in setGetMeshCaps(). + + if (! mGetMesh2Capability.empty() && mGetMeshVersion > 1) { - http_url = gMeshRepo.mGetMesh2Capability; + res_url = mGetMesh2Capability; + res_version = 2; } else { - http_url = gMeshRepo.mGetMeshCapability; + res_url = mGetMeshCapability; + res_version = 1; } } - if (!http_url.empty()) + if (! res_url.empty()) { - http_url += "/?mesh_id="; - http_url += mesh_id.asString().c_str(); + res_url += "/?mesh_id="; + res_url += mesh_id.asString().c_str(); } else { @@ -1078,7 +1101,8 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) << mesh_id << ".mesh" << LL_ENDL; } - return http_url; + *url = res_url; + *version = res_version; } // Issue an HTTP GET request with byte range using the right @@ -1200,8 +1224,10 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - int cap_version(gMeshRepo.mGetMeshVersion); - std::string http_url = constructUrl(mesh_id); + int cap_version(2); + std::string http_url; + constructUrl(mesh_id, &http_url, &cap_version); + if (!http_url.empty()) { LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size); @@ -1293,8 +1319,10 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - int cap_version(gMeshRepo.mGetMeshVersion); - std::string http_url = constructUrl(mesh_id); + int cap_version(2); + std::string http_url; + constructUrl(mesh_id, &http_url, &cap_version); + if (!http_url.empty()) { LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size); @@ -1384,8 +1412,10 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - int cap_version(gMeshRepo.mGetMeshVersion); - std::string http_url = constructUrl(mesh_id); + int cap_version(2); + std::string http_url; + constructUrl(mesh_id, &http_url, &cap_version); + if (!http_url.empty()) { LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size); @@ -1477,8 +1507,10 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) //either cache entry doesn't exist or is corrupt, request header from simulator bool retval = true; - int cap_version(gMeshRepo.mGetMeshVersion); - std::string http_url = constructUrl(mesh_params.getSculptID()); + int cap_version(2); + std::string http_url; + constructUrl(mesh_params.getSculptID(), &http_url, &cap_version); + if (!http_url.empty()) { //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits @@ -1563,8 +1595,10 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } //reading from VFS failed for whatever reason, fetch from sim - int cap_version(gMeshRepo.mGetMeshVersion); - std::string http_url = constructUrl(mesh_id); + int cap_version(2); + std::string http_url; + constructUrl(mesh_id, &http_url, &cap_version); + if (!http_url.empty()) { LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size); @@ -1812,7 +1846,6 @@ bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 return true; } - LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, const std::string & upload_url, bool do_upload, LLHandle fee_observer, @@ -3249,15 +3282,15 @@ void LLMeshRepository::notifyLoadedMeshes() if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) { - const bool use_v1(gSavedSettings.getBOOL("MeshUseGetMesh1")); - region_name = gAgent.getRegion()->getName(); - mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh"); - mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2"); - mGetMeshVersion = (mGetMesh2Capability.empty() || use_v1) ? 1 : 2; + const bool use_v1(gSavedSettings.getBOOL("MeshUseGetMesh1")); + const std::string mesh1(gAgent.getRegion()->getCapability("GetMesh")); + const std::string mesh2(gAgent.getRegion()->getCapability("GetMesh2")); + mGetMeshVersion = (mesh2.empty() || use_v1) ? 1 : 2; + mThread->setGetMeshCaps(mesh1, mesh2, mGetMeshVersion); LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name - << "', GetMesh2: " << mGetMesh2Capability - << ", GetMesh: " << mGetMeshCapability + << "', GetMesh2: " << mesh2 + << ", GetMesh: " << mesh1 << ", using version: " << mGetMeshVersion << LL_ENDL; } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index e09f39f7a8..9d8b102110 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -333,7 +333,9 @@ public: typedef std::set http_request_set; http_request_set mHttpRequestSet; // Outstanding HTTP requests - static std::string constructUrl(LLUUID mesh_id); + std::string mGetMeshCapability; + std::string mGetMesh2Capability; + int mGetMeshVersion; LLMeshRepoThread(); ~LLMeshRepoThread(); @@ -376,6 +378,17 @@ public: static void incActiveHeaderRequests(); static void decActiveHeaderRequests(); + // Set the caps strings and preferred version for constructing + // mesh fetch URLs. + // + // Mutex: must be holding mMutex when called + void setGetMeshCaps(const std::string & get_mesh1, + const std::string & get_mesh2, + int pref_version); + + // Mutex: acquires mMutex + void constructUrl(LLUUID mesh_id, std::string * url, int * version); + private: // Issue a GET request to a URL with 'Range' header using // the correct policy class and other attributes. If an invalid @@ -613,9 +626,7 @@ public: void uploadError(LLSD& args); void updateInventory(inventory_data data); - std::string mGetMeshCapability; - std::string mGetMesh2Capability; - int mGetMeshVersion; + int mGetMeshVersion; // Shadows value in LLMeshRepoThread }; extern LLMeshRepository gMeshRepo; -- cgit v1.2.3 From 56e2f11417183d8dcc3d681f79fc63446b236abb Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 27 Sep 2013 17:22:31 -0400 Subject: Up the transfer timeout of small meshes to 120S. This matches the change made for MAINT-2347. Large transfers are still 10 minutes. Add/update to-do list and add some more info to the FAQ in the Readme. --- indra/newview/llmeshrepository.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 42952909d7..044cf27b07 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -351,7 +351,7 @@ const S32 REQUEST2_HIGH_WATER_MAX = 80; const S32 REQUEST2_LOW_WATER_MIN = 16; const S32 REQUEST2_LOW_WATER_MAX = 40; const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes to narrow/slow queue -const long SMALL_MESH_XFER_TIMEOUT = 60L; // Seconds to complete xfer, small mesh downloads +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 // Maximum mesh version to support. Three least significant digits are reserved for the minor version, -- cgit v1.2.3 From ea1f6a6343fe83f1352a8a839265c471640acdce Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 6 Dec 2013 16:02:53 -0500 Subject: SH-4645 Viewer hangs on exit after cancelling a mesh upload. Problem involved a 3-way livelock between the main, upload and decomposition threads. Viewer is shutting down but an upload is in the 'generate hulls' state. Main thread asks upload request to discard and spins waiting for it to finish. Upload thread is in generateHulls spinning waiting for the decomposition thread to process a mesh request. Decomposition thread is sleeping waiting for main thread to deliver work that upload thread has asked the decomposition thread to do. --- indra/newview/llmeshrepository.cpp | 39 ++++++++++++++++++++++++++++++-------- indra/newview/llmeshrepository.h | 4 ++-- 2 files changed, 33 insertions(+), 10 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index ebfb22a360..4296abb2cc 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -257,6 +257,11 @@ // mGetMeshVersion mMutex rw.main.mMutex, ro.repo.mMutex // mHttp* none rw.repo.none // +// LLMeshUploadThread: +// +// mDiscarded mMutex rw.main.mMutex, ro.uploadN.none [1] +// ... more ... +// // QA/Development Testing // // Debug variable 'MeshUploadFakeErrors' takes a mask of bits that will @@ -1852,7 +1857,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLHandle upload_observer) : LLThread("mesh upload"), LLCore::HttpHandler(), - mDiscarded(FALSE), + mDiscarded(false), mDoUpload(do_upload), mWholeModelUploadURL(upload_url), mFeeObserverHandle(fee_observer), @@ -1940,10 +1945,10 @@ void LLMeshUploadThread::preStart() void LLMeshUploadThread::discard() { LLMutexLock lock(mMutex); - mDiscarded = TRUE; + mDiscarded = true; } -BOOL LLMeshUploadThread::isDiscarded() const +bool LLMeshUploadThread::isDiscarded() const { LLMutexLock lock(mMutex); return mDiscarded; @@ -2199,7 +2204,13 @@ void LLMeshUploadThread::generateHulls() if (has_valid_requests) { - while (!mPhysicsComplete) + // *NOTE: Interesting livelock condition on shutdown. If there + // is an upload request in generateHulls() when shutdown starts, + // the main thread isn't available to manage communication between + // the decomposition thread and the upload thread and this loop + // wouldn't complete in turn stalling the main thread. The check + // on isDiscarded() prevents that. + while (! mPhysicsComplete && ! isDiscarded()) { apr_sleep(100); } @@ -2253,13 +2264,21 @@ void LLMeshUploadThread::doWholeModelUpload() LL_DEBUGS(LOG_MESH) << "POST request issued." << LL_ENDL; mHttpRequest->update(0); - while (! LLApp::isQuitting() && ! mFinished) + while (! LLApp::isQuitting() && ! finished() && ! isDiscarded()) { ms_sleep(sleep_time); sleep_time = llmin(250U, sleep_time + sleep_time); mHttpRequest->update(0); } - LL_DEBUGS(LOG_MESH) << "Mesh upload operation completed." << LL_ENDL; + + if (isDiscarded()) + { + LL_DEBUGS(LOG_MESH) << "Mesh upload operation discarded." << LL_ENDL; + } + else + { + LL_DEBUGS(LOG_MESH) << "Mesh upload operation completed." << LL_ENDL; + } } } } @@ -2299,12 +2318,16 @@ void LLMeshUploadThread::requestWholeModelFee() U32 sleep_time(10); mHttpRequest->update(0); - while (! LLApp::isQuitting() && ! mFinished) + while (! LLApp::isQuitting() && ! finished() && ! isDiscarded()) { ms_sleep(sleep_time); sleep_time = llmin(250U, sleep_time + sleep_time); mHttpRequest->update(0); } + if (isDiscarded()) + { + LL_DEBUGS(LOG_MESH) << "Mesh fee query operation discarded." << LL_ENDL; + } } } @@ -3020,7 +3043,7 @@ void LLMeshRepository::shutdown() for (U32 i = 0; i < mUploads.size(); ++i) { - LL_INFOS(LOG_MESH) << "Waiting for pending mesh upload " << i << "/" << mUploads.size() << LL_ENDL; + LL_INFOS(LOG_MESH) << "Waiting for pending mesh upload " << (i + 1) << "/" << mUploads.size() << LL_ENDL; while (!mUploads[i]->isStopped()) { apr_sleep(10); diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 9d8b102110..39280bea3a 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -447,7 +447,7 @@ public: bool mUploadTextures; bool mUploadSkin; bool mUploadJoints; - BOOL mDiscarded; + volatile bool mDiscarded; LLHost mHost; std::string mWholeModelFeeCapability; @@ -463,7 +463,7 @@ public: virtual void run(); void preStart(); void discard() ; - BOOL isDiscarded() const; + bool isDiscarded() const; void generateHulls(); -- cgit v1.2.3 From b2fe32e5725e13930437ae8939d7213de69b35ca Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 9 Jan 2014 15:18:54 -0500 Subject: SH-4667 HTTP Viewer reports network error instead of a misnamed joint on mesh upload Tried to add consistent mesh upload retries in the HTTP work but a combination of bad status choices in the upload service and the one-shot nature of the upload capabilities means that status information can be lost. For now, retain the wonderful manual retry logic. At some future point, we might fix the services or add application-level retry. --- indra/newview/llmeshrepository.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 4296abb2cc..5afd2cb329 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -5,7 +5,7 @@ * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010-2013, Linden Research, Inc. + * Copyright (C) 2010-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 @@ -359,6 +359,16 @@ const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes 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 +// Would normally like to retry on uploads as some +// retryable failures would be recoverable. Unfortunately, +// the mesh service is using 500 (retryable) rather than +// 400/bad request (permanent) for a bad payload and +// retrying that just leads to revocation of the one-shot +// cap which then produces a 404 on retry destroying some +// (occasionally) useful error information. We'll leave +// upload retries to the user as in the past. SH-4667. +const long UPLOAD_RETRY_LIMIT = 0L; + // Maximum mesh version to support. Three least significant digits are reserved for the minor version, // with major version changes indicating a format change that is not backwards compatible and should not // be parsed by viewers that don't specifically support that version. For example, if the integer "1" is @@ -1883,6 +1893,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, mHttpOptions = new LLCore::HttpOptions; mHttpOptions->setTransferTimeout(mMeshUploadTimeOut); mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); + mHttpOptions->setRetries(UPLOAD_RETRY_LIMIT); mHttpHeaders = new LLCore::HttpHeaders; mHttpHeaders->append("Content-Type", "application/llsd+xml"); mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS); -- cgit v1.2.3 From de8fea13627cc5978b8a6135802a52864a11c39a Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Mon, 24 Feb 2014 14:50:35 -0500 Subject: increment viewer version to 3.7.3 --- indra/newview/VIEWER_VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 0b2eb36f50..c1e43e6d45 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -3.7.2 +3.7.3 -- cgit v1.2.3