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.
This commit is contained in:
Aleric Inglewood
2013-06-24 15:55:00 +02:00
parent af9018f35f
commit 6c36b6efa0
5 changed files with 150 additions and 14 deletions

View File

@@ -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)

View File

@@ -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.

View File

@@ -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<AICapabilityType>(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);

View File

@@ -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;

View File

@@ -169,6 +169,7 @@
#include "llfloatermessagelog.h"
#include "shfloatermediaticker.h"
#include "llpacketring.h"
#include "aihttpview.h"
// </edit>
#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,