From 432d6b5697818eb41751e858e45a4ce670f03e5c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 22 Jun 2013 20:08:15 +0200 Subject: [PATCH 1/8] More comment fixes to refer to services instead of hostnames. --- indra/llmessage/aicurlperservice.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/indra/llmessage/aicurlperservice.h b/indra/llmessage/aicurlperservice.h index 7c53a06ab..13673f19c 100644 --- a/indra/llmessage/aicurlperservice.h +++ b/indra/llmessage/aicurlperservice.h @@ -103,7 +103,7 @@ class AIPerService { typedef AIAccess instance_map_wat; private: - static threadsafe_instance_map_type sInstanceMap; // Map of AIPerService instances with the hostname as key. + static threadsafe_instance_map_type sInstanceMap; // Map of AIPerService instances with the canonical hostname:port as key. friend class AIThreadSafeSimpleDC; // threadsafe_PerService AIPerService(void); @@ -115,7 +115,7 @@ class AIPerService { // Utility function; extract canonical (lowercase) hostname and port from url. static std::string extract_canonical_servicename(std::string const& url); - // Return (possibly create) a unique instance for the given hostname. + // Return (possibly create) a unique instance for the given hostname:port combination. static AIPerServicePtr instance(std::string const& servicename); // Release instance (object will be deleted if this was the last instance). @@ -158,7 +158,7 @@ class AIPerService { AIAverage mHTTPBandwidth; // Keeps track on number of bytes received for this service in the past second. int mConcurrectConnections; // The maximum number of allowed concurrent connections to this service. - int mTotalAdded; // Number of active easy handles with this host. + int mTotalAdded; // Number of active easy handles with this service. int mApprovedFirst; // First capability type to try. int mUnapprovedFirst; // First capability type to try after all approved types were tried. @@ -213,10 +213,10 @@ class AIPerService { public: void added_to_command_queue(AICapabilityType capability_type) { ++mCapabilityType[capability_type].mQueuedCommands; } void removed_from_command_queue(AICapabilityType capability_type) { --mCapabilityType[capability_type].mQueuedCommands; llassert(mCapabilityType[capability_type].mQueuedCommands >= 0); } - void added_to_multi_handle(AICapabilityType capability_type); // Called when an easy handle for this host has been added to the multi handle. - void removed_from_multi_handle(AICapabilityType capability_type, bool downloaded_something); // Called when an easy handle for this host is removed again from the multi handle. + void added_to_multi_handle(AICapabilityType capability_type); // Called when an easy handle for this service has been added to the multi handle. + void removed_from_multi_handle(AICapabilityType capability_type, bool downloaded_something); // Called when an easy handle for this service is removed again from the multi handle. void download_started(AICapabilityType capability_type) { ++mCapabilityType[capability_type].mDownloading; } - bool throttled(void) const; // Returns true if the maximum number of allowed requests for this host have been added to the multi handle. + bool throttled(void) const; // Returns true if the maximum number of allowed requests for this service have been added to the multi handle. void queue(AICurlEasyRequest const& easy_request, AICapabilityType capability_type); // Add easy_request to the queue. bool cancel(AICurlEasyRequest const& easy_request, AICapabilityType capability_type); // Remove easy_request from the queue (if it's there). @@ -256,7 +256,7 @@ class AIPerService { // the AIPerService object locked for the whole duration of the call. // The functions only lock it when access is required. - // Returns approvement if curl can handle another request for this host. + // Returns approvement if curl can handle another request for this service. // Should return NULL if the maximum allowed HTTP bandwidth is reached, or when // the latency between request and actual delivery becomes too large. static Approvement* approveHTTPRequestFor(AIPerServicePtr const& per_service, AICapabilityType capability_type); From ee59617c5ebbff10846789d75c113b7a3d926f7c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 22 Jun 2013 22:20:37 +0200 Subject: [PATCH 2/8] Always add new http requests to the back the queue. --- indra/llmessage/aicurlperservice.cpp | 13 ++++++++++--- indra/llmessage/aicurlperservice.h | 4 ++-- indra/llmessage/aicurlthread.cpp | 13 +++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/indra/llmessage/aicurlperservice.cpp b/indra/llmessage/aicurlperservice.cpp index 6f6047bae..4c503e517 100644 --- a/indra/llmessage/aicurlperservice.cpp +++ b/indra/llmessage/aicurlperservice.cpp @@ -301,10 +301,17 @@ void AIPerService::removed_from_multi_handle(AICapabilityType capability_type, b --mTotalAdded; } -void AIPerService::queue(AICurlEasyRequest const& easy_request, AICapabilityType capability_type) +// Returns true if the request was queued. +bool AIPerService::queue(AICurlEasyRequest const& easy_request, AICapabilityType capability_type, bool force_queuing) { - mCapabilityType[capability_type].mQueuedRequests.push_back(easy_request.get_ptr()); - TotalQueued_wat(sTotalQueued)->count++; + CapabilityType::queued_request_type& queued_requests(mCapabilityType[capability_type].mQueuedRequests); + bool needs_queuing = force_queuing || !queued_requests.empty(); + if (needs_queuing) + { + queued_requests.push_back(easy_request.get_ptr()); + TotalQueued_wat(sTotalQueued)->count++; + } + return needs_queuing; } bool AIPerService::cancel(AICurlEasyRequest const& easy_request, AICapabilityType capability_type) diff --git a/indra/llmessage/aicurlperservice.h b/indra/llmessage/aicurlperservice.h index 13673f19c..bd399b6e9 100644 --- a/indra/llmessage/aicurlperservice.h +++ b/indra/llmessage/aicurlperservice.h @@ -218,8 +218,8 @@ class AIPerService { void download_started(AICapabilityType capability_type) { ++mCapabilityType[capability_type].mDownloading; } bool throttled(void) const; // Returns true if the maximum number of allowed requests for this service have been added to the multi handle. - void queue(AICurlEasyRequest const& easy_request, AICapabilityType capability_type); // Add easy_request to the queue. - bool cancel(AICurlEasyRequest const& easy_request, AICapabilityType capability_type); // Remove easy_request from the queue (if it's there). + bool queue(AICurlEasyRequest const& easy_request, AICapabilityType capability_type, bool force_queuing = true); // Add easy_request to the queue if queue is empty or force_queuing. + bool cancel(AICurlEasyRequest const& easy_request, AICapabilityType capability_type); // Remove easy_request from the queue (if it's there). void add_queued_to(AICurlPrivate::curlthread::MultiHandle* mh, bool recursive = false); // Add queued easy handle (if any) to the multi handle. The request is removed from the queue, diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 6ede742b8..e16ae7e3a 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -1721,6 +1721,19 @@ bool MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request, bool f AICurlEasyRequest_wat curl_easy_request_w(*easy_request); capability_type = curl_easy_request_w->capability_type(); per_service = curl_easy_request_w->getPerServicePtr(); + if (!from_queue) + { + // Add the request to the back of a non-empty queue. + if (PerService_wat(*per_service)->queue(easy_request, capability_type, false)) + { + // The queue was not empty, therefore the request was queued. +#ifdef SHOW_ASSERT + // Not active yet, but it's no longer an error if next we try to remove the request. + curl_easy_request_w->mRemovedPerCommand = false; +#endif + return true; + } + } bool too_much_bandwidth = !curl_easy_request_w->approved() && AIPerService::checkBandwidthUsage(per_service, get_clock_count() * HTTPTimeout::sClockWidth_40ms); PerService_wat per_service_w(*per_service); if (!too_much_bandwidth && sTotalAdded < curl_max_total_concurrent_connections && !per_service_w->throttled()) From 87176418a2acaaecab8d0e8b48a721fb7ca36dec Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 23 Jun 2013 18:05:23 +0200 Subject: [PATCH 3/8] Change CurlConcurrentConnectionsPerService to U16 and clamp it between 1 and 32. Also fixes a bug where going from 1 to 2 would not update AIPerService::mConcurrectConnections correctly. --- indra/llmessage/aicurlperservice.cpp | 7 ++++--- indra/llmessage/aicurlperservice.h | 2 +- indra/llmessage/aicurlthread.cpp | 19 ++++++++++++++----- .../app_settings/settings_crash_behavior.xml | 2 +- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/indra/llmessage/aicurlperservice.cpp b/indra/llmessage/aicurlperservice.cpp index 4c503e517..78a4761e0 100644 --- a/indra/llmessage/aicurlperservice.cpp +++ b/indra/llmessage/aicurlperservice.cpp @@ -49,7 +49,7 @@ AIThreadSafeSimpleDC AIPerService::sTotalQueued; namespace AICurlPrivate { // Cached value of CurlConcurrentConnectionsPerService. -U32 CurlConcurrentConnectionsPerService; +U16 CurlConcurrentConnectionsPerService; // Friend functions of RefCountedThreadSafePerService @@ -473,8 +473,9 @@ void AIPerService::adjust_concurrent_connections(int increment) for (AIPerService::iterator iter = instance_map_w->begin(); iter != instance_map_w->end(); ++iter) { PerService_wat per_service_w(*iter->second); - U32 old_concurrent_connections = per_service_w->mConcurrectConnections; - per_service_w->mConcurrectConnections = llclamp(old_concurrent_connections + increment, (U32)1, CurlConcurrentConnectionsPerService); + U16 old_concurrent_connections = per_service_w->mConcurrectConnections; + int new_concurrent_connections = llclamp(old_concurrent_connections + increment, 1, (int)CurlConcurrentConnectionsPerService); + per_service_w->mConcurrectConnections = (U16)new_concurrent_connections; increment = per_service_w->mConcurrectConnections - old_concurrent_connections; for (int i = 0; i < number_of_capability_types; ++i) { diff --git a/indra/llmessage/aicurlperservice.h b/indra/llmessage/aicurlperservice.h index bd399b6e9..bdbc1544c 100644 --- a/indra/llmessage/aicurlperservice.h +++ b/indra/llmessage/aicurlperservice.h @@ -283,7 +283,7 @@ class RefCountedThreadSafePerService : public threadsafe_PerService { friend void intrusive_ptr_release(RefCountedThreadSafePerService* p); }; -extern U32 CurlConcurrentConnectionsPerService; +extern U16 CurlConcurrentConnectionsPerService; } // namespace AICurlPrivate diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index e16ae7e3a..d9b5b19fa 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -2561,7 +2561,7 @@ void startCurlThread(LLControlGroup* control_group) // Cache Debug Settings. sConfigGroup = control_group; curl_max_total_concurrent_connections = sConfigGroup->getU32("CurlMaxTotalConcurrentConnections"); - CurlConcurrentConnectionsPerService = sConfigGroup->getU32("CurlConcurrentConnectionsPerService"); + CurlConcurrentConnectionsPerService = (U16)sConfigGroup->getU32("CurlConcurrentConnectionsPerService"); gNoVerifySSLCert = sConfigGroup->getBOOL("NoVerifySSLCert"); AIPerService::setMaxPipelinedRequests(curl_max_total_concurrent_connections); AIPerService::setHTTPThrottleBandwidth(sConfigGroup->getF32("HTTPThrottleBandwidth")); @@ -2586,10 +2586,19 @@ bool handleCurlConcurrentConnectionsPerService(LLSD const& newvalue) { using namespace AICurlPrivate; - U32 new_concurrent_connections = newvalue.asInteger(); - AIPerService::adjust_concurrent_connections(new_concurrent_connections - CurlConcurrentConnectionsPerService); - CurlConcurrentConnectionsPerService = new_concurrent_connections; - llinfos << "CurlConcurrentConnectionsPerService set to " << CurlConcurrentConnectionsPerService << llendl; + U16 new_concurrent_connections = (U16)newvalue.asInteger(); + U16 const maxCurlConcurrentConnectionsPerService = 32; + if (new_concurrent_connections < 1 || new_concurrent_connections > maxCurlConcurrentConnectionsPerService) + { + sConfigGroup->setU32("CurlConcurrentConnectionsPerService", static_cast((new_concurrent_connections < 1) ? 1 : maxCurlConcurrentConnectionsPerService)); + } + else + { + int increment = new_concurrent_connections - CurlConcurrentConnectionsPerService; + CurlConcurrentConnectionsPerService = new_concurrent_connections; + AIPerService::adjust_concurrent_connections(increment); + llinfos << "CurlConcurrentConnectionsPerService set to " << CurlConcurrentConnectionsPerService << llendl; + } return true; } diff --git a/indra/newview/app_settings/settings_crash_behavior.xml b/indra/newview/app_settings/settings_crash_behavior.xml index 433583bee..0665d86f2 100644 --- a/indra/newview/app_settings/settings_crash_behavior.xml +++ b/indra/newview/app_settings/settings_crash_behavior.xml @@ -31,7 +31,7 @@ Type U32 Value - 16 + 8 NoVerifySSLCert From af9018f35f167de2f20686fe0ae9f7d0ec45245e Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 23 Jun 2013 18:09:09 +0200 Subject: [PATCH 4/8] Add AIPerService::CapabilityType::mConcurrectConnections Prepares for throttling number of connections per capability type. The value is current left at AIPerService::mConcurrectConnections, so not having an effect yet. --- indra/llmessage/aicurlperservice.cpp | 13 +++++++++---- indra/llmessage/aicurlperservice.h | 5 +++-- indra/llmessage/aicurlthread.cpp | 2 +- indra/newview/aihttpview.cpp | 6 +++--- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/indra/llmessage/aicurlperservice.cpp b/indra/llmessage/aicurlperservice.cpp index 78a4761e0..b3d157cf9 100644 --- a/indra/llmessage/aicurlperservice.cpp +++ b/indra/llmessage/aicurlperservice.cpp @@ -85,7 +85,8 @@ AIPerService::CapabilityType::CapabilityType(void) : mAdded(0), mFlags(0), mDownloading(0), - mMaxPipelinedRequests(CurlConcurrentConnectionsPerService) + mMaxPipelinedRequests(CurlConcurrentConnectionsPerService), + mConcurrectConnections(CurlConcurrentConnectionsPerService) { } @@ -278,9 +279,10 @@ void AIPerService::release(AIPerServicePtr& instance) instance.reset(); } -bool AIPerService::throttled() const +bool AIPerService::throttled(AICapabilityType capability_type) const { - return mTotalAdded >= mConcurrectConnections; + return mTotalAdded >= mConcurrectConnections || + mCapabilityType[capability_type].mAdded >= mCapabilityType[capability_type].mConcurrectConnections; } void AIPerService::added_to_multi_handle(AICapabilityType capability_type) @@ -479,7 +481,10 @@ void AIPerService::adjust_concurrent_connections(int increment) increment = per_service_w->mConcurrectConnections - old_concurrent_connections; for (int i = 0; i < number_of_capability_types; ++i) { - per_service_w->mCapabilityType[i].mMaxPipelinedRequests = llmax(per_service_w->mCapabilityType[i].mMaxPipelinedRequests + increment, (U32)0); + per_service_w->mCapabilityType[i].mMaxPipelinedRequests = llmax(per_service_w->mCapabilityType[i].mMaxPipelinedRequests + increment, 0); + int new_concurrent_connections_per_capability_type = + llclamp((new_concurrent_connections * per_service_w->mCapabilityType[i].mConcurrectConnections + old_concurrent_connections / 2) / old_concurrent_connections, 1, new_concurrent_connections); + per_service_w->mCapabilityType[i].mConcurrectConnections = (U16)new_concurrent_connections_per_capability_type; } } } diff --git a/indra/llmessage/aicurlperservice.h b/indra/llmessage/aicurlperservice.h index bdbc1544c..85885a285 100644 --- a/indra/llmessage/aicurlperservice.h +++ b/indra/llmessage/aicurlperservice.h @@ -144,7 +144,8 @@ class AIPerService { // ctf_full : Set to true when the queue is popped and then still isn't empty; // ctf_starvation: Set to true when the queue was about to be popped but was already empty. U32 mDownloading; // The number of active easy handles with this service for which data was received. - U32 mMaxPipelinedRequests; // The maximum number of accepted requests for this service and (approved) capability type, that didn't finish yet. + U16 mMaxPipelinedRequests; // The maximum number of accepted requests for this service and (approved) capability type, that didn't finish yet. + U16 mConcurrectConnections; // The maximum number of allowed concurrent connections to the service of this capability type. // Declare, not define, constructor and destructor - in order to avoid instantiation of queued_request_type from header. CapabilityType(void); @@ -216,7 +217,7 @@ class AIPerService { void added_to_multi_handle(AICapabilityType capability_type); // Called when an easy handle for this service has been added to the multi handle. void removed_from_multi_handle(AICapabilityType capability_type, bool downloaded_something); // Called when an easy handle for this service is removed again from the multi handle. void download_started(AICapabilityType capability_type) { ++mCapabilityType[capability_type].mDownloading; } - bool throttled(void) const; // Returns true if the maximum number of allowed requests for this service have been added to the multi handle. + bool throttled(AICapabilityType capability_type) const; // Returns true if the maximum number of allowed requests for this service/capability type have been added to the multi handle. bool queue(AICurlEasyRequest const& easy_request, AICapabilityType capability_type, bool force_queuing = true); // Add easy_request to the queue if queue is empty or force_queuing. bool cancel(AICurlEasyRequest const& easy_request, AICapabilityType capability_type); // Remove easy_request from the queue (if it's there). diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index d9b5b19fa..8575fb4f2 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -1736,7 +1736,7 @@ bool MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request, bool f } bool too_much_bandwidth = !curl_easy_request_w->approved() && AIPerService::checkBandwidthUsage(per_service, get_clock_count() * HTTPTimeout::sClockWidth_40ms); PerService_wat per_service_w(*per_service); - if (!too_much_bandwidth && sTotalAdded < curl_max_total_concurrent_connections && !per_service_w->throttled()) + if (!too_much_bandwidth && sTotalAdded < curl_max_total_concurrent_connections && !per_service_w->throttled(capability_type)) { curl_easy_request_w->set_timeout_opts(); if (curl_easy_request_w->add_handle_to_multi(curl_easy_request_w, mMultiHandle) == CURLM_OK) diff --git a/indra/newview/aihttpview.cpp b/indra/newview/aihttpview.cpp index caa2fc24d..522276cb5 100644 --- a/indra/newview/aihttpview.cpp +++ b/indra/newview/aihttpview.cpp @@ -83,11 +83,11 @@ void AIServiceBar::draw() start = mHTTPView->updateColumn(col, start); if (col < 2) { - text = llformat(" | %hu-%hu-%lu,{%hu,%u}/%u", ct.mApprovedRequests, ct.mQueuedCommands, ct.mQueuedRequests.size(), ct.mAdded, ct.mDownloading, ct.mMaxPipelinedRequests); + text = llformat(" | %hu-%hu-%lu,{%hu/%hu,%u}/%u", ct.mApprovedRequests, ct.mQueuedCommands, ct.mQueuedRequests.size(), ct.mAdded, ct.mConcurrectConnections, ct.mDownloading, ct.mMaxPipelinedRequests); } else { - text = llformat(" | --%hu-%lu,{%hu,%u}", ct.mQueuedCommands, ct.mQueuedRequests.size(), ct.mAdded, ct.mDownloading); + text = llformat(" | --%hu-%lu,{%hu/%hu,%u}", ct.mQueuedCommands, ct.mQueuedRequests.size(), ct.mAdded, ct.mConcurrectConnections, ct.mDownloading); } LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(text); @@ -151,7 +151,7 @@ void AIGLHTTPHeaderBar::draw(void) // First header line. F32 height = v_offset + sLineHeight * number_of_header_lines; - text = "HTTP console -- [approved]-commandQ-curlQ,{added,downloading}[/max]"; + text = "HTTP console -- [approved]-commandQ-curlQ,{added/max,downloading}[/max]"; LLFontGL::getFontMonospace()->renderUTF8(text, 0, h_offset, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); text = " | Added/Max"; U32 start = mHTTPView->updateColumn(mc_col, 100); From 6c36b6efa0ae7c7c8de9af21a4af5f57b90ce391 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 24 Jun 2013 15:55:00 +0200 Subject: [PATCH 5/8] Add AIPerService::mCTInUse and AIPerService::mUsedCT Keep track of which capability types are used and in use at the moment for each service. Update the http debug console to only show services that have at least one capability type marked as used (this resets upon restart of the debug console) and show previously used but currently unused capability types in grey. Update CapabilityType::mConcurrentConnections based on usage of the capability type (CT): Each currently in-use CT gets an (approximate) equal portion of the available number of connections, currently unused CTs get 1 connection for future use, so that requests can and will be added to them if they occur. If a CT is currently not in use but was used before then it's connection (but at most one connection) is kept in reserve. For example, if there are 8 connections available and a service served textures and mesh in the past, but currently there are no texture downloads, then mesh get at most 7 connections, so that at all times there is a connection available for textures. When one texture is added, both get 4 connections. --- indra/llmessage/aicurlperservice.cpp | 69 ++++++++++++++++++++++++++-- indra/llmessage/aicurlperservice.h | 44 +++++++++++++++++- indra/newview/aihttpview.cpp | 47 ++++++++++++++++--- indra/newview/aihttpview.h | 1 + indra/newview/llviewermenu.cpp | 3 +- 5 files changed, 150 insertions(+), 14 deletions(-) diff --git a/indra/llmessage/aicurlperservice.cpp b/indra/llmessage/aicurlperservice.cpp index b3d157cf9..61693d91f 100644 --- a/indra/llmessage/aicurlperservice.cpp +++ b/indra/llmessage/aicurlperservice.cpp @@ -75,7 +75,9 @@ AIPerService::AIPerService(void) : mConcurrectConnections(CurlConcurrentConnectionsPerService), mTotalAdded(0), mApprovedFirst(0), - mUnapprovedFirst(0) + mUnapprovedFirst(0), + mUsedCT(0), + mCTInUse(0) { } @@ -279,6 +281,53 @@ void AIPerService::release(AIPerServicePtr& instance) instance.reset(); } +void AIPerService::redivide_connections(void) +{ + // Priority order. + static AICapabilityType order[number_of_capability_types] = { cap_inventory, cap_texture, cap_mesh, cap_other }; + // Count the number of capability types that are currently in use and store the types in an array. + AICapabilityType used_order[number_of_capability_types]; + int number_of_capability_types_in_use = 0; + for (int i = 0; i < number_of_capability_types; ++i) + { + U32 const mask = CT2mask(order[i]); + if ((mCTInUse & mask)) + { + used_order[number_of_capability_types_in_use++] = order[i]; + } + else + { + // Give every other type (that is not in use) one connection, so they can be used (at which point they'll get more). + mCapabilityType[order[i]].mConcurrectConnections = 1; + } + } + // Keep one connection in reserve for currently unused capability types (that have been used before). + int reserve = (mUsedCT != mCTInUse) ? 1 : 0; + // Distribute (mConcurrectConnections - reserve) over number_of_capability_types_in_use. + U16 max_connections_per_CT = (mConcurrectConnections - reserve) / number_of_capability_types_in_use + 1; + // The first count CTs get max_connections_per_CT connections. + int count = (mConcurrectConnections - reserve) % number_of_capability_types_in_use; + for(int i = 1, j = 0;; --i) + { + while (j < count) + { + mCapabilityType[used_order[j++]].mConcurrectConnections = max_connections_per_CT; + } + if (i == 0) + { + break; + } + // Finish the loop till all used CTs are assigned. + count = number_of_capability_types_in_use; + // Never assign 0 as maximum. + if (max_connections_per_CT > 1) + { + // The remaining CTs get one connection less so that the sum of all assigned connections is mConcurrectConnections - reserve. + --max_connections_per_CT; + } + } +} + bool AIPerService::throttled(AICapabilityType capability_type) const { return mTotalAdded >= mConcurrectConnections || @@ -293,14 +342,19 @@ void AIPerService::added_to_multi_handle(AICapabilityType capability_type) void AIPerService::removed_from_multi_handle(AICapabilityType capability_type, bool downloaded_something) { - llassert(mTotalAdded > 0 && mCapabilityType[capability_type].mAdded > 0); - --mCapabilityType[capability_type].mAdded; + CapabilityType& ct(mCapabilityType[capability_type]); + llassert(mTotalAdded > 0 && ct.mAdded > 0); + bool done = --ct.mAdded == 0; if (downloaded_something) { - llassert(mCapabilityType[capability_type].mDownloading > 0); - --mCapabilityType[capability_type].mDownloading; + llassert(ct.mDownloading > 0); + --ct.mDownloading; } --mTotalAdded; + if (done && ct.pipelined_requests() == 0) + { + mark_unused(capability_type); + } } // Returns true if the request was queued. @@ -489,6 +543,11 @@ void AIPerService::adjust_concurrent_connections(int increment) } } +void AIPerService::ResetUsed::operator()(AIPerService::instance_map_type::value_type const& service) const +{ + PerService_wat(*service.second)->resetUsedCt(); +} + void AIPerService::Approvement::honored(void) { if (!mHonored) diff --git a/indra/llmessage/aicurlperservice.h b/indra/llmessage/aicurlperservice.h index 85885a285..b8d8169ea 100644 --- a/indra/llmessage/aicurlperservice.h +++ b/indra/llmessage/aicurlperservice.h @@ -163,6 +163,48 @@ class AIPerService { int mApprovedFirst; // First capability type to try. int mUnapprovedFirst; // First capability type to try after all approved types were tried. + U32 mUsedCT; // Bit mask with one bit per capability type. A '1' means the capability was in use since the last resetUsedCT(). + U32 mCTInUse; // Bit mask with one bit per capability type. A '1' means the capability is in use right now. + + // Helper struct, used in the static resetUsed. + struct ResetUsed { void operator()(instance_map_type::value_type const& service) const; }; + + void redivide_connections(void); + void mark_inuse(AICapabilityType capability_type) + { + U32 bit = CT2mask(capability_type); + if ((mCTInUse & bit) == 0) // If this CT went from unused to used + { + mCTInUse |= bit; + mUsedCT |= bit; + if (mUsedCT != bit) // and more than one CT use this service. + { + redivide_connections(); + } + } + } + void mark_unused(AICapabilityType capability_type) + { + U32 bit = CT2mask(capability_type); + if ((mCTInUse & bit) != 0) // If this CT went from used to unused + { + mCTInUse &= ~bit; + if (mCTInUse && mUsedCT != bit) // and more than one CT use this service, and at least one is in use. + { + redivide_connections(); + } + } + } + public: + static U32 CT2mask(AICapabilityType capability_type) { return (U32)1 << capability_type; } + void resetUsedCt(void) { mUsedCT = mCTInUse; } + bool is_used(AICapabilityType capability_type) const { return (mUsedCT & CT2mask(capability_type)); } + bool is_inuse(AICapabilityType capability_type) const { return (mCTInUse & CT2mask(capability_type)); } + + static void resetUsed(void) { copy_forEach(ResetUsed()); } + U32 is_used(void) const { return mUsedCT; } // Non-zero if this service was used for any capability type. + U32 is_inuse(void) const { return mCTInUse; } // Non-zero if this service is in use for any capability type. + // Global administration of the total number of queued requests of all services combined. private: struct TotalQueued { @@ -212,7 +254,7 @@ class AIPerService { static bool sNoHTTPBandwidthThrottling; // Global override to disable bandwidth throttling. public: - void added_to_command_queue(AICapabilityType capability_type) { ++mCapabilityType[capability_type].mQueuedCommands; } + void added_to_command_queue(AICapabilityType capability_type) { ++mCapabilityType[capability_type].mQueuedCommands; mark_inuse(capability_type); } void removed_from_command_queue(AICapabilityType capability_type) { --mCapabilityType[capability_type].mQueuedCommands; llassert(mCapabilityType[capability_type].mQueuedCommands >= 0); } void added_to_multi_handle(AICapabilityType capability_type); // Called when an easy handle for this service has been added to the multi handle. void removed_from_multi_handle(AICapabilityType capability_type, bool downloaded_something); // Called when an easy handle for this service is removed again from the multi handle. diff --git a/indra/newview/aihttpview.cpp b/indra/newview/aihttpview.cpp index 522276cb5..aa02aa0a9 100644 --- a/indra/newview/aihttpview.cpp +++ b/indra/newview/aihttpview.cpp @@ -76,12 +76,32 @@ void AIServiceBar::draw() LLFontGL::getFontMonospace()->renderUTF8(mName, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(mName); std::string text; - PerService_rat per_service_r(*mPerService); + AIPerService::CapabilityType const* cts; + U32 is_used; + U32 is_inuse; + int total_added; + int concurrent_connections; + size_t bandwidth; + { + PerService_rat per_service_r(*mPerService); + is_used = per_service_r->is_used(); + is_inuse = per_service_r->is_inuse(); + total_added = per_service_r->mTotalAdded; + concurrent_connections = per_service_r->mConcurrectConnections; + bandwidth = per_service_r->bandwidth().truncateData(AIHTTPView::getTime_40ms()); + cts = per_service_r->mCapabilityType; // Not thread-safe, but we're only reading from it and only using the results to show in a debug console. + } for (int col = 0; col < number_of_capability_types; ++col) { - AIPerService::CapabilityType& ct(per_service_r->mCapabilityType[col]); + AICapabilityType capability_type = static_cast(col); + AIPerService::CapabilityType const& ct(cts[capability_type]); start = mHTTPView->updateColumn(col, start); - if (col < 2) + U32 mask = AIPerService::CT2mask(capability_type); + if (!(is_used & mask)) + { + text = " | "; + } + else if (col < 2) { text = llformat(" | %hu-%hu-%lu,{%hu/%hu,%u}/%u", ct.mApprovedRequests, ct.mQueuedCommands, ct.mQueuedRequests.size(), ct.mAdded, ct.mConcurrectConnections, ct.mDownloading, ct.mMaxPipelinedRequests); } @@ -89,15 +109,14 @@ void AIServiceBar::draw() { text = llformat(" | --%hu-%lu,{%hu/%hu,%u}", ct.mQueuedCommands, ct.mQueuedRequests.size(), ct.mAdded, ct.mConcurrectConnections, ct.mDownloading); } - LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, ((is_inuse & mask) == 0) ? LLColor4::grey : text_color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(text); } start = mHTTPView->updateColumn(mc_col, start); - text = llformat(" | %d/%d", per_service_r->mTotalAdded, per_service_r->mConcurrectConnections); + text = llformat(" | %d/%d", total_added, concurrent_connections); LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(text); start = mHTTPView->updateColumn(bw_col, start); - size_t bandwidth = per_service_r->bandwidth().truncateData(AIHTTPView::getTime_40ms()); size_t max_bandwidth = mHTTPView->mMaxBandwidthPerService; text = " | "; LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); @@ -107,7 +126,7 @@ void AIServiceBar::draw() LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(text); text = llformat("/%lu", max_bandwidth / 125); - LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, (bandwidth == 0) ? LLColor4::grey : text_color, LLFontGL::LEFT, LLFontGL::TOP); } LLRect AIServiceBar::getRequiredRect(void) @@ -241,6 +260,18 @@ U32 AIHTTPView::updateColumn(int col, U32 start) return mStartColumn[col]; } +//static +void AIHTTPView::toggle_visibility(void* user_data) +{ + LLView* viewp = (LLView*)user_data; + bool visible = !viewp->getVisible(); + if (visible) + { + AIPerService::resetUsed(); + } + viewp->setVisible(visible); +} + U64 AIHTTPView::sTime_40ms; struct KillView @@ -260,6 +291,8 @@ struct CreateServiceBar void operator()(AIPerService::instance_map_type::value_type const& service) { + if (!PerService_rat(*service.second)->is_used()) + return; AIServiceBar* service_bar = new AIServiceBar(mHTTPView, service); mHTTPView->addChild(service_bar); mHTTPView->mServiceBars.push_back(service_bar); diff --git a/indra/newview/aihttpview.h b/indra/newview/aihttpview.h index f63bc5a61..767d37e54 100644 --- a/indra/newview/aihttpview.h +++ b/indra/newview/aihttpview.h @@ -66,6 +66,7 @@ class AIHTTPView : public LLContainerView public: static U64 getTime_40ms(void) { return sTime_40ms; } + static void toggle_visibility(void* user_data); }; extern AIHTTPView *gHttpView; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index d9582da4f..572648025 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -169,6 +169,7 @@ #include "llfloatermessagelog.h" #include "shfloatermediaticker.h" #include "llpacketring.h" +#include "aihttpview.h" // #include "scriptcounter.h" @@ -818,7 +819,7 @@ void init_client_menu(LLMenuGL* menu) } sub->addChild(new LLMenuItemCheckGL("HTTP Console", - &toggle_visibility, + &AIHTTPView::toggle_visibility, NULL, &get_visibility, (void*)gHttpView, From 719f57e00dff870ab2d60fcd4c3c2d9285a40cc3 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 24 Jun 2013 18:37:57 +0200 Subject: [PATCH 6/8] Fix documentation at top of AIPerService::approveHTTPRequestFor --- indra/llmessage/aicurlthread.cpp | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 8575fb4f2..243654440 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -2647,29 +2647,24 @@ AIThreadSafeSimpleDC AIPerService::sThrottleFrac LLAtomicU32 AIPerService::sHTTPThrottleBandwidth125(250000); bool AIPerService::sNoHTTPBandwidthThrottling; -// Return true if we want at least one more HTTP request for this host. +// Return Approvement if we want at least one more HTTP request for this service. // // It's OK if this function is a bit fuzzy, but we don't want it to return -// true a hundred times on a row when it is called fast in a loop. +// approvement a hundred times on a row when it is called in a tight loop. // Hence the following consideration: // -// This function is called only from LLTextureFetchWorker::doWork, and when it returns true -// then doWork will call LLHTTPClient::request with a NULL default engine (signaling that -// it is OK to run in any thread). -// -// At the end, LLHTTPClient::request calls AIStateMachine::run, which in turn calls -// AIStateMachine::reset at the end. Because NULL is passed as default_engine, reset will -// call AIStateMachine::multiplex to immediately start running the state machine. This -// causes it to go through the states bs_reset, bs_initialize and then bs_multiplex with -// run state AICurlEasyRequestStateMachine_addRequest. Finally, in this state, multiplex -// calls AICurlEasyRequestStateMachine::multiplex_impl which then calls AICurlEasyRequest::addRequest -// which causes an increment of command_queue_w->size and AIPerService::mQueuedCommands. +// If this function returns non-NULL, a Approvement object was created and +// the corresponding AIPerService::CapabilityType::mApprovedRequests was +// incremented. The Approvement object is passed to LLHTTPClient::request, +// and once the request is added to the command queue, used to update the counters. // // It is therefore guaranteed that in one loop of LLTextureFetchWorker::doWork, -// this size is incremented; stopping this function from returning true once we reached the -// threshold of "pipelines" requests (the sum of requests in the command queue, the ones -// throttled and queued in AIPerService::mQueuedRequests and the already -// running requests (in MultiHandle::mAddedEasyRequests)). +// or LLInventoryModelBackgroundFetch::bulkFetch (the two functions currently +// calling this function) this function will stop returning aprovement once we +// reached the threshold of "pipelined" requests (the sum of approved requests, +// requests in the command queue, the ones throttled and queued in +// AIPerService::mQueuedRequests and the already running requests +// (in MultiHandle::mAddedEasyRequests)). // //static AIPerService::Approvement* AIPerService::approveHTTPRequestFor(AIPerServicePtr const& per_service, AICapabilityType capability_type) From 1d04b4af246c4543a8dc96690af96a31c5e0198d Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 24 Jun 2013 18:57:42 +0200 Subject: [PATCH 7/8] Actually use CapabilityType::mConcurrentConnections for Approvements. --- indra/llmessage/aicurlthread.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 243654440..98d57cdcf 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -2707,7 +2707,7 @@ AIPerService::Approvement* AIPerService::approveHTTPRequestFor(AIPerServicePtr c bool reject, equal, increment_threshold; { - PerService_wat per_service_w(*per_service); + PerService_wat per_service_w(*per_service); // Keep this lock for the duration of accesses to ct. CapabilityType& ct(per_service_w->mCapabilityType[capability_type]); S32 const pipelined_requests_per_capability_type = ct.pipelined_requests(); reject = pipelined_requests_per_capability_type >= (S32)ct.mMaxPipelinedRequests; @@ -2717,14 +2717,14 @@ AIPerService::Approvement* AIPerService::approveHTTPRequestFor(AIPerServicePtr c ct.mFlags = 0; if (decrement_threshold) { - if ((int)ct.mMaxPipelinedRequests > per_service_w->mConcurrectConnections) + if ((int)ct.mMaxPipelinedRequests > ct.mConcurrectConnections) { ct.mMaxPipelinedRequests--; } } else if (increment_threshold && reject) { - if ((int)ct.mMaxPipelinedRequests < 2 * per_service_w->mConcurrectConnections) + if ((int)ct.mMaxPipelinedRequests < 2 * ct.mConcurrectConnections) { ct.mMaxPipelinedRequests++; // Immediately take the new threshold into account. From 58972a1f8714dfdcedf1fe0c89dc18b6ecc4384b Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 24 Jun 2013 19:12:37 +0200 Subject: [PATCH 8/8] Bug fix and improvement (HTTP debug console) --- indra/newview/aihttpview.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/indra/newview/aihttpview.cpp b/indra/newview/aihttpview.cpp index aa02aa0a9..84bb4dc59 100644 --- a/indra/newview/aihttpview.cpp +++ b/indra/newview/aihttpview.cpp @@ -109,7 +109,7 @@ void AIServiceBar::draw() { text = llformat(" | --%hu-%lu,{%hu/%hu,%u}", ct.mQueuedCommands, ct.mQueuedRequests.size(), ct.mAdded, ct.mConcurrectConnections, ct.mDownloading); } - LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, ((is_inuse & mask) == 0) ? LLColor4::grey : text_color, LLFontGL::LEFT, LLFontGL::TOP); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, ((is_inuse & mask) == 0) ? LLColor4::grey2 : text_color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(text); } start = mHTTPView->updateColumn(mc_col, start); @@ -122,11 +122,15 @@ void AIServiceBar::draw() LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(text); text = llformat("%lu", bandwidth / 125); + if (bandwidth == 0) + { + text_color = LLColor4::grey2; + } LLColor4 color = (bandwidth > max_bandwidth) ? LLColor4::red : ((bandwidth > max_bandwidth * .75f) ? LLColor4::yellow : text_color); LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, color, LLFontGL::LEFT, LLFontGL::TOP); start += LLFontGL::getFontMonospace()->getWidth(text); text = llformat("/%lu", max_bandwidth / 125); - LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, (bandwidth == 0) ? LLColor4::grey : text_color, LLFontGL::LEFT, LLFontGL::TOP); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); } LLRect AIServiceBar::getRequiredRect(void)