diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index c6d42e1ad..246371aab 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -1437,6 +1437,7 @@ void BufferedCurlEasyRequest::prepRequest(AICurlEasyRequest_wat& curl_easy_reque mResponder = responder; // Cache capability type, because it will be needed even after the responder was removed. mCapabilityType = responder->capability_type(); + mIsEventPoll = responder->is_event_poll(); // Send header events to responder if needed. if (mResponder->needsHeaders()) diff --git a/indra/llmessage/aicurlperservice.cpp b/indra/llmessage/aicurlperservice.cpp index af01e94f0..570a7e5b2 100644 --- a/indra/llmessage/aicurlperservice.cpp +++ b/indra/llmessage/aicurlperservice.cpp @@ -75,6 +75,7 @@ AIPerService::AIPerService(void) : mConcurrentConnections(CurlConcurrentConnectionsPerService), mApprovedRequests(0), mTotalAdded(0), + mEventPolls(0), mEstablishedConnections(0), mUsedCT(0), mCTInUse(0) @@ -335,24 +336,70 @@ bool AIPerService::throttled(AICapabilityType capability_type) const mCapabilityType[capability_type].mAdded >= mCapabilityType[capability_type].mConcurrentConnections; } -void AIPerService::added_to_multi_handle(AICapabilityType capability_type) +void AIPerService::added_to_multi_handle(AICapabilityType capability_type, bool event_poll) { + if (event_poll) + { + llassert(capability_type == cap_other); + // We want to mark this service as unused when only long polls have been added, because they + // are not counted towards the maximum number of connection for this service and therefore + // should not cause another capability type to get less connections. + // For example, if - like on opensim - Textures and Other capability types use the same + // service then it is nonsense to reserve 4 connections Other and only give 4 connections + // to Textures, only because there is a long poll connection (or any number of long poll + // connections). What we want is to see: 0-0-0,{0/7,0} for textures when Other is ONLY in + // use for the Event Poll. + // + // This translates to that, since we're adding an event_poll and are about to remove it from + // either the command queue OR the request queue, that when mAdded == 1 at the end of this function + // (and the rest of the pipeline is empty) we want to mark this capability type as unused. + // + // If mEventPolls > 0 at this point then mAdded will not be incremented. + // If mEventPolls == 0 then mAdded will be incremented and thus should be 0 now. + // In other words, if the number of mAdded requests is equal to the number of (counted) + // mEventPoll requests right now, then that will still be the case after we added another + // event poll request (the transition from used to unused only being necessary because + // event poll requests in the pipe line ARE counted; not because that is necessary but + // because it would be more complex to not do so). + // + // Moreover, when we get here then the request that is being added is still counted as being in + // the command queue, or the request queue, so that pipelined_requests() will return 1 more than + // the actual count. + U16 counted_event_polls = (mEventPolls == 0) ? 0 : 1; + if (mCapabilityType[capability_type].mAdded == counted_event_polls && + mCapabilityType[capability_type].pipelined_requests() == counted_event_polls + 1) + { + mark_unused(capability_type); + } + if (++mEventPolls > 1) + { + // This only happens on megaregions. Do not count the additional long poll connections against the maximum handles for this service. + return; + } + } ++mCapabilityType[capability_type].mAdded; ++mTotalAdded; } -void AIPerService::removed_from_multi_handle(AICapabilityType capability_type, bool downloaded_something, bool success) +void AIPerService::removed_from_multi_handle(AICapabilityType capability_type, bool event_poll, bool downloaded_something, bool success) { CapabilityType& ct(mCapabilityType[capability_type]); - llassert(mTotalAdded > 0 && ct.mAdded > 0); - bool done = --ct.mAdded == 0; + llassert(mTotalAdded > 0 && ct.mAdded > 0 && (!event_poll || mEventPolls)); + if (!event_poll || --mEventPolls == 0) + { + --ct.mAdded; + --mTotalAdded; + } if (downloaded_something) { llassert(ct.mDownloading > 0); --ct.mDownloading; } - --mTotalAdded; - if (done && ct.pipelined_requests() == 0) + // If the number of added request handles is equal to the number of counted event poll handles, + // in other words, when there are only long poll connections left, then mark the capability type + // as unused. + U16 counted_event_polls = (capability_type != cap_other || mEventPolls == 0) ? 0 : 1; + if (ct.mAdded == counted_event_polls && ct.pipelined_requests() == counted_event_polls) { mark_unused(capability_type); } @@ -467,6 +514,9 @@ void AIPerService::add_queued_to(curlthread::MultiHandle* multi_handle, bool onl break; } // Request was added, remove it from the queue. + // Note: AIPerService::added_to_multi_handle (called from add_easy_request above) relies on the fact that + // we first add the easy handle and then remove it from the request queue (which is necessary to avoid + // that another thread adds one just in between). ct.mQueuedRequests.pop_front(); // Mark that at least one request of this CT was successfully added. success |= mask; diff --git a/indra/llmessage/aicurlperservice.h b/indra/llmessage/aicurlperservice.h index 188d383bf..8af4cdab6 100644 --- a/indra/llmessage/aicurlperservice.h +++ b/indra/llmessage/aicurlperservice.h @@ -169,6 +169,7 @@ class AIPerService { int mConcurrentConnections; // The maximum number of allowed concurrent connections to this service. int mApprovedRequests; // The number of approved requests for this service by approveHTTPRequestFor that were not added to the command queue yet. int mTotalAdded; // Number of active easy handles with this service. + int mEventPolls; // Number of active event poll handles with this service. int mEstablishedConnections; // Number of connected sockets to this service. U32 mUsedCT; // Bit mask with one bit per capability type. A '1' means the capability was in use since the last resetUsedCT(). @@ -267,9 +268,10 @@ class AIPerService { public: 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, bool success); // Called when an easy handle for this service is removed again from the multi handle. + void removed_from_command_queue(AICapabilityType capability_type) { llassert(mCapabilityType[capability_type].mQueuedCommands > 0); --mCapabilityType[capability_type].mQueuedCommands; } + void added_to_multi_handle(AICapabilityType capability_type, bool event_poll); // Called when an easy handle for this service has been added to the multi handle. + void removed_from_multi_handle(AICapabilityType capability_type, bool event_poll, + bool downloaded_something, bool success); // 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(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 nothing_added(AICapabilityType capability_type) const { return mCapabilityType[capability_type].mAdded == 0; } diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index cbb9a293d..c5e3514a7 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -418,6 +418,7 @@ class BufferedCurlEasyRequest : public CurlEasyRequest { buffer_ptr_t mOutput; LLHTTPClient::ResponderPtr mResponder; AICapabilityType mCapabilityType; + bool mIsEventPoll; //U32 mBodyLimit; // From the old LLURLRequestDetail::mBodyLimit, but never used. U32 mStatus; // HTTP status, decoded from the first header line. std::string mReason; // The "reason" from the same header line. @@ -466,6 +467,7 @@ class BufferedCurlEasyRequest : public CurlEasyRequest { // Return the capability type of this request. AICapabilityType capability_type(void) const { llassert(mCapabilityType != number_of_capability_types); return mCapabilityType; } + bool is_event_poll(void) const { return mIsEventPoll; } // Return true if any data was received. bool received_data(void) const { return mTotalRawBytes > 0; } diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 5339e9b65..83e258fd9 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -1340,6 +1340,9 @@ void AICurlThread::process_commands(AICurlMultiHandle_wat const& multi_handle_w) break; case cmd_add: { + // Note: AIPerService::added_to_multi_handle (called from add_easy_request) relies on the fact that + // we first add the easy handle and then remove it from the command queue (which is necessary to + // avoid that another thread adds one just in between). multi_handle_w->add_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request()), false); PerService_wat(*per_service)->removed_from_command_queue(capability_type); break; @@ -1747,10 +1750,12 @@ bool MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request, bool f { bool throttled = true; // Default. AICapabilityType capability_type; + bool event_poll; AIPerServicePtr per_service; { AICurlEasyRequest_wat curl_easy_request_w(*easy_request); capability_type = curl_easy_request_w->capability_type(); + event_poll = curl_easy_request_w->is_event_poll(); per_service = curl_easy_request_w->getPerServicePtr(); if (!from_queue) { @@ -1782,7 +1787,7 @@ bool MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request, bool f curl_easy_request_w->set_timeout_opts(); if (curl_easy_request_w->add_handle_to_multi(curl_easy_request_w, mMultiHandle) == CURLM_OK) { - per_service_w->added_to_multi_handle(capability_type); // (About to be) added to mAddedEasyRequests. + per_service_w->added_to_multi_handle(capability_type, event_poll); // (About to be) added to mAddedEasyRequests. throttled = false; // Fall through... } } @@ -1841,6 +1846,7 @@ CURLMcode MultiHandle::remove_easy_request(addedEasyRequests_type::iterator cons { CURLMcode res; AICapabilityType capability_type; + bool event_poll; AIPerServicePtr per_service; { AICurlEasyRequest_wat curl_easy_request_w(**iter); @@ -1848,8 +1854,9 @@ CURLMcode MultiHandle::remove_easy_request(addedEasyRequests_type::iterator cons bool success = curl_easy_request_w->success(); res = curl_easy_request_w->remove_handle_from_multi(curl_easy_request_w, mMultiHandle); capability_type = curl_easy_request_w->capability_type(); + event_poll = curl_easy_request_w->is_event_poll(); per_service = curl_easy_request_w->getPerServicePtr(); - PerService_wat(*per_service)->removed_from_multi_handle(capability_type, downloaded_something, success); // (About to be) removed from mAddedEasyRequests. + PerService_wat(*per_service)->removed_from_multi_handle(capability_type, event_poll, downloaded_something, success); // (About to be) removed from mAddedEasyRequests. #ifdef SHOW_ASSERT curl_easy_request_w->mRemovedPerCommand = as_per_command; #endif diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index 507ee2a04..a366811d6 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -228,6 +228,9 @@ public: // If this function returns false then we generate an error when a redirect status (300..399) is received. virtual bool redirect_status_ok(void) const { return true; } + // Overridden by LLEventPollResponder to return true. + virtual bool is_event_poll(void) const { return false; } + // Returns the capability type used by this responder. virtual AICapabilityType capability_type(void) const { return cap_other; } diff --git a/indra/newview/aihttpview.cpp b/indra/newview/aihttpview.cpp index 58bc2f689..ec9e3acc6 100644 --- a/indra/newview/aihttpview.cpp +++ b/indra/newview/aihttpview.cpp @@ -80,6 +80,7 @@ void AIServiceBar::draw() U32 is_used; U32 is_inuse; int total_added; + int event_polls; int established_connections; int concurrent_connections; size_t bandwidth; @@ -88,6 +89,7 @@ void AIServiceBar::draw() is_used = per_service_r->is_used(); is_inuse = per_service_r->is_inuse(); total_added = per_service_r->mTotalAdded; + event_polls = per_service_r->mEventPolls; established_connections = per_service_r->mEstablishedConnections; concurrent_connections = per_service_r->mConcurrentConnections; bandwidth = per_service_r->bandwidth().truncateData(AIHTTPView::getTime_40ms()); @@ -148,7 +150,7 @@ void AIServiceBar::draw() } start = mHTTPView->updateColumn(mc_col, start); #if defined(CWDEBUG) || defined(DEBUG_CURLIO) - text = llformat(" | %d,%d/%d", total_added, established_connections, concurrent_connections); + text = llformat(" | %d,%d,%d/%d", total_added, event_polls, established_connections, concurrent_connections); #else text = llformat(" | %d/%d", total_added, concurrent_connections); #endif @@ -227,6 +229,7 @@ void AIGLHTTPHeaderBar::draw(void) height -= sLineHeight; start = h_offset; text = "Service (host:port)"; + // This must match AICapabilityType! static char const* caption[number_of_capability_types] = { " | Textures", " | Inventory", " | Mesh", " | Other" }; diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp index 75d3c4eda..604f8a7c8 100644 --- a/indra/newview/lleventpoll.cpp +++ b/indra/newview/lleventpoll.cpp @@ -75,6 +75,7 @@ namespace void handleMessage(const LLSD& content); /*virtual*/ void error(U32 status, const std::string& reason); /*virtual*/ void result(const LLSD& content); + /*virtual*/ bool is_event_poll(void) const { return true; } /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return eventPollResponder_timeout; } /*virtual*/ char const* getName(void) const { return "LLEventPollResponder"; }