Skip to content
Snippets Groups Projects
Commit f952e72f authored by Logan Dethrow's avatar Logan Dethrow
Browse files

Merge.

parents 1004eff4 be9c0574
Branches
Tags
No related merge requests found
...@@ -4315,6 +4315,17 @@ ...@@ -4315,6 +4315,17 @@
<key>Value</key> <key>Value</key>
<real>1.0</real> <real>1.0</real>
</map> </map>
<key>InventoryDebugSimulateOpFailureRate</key>
<map>
<key>Comment</key>
<string>Rate at which we simulate failures of copy/link requests in some operations</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>0.0</real>
</map>
<key>InventoryDisplayInbox</key> <key>InventoryDisplayInbox</key>
<map> <map>
<key>Comment</key> <key>Comment</key>
......
...@@ -186,140 +186,255 @@ void report_fire(const LLUUID& item_id) ...@@ -186,140 +186,255 @@ void report_fire(const LLUUID& item_id)
llinfos << item_id << llendl; llinfos << item_id << llendl;
} }
class LLInventoryCopyMgr: public LLEventTimer class LLCallAfterInventoryBatchMgr: public LLEventTimer
{ {
public: public:
LLInventoryCopyMgr(LLInventoryModel::item_array_t& src_items, const LLUUID& dst_cat_id, LLCallAfterInventoryBatchMgr(const LLUUID& dst_cat_id,
bool append, const std::string& phase): const std::string& phase_name,
nullary_func_t on_completion_func,
nullary_func_t on_failure_func,
F32 check_period = 5.0,
F32 retry_after = 10.0,
S32 max_retries = 2
):
mDstCatID(dst_cat_id), mDstCatID(dst_cat_id),
mAppend(append), mTrackingPhase(phase_name),
mTrackingPhase(phase), mOnCompletionFunc(on_completion_func),
LLEventTimer(5.0) mOnFailureFunc(on_failure_func),
mRetryAfter(retry_after),
mMaxRetries(max_retries),
mPendingRequests(0),
mFailCount(0),
mRetryCount(0),
LLEventTimer(check_period)
{
if (!mTrackingPhase.empty())
{
selfStartPhase(mTrackingPhase);
}
}
void addItems(LLInventoryModel::item_array_t& src_items)
{ {
for (LLInventoryModel::item_array_t::const_iterator it = src_items.begin(); for (LLInventoryModel::item_array_t::const_iterator it = src_items.begin();
it != src_items.end(); it != src_items.end();
++it) ++it)
{ {
LLViewerInventoryItem* item = *it; LLViewerInventoryItem* item = *it;
mSrcTimes[item->getUUID()] = LLTimer(); llassert(item);
requestCopy(item->getUUID()); addItem(item);
}
if (!mTrackingPhase.empty())
{
selfStartPhase(mTrackingPhase);
} }
} }
void requestCopy(const LLUUID& item_id) // Request or re-request operation for specified item.
void addItem(LLViewerInventoryItem *item)
{ {
LLViewerInventoryItem *item = gInventory.getItem(item_id); const LLUUID& item_id = item->getUUID();
if (!item) if (!item)
{ {
llwarns << "requestCopy item not found " << item_id << llendl; llwarns << "item not found for " << item_id << llendl;
return; return;
} }
copy_inventory_item( mPendingRequests++;
gAgent.getID(), // On a re-request, this will reset the timer.
item->getPermissions().getOwner(), mWaitTimes[item_id] = LLTimer();
item->getUUID(), if (mRetryCounts.find(item_id) == mRetryCounts.end())
mDstCatID, {
std::string(), mRetryCounts[item_id] = 0;
make_inventory_func_callback(boost::bind(&LLInventoryCopyMgr::onCopy,this,item->getUUID(),_1)) }
); else
{
mRetryCounts[item_id]++;
}
if (ll_frand()<gSavedSettings.getF32("InventoryDebugSimulateOpFailureRate"))
{
// simulate server failure by not sending the request.
return;
} }
void onCopy(const LLUUID& src_id, const LLUUID& dst_id) requestOperation(item);
}
virtual void requestOperation(LLViewerInventoryItem *item) = 0;
void onOp(const LLUUID& src_id, const LLUUID& dst_id)
{ {
LL_DEBUGS("Avatar") << "copied, src_id " << src_id << " to dst_id " << dst_id << " after " << mSrcTimes[src_id].getElapsedTimeF32() << " seconds" << llendl; LL_DEBUGS("Avatar") << "copied, src_id " << src_id << " to dst_id " << dst_id << " after " << mWaitTimes[src_id].getElapsedTimeF32() << " seconds" << llendl;
mSrcTimes.erase(src_id); mPendingRequests--;
if (mSrcTimes.empty()) F32 wait_time = mWaitTimes[src_id].getElapsedTimeF32();
mTimeStats.push(wait_time);
mWaitTimes.erase(src_id);
if (mWaitTimes.empty())
{ {
onCompletion(); onCompletionOrFailure();
} }
} }
void onCompletion() void onCompletionOrFailure()
{ {
llinfos << "done" << llendl; // Will never call onCompletion() if any item has been flagged as
// a failure - otherwise could wind up with corrupted
// outfit, involuntary nudity, etc.
reportStats();
if (!mTrackingPhase.empty()) if (!mTrackingPhase.empty())
{ {
selfStopPhase(mTrackingPhase); selfStopPhase(mTrackingPhase);
} }
LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(gInventory.getCategory(mDstCatID), mAppend); if (!mFailCount)
{
onCompletion();
}
else
{
onFailure();
}
}
void onFailure()
{
llinfos << "failed" << llendl;
mOnFailureFunc();
}
void onCompletion()
{
llinfos << "done" << llendl;
mOnCompletionFunc();
} }
// virtual // virtual
// Will be deleted after returning true - only safe to do this if all callbacks have fired. // Will be deleted after returning true - only safe to do this if all callbacks have fired.
BOOL tick() BOOL tick()
{ {
bool all_done = mSrcTimes.empty(); // mPendingRequests will be zero if all requests have been
// responded to. mWaitTimes.empty() will be true if we have
// received at least one reply for each UUID. If requests
// have been dropped and retried, these will not necessarily
// be the same. Only safe to return true if all requests have
// been serviced, since it will result in this object being
// deleted.
bool all_done = (mPendingRequests==0);
if (!mWaitTimes.empty())
{
llwarns << "still waiting on " << mWaitTimes.size() << " items" << llendl;
for (std::map<LLUUID,LLTimer>::const_iterator it = mWaitTimes.begin();
it != mWaitTimes.end();)
{
// Use a copy of iterator because it may be erased/invalidated.
std::map<LLUUID,LLTimer>::const_iterator curr_it = it;
++it;
if (!all_done) F32 time_waited = curr_it->second.getElapsedTimeF32();
S32 retries = mRetryCounts[curr_it->first];
if (time_waited > mRetryAfter)
{ {
llwarns << "possible hang in copy, waiting on " << mSrcTimes.size() << " items" << llendl; if (retries < mMaxRetries)
// TODO possibly add retry logic here. {
LL_DEBUGS("Avatar") << "Waited " << time_waited <<
" for " << curr_it->first << ", retrying" << llendl;
mRetryCount++;
addItem(gInventory.getItem(curr_it->first));
}
else
{
llwarns << "Giving up on " << curr_it->first << " after too many retries" << llendl;
mWaitTimes.erase(curr_it);
mFailCount++;
}
}
if (mWaitTimes.empty())
{
onCompletionOrFailure();
}
} }
}
return all_done; return all_done;
} }
private: void reportStats()
{
LL_DEBUGS("Avatar") << "mFailCount: " << mFailCount << llendl;
LL_DEBUGS("Avatar") << "mRetryCount: " << mRetryCount << llendl;
LL_DEBUGS("Avatar") << "Times: n " << mTimeStats.getCount() << " min " << mTimeStats.getMinValue() << " max " << mTimeStats.getMaxValue() << llendl;
LL_DEBUGS("Avatar") << "Mean " << mTimeStats.getMean() << " stddev " << mTimeStats.getStdDev() << llendl;
}
virtual ~LLCallAfterInventoryBatchMgr()
{
LL_DEBUGS("Avatar") << "deleting" << llendl;
}
protected:
std::string mTrackingPhase; std::string mTrackingPhase;
std::map<LLUUID,LLTimer> mSrcTimes; std::map<LLUUID,LLTimer> mWaitTimes;
std::map<LLUUID,S32> mRetryCounts;
LLUUID mDstCatID; LLUUID mDstCatID;
bool mAppend; nullary_func_t mOnCompletionFunc;
nullary_func_t mOnFailureFunc;
F32 mRetryAfter;
S32 mMaxRetries;
S32 mPendingRequests;
S32 mFailCount;
S32 mRetryCount;
LLViewerStats::StatsAccumulator mTimeStats;
}; };
class LLWearInventoryCategoryCallback : public LLInventoryCallback class LLCallAfterInventoryCopyMgr: public LLCallAfterInventoryBatchMgr
{ {
public: public:
LLWearInventoryCategoryCallback(const LLUUID& cat_id, bool append) LLCallAfterInventoryCopyMgr(LLInventoryModel::item_array_t& src_items,
const LLUUID& dst_cat_id,
const std::string& phase_name,
nullary_func_t on_completion_func,
nullary_func_t on_failure_func
):
LLCallAfterInventoryBatchMgr(dst_cat_id, phase_name, on_completion_func, on_failure_func)
{ {
mCatID = cat_id; addItems(src_items);
mAppend = append;
LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL;
selfStartPhase("wear_inventory_category_callback");
} }
void fire(const LLUUID& item_id)
virtual void requestOperation(LLViewerInventoryItem *item)
{ {
/* copy_inventory_item(
* Do nothing. We only care about the destructor gAgent.getID(),
* item->getPermissions().getOwner(),
* The reason for this is that this callback is used in a hack where the item->getUUID(),
* same callback is given to dozens of items, and the destructor is called mDstCatID,
* after the last item has fired the event and dereferenced it -- if all std::string(),
* the events actually fire! make_inventory_func_callback(boost::bind(&LLCallAfterInventoryBatchMgr::onOp,this,item->getUUID(),_1))
*/ );
LL_DEBUGS("Avatar") << self_av_string() << " fired on copied item, id " << item_id << LL_ENDL;
} }
};
protected: class LLCallAfterInventoryLinkMgr: public LLCallAfterInventoryBatchMgr
~LLWearInventoryCategoryCallback()
{ {
LL_INFOS("Avatar") << self_av_string() << "done all inventory callbacks" << LL_ENDL; LLCallAfterInventoryLinkMgr(LLInventoryModel::item_array_t& src_items,
const LLUUID& dst_cat_id,
selfStopPhase("wear_inventory_category_callback"); const std::string& phase_name,
nullary_func_t on_completion_func,
// Is the destructor called by ordinary dereference, or because the app's shutting down? nullary_func_t on_failure_func
// If the inventory callback manager goes away, we're shutting down, no longer want the callback. ):
if( LLInventoryCallbackManager::is_instantiated() ) LLCallAfterInventoryBatchMgr(dst_cat_id, phase_name, on_completion_func, on_failure_func)
{ {
LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(gInventory.getCategory(mCatID), mAppend); addItems(src_items);
} }
else
virtual void requestOperation(LLViewerInventoryItem *item)
{ {
llwarns << self_av_string() << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl; link_inventory_item(gAgent.getID(),
} item->getLinkedUUID(),
mDstCatID,
item->getName(),
item->LLInventoryItem::getDescription(),
LLAssetType::AT_LINK,
make_inventory_func_callback(
boost::bind(&LLCallAfterInventoryBatchMgr::onOp,this,item->getUUID(),_1)));
} }
private:
LLUUID mCatID;
bool mAppend;
}; };
//Inventory callback updating "dirty" state when destroyed //Inventory callback updating "dirty" state when destroyed
class LLUpdateDirtyState: public LLInventoryCallback class LLUpdateDirtyState: public LLInventoryCallback
{ {
...@@ -2097,7 +2212,13 @@ void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool ap ...@@ -2097,7 +2212,13 @@ void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool ap
name); name);
// Create a CopyMgr that will copy items, manage its own destruction // Create a CopyMgr that will copy items, manage its own destruction
new LLInventoryCopyMgr(*items, new_cat_id, append, std::string("wear_inventory_category_callback")); new LLCallAfterInventoryCopyMgr(
*items, new_cat_id, std::string("wear_inventory_category_callback"),
boost::bind(&LLAppearanceMgr::wearInventoryCategoryOnAvatar,
LLAppearanceMgr::getInstance(),
gInventory.getCategory(new_cat_id),
append),
boost::function<void()>());
// BAP fixes a lag in display of created dir. // BAP fixes a lag in display of created dir.
gInventory.notifyObservers(); gInventory.notifyObservers();
...@@ -2119,6 +2240,7 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego ...@@ -2119,6 +2240,7 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego
if ( !LLInventoryCallbackManager::is_instantiated() ) if ( !LLInventoryCallbackManager::is_instantiated() )
{ {
// shutting down, ignore.
return; return;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment