Do not count long poll connections against CurlConcurrentConnectionsPerService
This is necessary for opensim mega regions that can have up to 16 long poll connections for a single service, while upping the maximum number of connections per service just for that is clearly nonsense. I also changed that long poll connections do not "use" the "Other" capability type (which shows that the debug info in the HTTP debug console for the Other capability type turns grey when it only has event poll connections). As a result additional connections become available for textures, mesh and the inventory (the other capability types) on opensim, which has all types on the same service, because now Other does no longer constantly reserves a full share of the available connections. This makes the actual number of connections used for textures and mesh a lot more like it is on Second Life.
This commit is contained in:
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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"
|
||||
};
|
||||
|
||||
@@ -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"; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user