diff options
Diffstat (limited to 'indra/newview/llappcorehttp.cpp')
-rwxr-xr-x[-rw-r--r--] | indra/newview/llappcorehttp.cpp | 352 |
1 files changed, 312 insertions, 40 deletions
diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 0d7d41304d..f5f224b83e 100644..100755 --- 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-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 @@ -28,17 +28,90 @@ #include "llappcorehttp.h" +#include "llappviewer.h" #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); +const long LLAppCoreHttp::PIPELINING_DEPTH(5L); + +// Default and dynamic values for classes +static const struct +{ + U32 mDefault; + U32 mMin; + U32 mMax; + U32 mRate; + bool mPipelined; + std::string mKey; + const char * mUsage; +} init_data[LLAppCoreHttp::AP_COUNT] = +{ + { // AP_DEFAULT + 8, 8, 8, 0, false, + "", + "other" + }, + { // AP_TEXTURE + 8, 1, 12, 0, true, + "TextureFetchConcurrency", + "texture fetch" + }, + { // AP_MESH1 + 32, 1, 128, 0, false, + "MeshMaxConcurrentRequests", + "mesh fetch" + }, + { // AP_MESH2 + 8, 1, 32, 0, true, + "Mesh2MaxConcurrentRequests", + "mesh2 fetch" + }, + { // AP_LARGE_MESH + 2, 1, 8, 0, false, + "", + "large mesh fetch" + }, + { // AP_UPLOADS + 2, 1, 8, 0, false, + "", + "asset upload" + }, + { // AP_LONG_POLL + 32, 32, 32, 0, false, + "", + "long poll" + }, + { // AP_INVENTORY + 4, 1, 4, 0, false, + "", + "inventory" + } +}; + +static void setting_changed(); + + +LLAppCoreHttp::HttpClass::HttpClass() + : mPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mConnLimit(0U), + mPipelined(false) +{} + LLAppCoreHttp::LLAppCoreHttp() : mRequest(NULL), mStopHandle(LLCORE_HTTP_HANDLE_INVALID), mStopRequested(0.0), mStopped(false), - mPolicyDefault(-1) + mPipelined(true) {} @@ -54,30 +127,28 @@ void LLAppCoreHttp::init() 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; } // 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() + LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " << status.toString() << 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. - status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); + // Establish HTTP Proxy, if desired. + status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_LLPROXY, + LLCore::HttpRequest::GLOBAL_POLICY_ID, + 1, NULL); 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): @@ -90,47 +161,93 @@ 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 - mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID; - static const std::string texture_concur("TextureFetchConcurrency"); - if (gSavedSettings.controlExists(texture_concur)) + mHttpClasses[AP_DEFAULT].mPolicy = LLCore::HttpRequest::DEFAULT_POLICY_ID; + + // Setup additional policies based on table and some special rules + llassert(LL_ARRAY_SIZE(init_data) == AP_COUNT); + for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) { - U32 concur(llmin(gSavedSettings.getU32(texture_concur), U32(12))); + const EAppPolicy app_policy(static_cast<EAppPolicy>(i)); - if (concur > 0) + if (AP_DEFAULT == app_policy) { - LLCore::HttpStatus status; - status = LLCore::HttpRequest::setPolicyClassOption(mPolicyDefault, - LLCore::HttpRequest::CP_CONNECTION_LIMIT, - concur); - if (! status) - { - LL_WARNS("Init") << "Unable to set texture fetch concurrency. Reason: " - << status.toString() - << LL_ENDL; - } - else - { - LL_INFOS("Init") << "Application settings overriding default texture fetch concurrency. New value: " - << concur - << LL_ENDL; - } + // Pre-created + continue; + } + + mHttpClasses[app_policy].mPolicy = LLCore::HttpRequest::createPolicyClass(); + if (! mHttpClasses[app_policy].mPolicy) + { + // 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; + mHttpClasses[app_policy].mPolicy = mHttpClasses[AP_DEFAULT].mPolicy; + continue; } } + + // Need a request object to handle dynamic options before setting them + mRequest = new LLCore::HttpRequest; + + // Apply initial settings + refreshSettings(true); // 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; } - mRequest = new LLCore::HttpRequest; + // Signal for global pipelining preference from settings + static const std::string http_pipelining("HttpPipelining"); + if (gSavedSettings.controlExists(http_pipelining)) + { + LLPointer<LLControlVariable> cntrl_ptr = gSavedSettings.getControl(http_pipelining); + if (cntrl_ptr.isNull()) + { + LL_WARNS("Init") << "Unable to set signal on global setting '" << http_pipelining + << "'" << LL_ENDL; + } + else + { + mPipelinedSignal = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed)); + } + } + + // Register signals for settings and state changes + for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) + { + const EAppPolicy app_policy(static_cast<EAppPolicy>(i)); + + if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey)) + { + LLPointer<LLControlVariable> 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 + { + mHttpClasses[app_policy].mSettingsSignal = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed)); + } + } + } +} + + +void setting_changed() +{ + LLAppViewer::instance()->getAppCoreHttp().refreshSettings(false); } @@ -173,6 +290,12 @@ void LLAppCoreHttp::cleanup() } } + for (int i(0); i < LL_ARRAY_SIZE(mHttpClasses); ++i) + { + mHttpClasses[i].mSettingsSignal.disconnect(); + } + mPipelinedSignal.disconnect(); + delete mRequest; mRequest = NULL; @@ -186,6 +309,155 @@ void LLAppCoreHttp::cleanup() } +void LLAppCoreHttp::refreshSettings(bool initial) +{ + LLCore::HttpStatus status; + + // Global pipelining setting + bool pipeline_changed(false); + static const std::string http_pipelining("HttpPipelining"); + if (gSavedSettings.controlExists(http_pipelining)) + { + // Default to true (in ctor) if absent. + bool pipelined(gSavedSettings.getBOOL(http_pipelining)); + if (pipelined != mPipelined) + { + mPipelined = pipelined; + pipeline_changed = true; + } + } + + for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) + { + const EAppPolicy app_policy(static_cast<EAppPolicy>(i)); + + if (initial) + { + // Init-time only settings, can use the static setters here + + if (init_data[i].mRate) + { + // Set any desired throttle + status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_THROTTLE_RATE, + mHttpClasses[app_policy].mPolicy, + init_data[i].mRate, + NULL); + if (! status) + { + LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage + << " throttle rate. Reason: " << status.toString() + << LL_ENDL; + } + } + + } + + // Init- or run-time settings. Must use the queued request API. + + // Pipelining changes + if (initial || pipeline_changed) + { + const bool to_pipeline(mPipelined && init_data[i].mPipelined); + if (to_pipeline != mHttpClasses[app_policy].mPipelined) + { + // Pipeline election changing, set dynamic option via request + + LLCore::HttpHandle handle; + const long new_depth(to_pipeline ? PIPELINING_DEPTH : 0); + + handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_PIPELINING_DEPTH, + mHttpClasses[app_policy].mPolicy, + new_depth, + NULL); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + status = mRequest->getStatus(); + LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage + << " pipelining. Reason: " << status.toString() + << LL_ENDL; + } + else + { + LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage + << " pipelining. New value: " << new_depth + << LL_ENDL; + mHttpClasses[app_policy].mPipelined = to_pipeline; + } + } + } + + // 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 = llclamp(new_setting, init_data[i].mMin, init_data[i].mMax); + } + } + + if (initial || setting != mHttpClasses[app_policy].mConnLimit || pipeline_changed) + { + // Set it and report. Strategies depend on pipelining: + // + // No Pipelining. Llcorehttp manages connections itself based + // on the PO_CONNECTION_LIMIT setting. Set both limits to the + // same value for logical consistency. In the future, may + // hand over connection management to libcurl after the + // connection cache has been better vetted. + // + // Pipelining. Libcurl is allowed to manage connections to a + // great degree. Steady state will connection limit based on + // the per-host setting. Transitions (region crossings, new + // avatars, etc.) can request additional outbound connections + // to other servers via 2X total connection limit. + // + LLCore::HttpHandle handle; + handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT, + mHttpClasses[app_policy].mPolicy, + (mHttpClasses[app_policy].mPipelined ? 2 * setting : setting), + NULL); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + status = mRequest->getStatus(); + LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage + << " concurrency. Reason: " << status.toString() + << LL_ENDL; + } + else + { + handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_PER_HOST_CONNECTION_LIMIT, + mHttpClasses[app_policy].mPolicy, + setting, + NULL); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + status = mRequest->getStatus(); + LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage + << " per-host concurrency. Reason: " << status.toString() + << LL_ENDL; + } + else + { + LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage + << " concurrency. New value: " << setting + << LL_ENDL; + mHttpClasses[app_policy].mConnLimit = setting; + if (initial && setting != init_data[i].mDefault) + { + LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage + << " concurrency. New value: " << setting + << LL_ENDL; + } + } + } + } + } +} + + void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *) { mStopped = true; |