Yet more cleanup around llmediadataclient.

In LLVOVolume, added a count of LLMediaDataClientObjectImpl objects referencing each LLVOVolume object.  This allows LLVOVolume::markDead() to skip the relatively expensive calls to removeFromQueue() when the LLVOVolume is known to have no active references.

Refactored LLMediaDataClient and its two child classes so that only LLObjectMediaDataClient has the round-robin queue (LLObjectMediaNavigateClient doesn't need it), and cleaned up some of the virtual function hierarchy around queue processing.

In LLMediaDataClient, added tracking for requests that aren't currently in a queue (i.e. requests that are in flight or waiting for retries) so they can be found when their objects are marked dead.

LLMediaDataClient::Request now directly keeps track of the object ID and face associated with the request.

Removed the "markedSent" concept from requests.  Requests that have been sent are no longer kept in a queue.

The Retry timer now references the Request object instead of the Responder.

Replaced LLMediaDataClient::findOrRemove() with separate template functions for find and remove.
master
Monroe Linden 2010-07-29 15:50:20 -07:00
parent 112abfe888
commit 94655359aa
5 changed files with 731 additions and 398 deletions

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,7 @@
#define LL_LLMEDIADATACLIENT_H
#include "llhttpclient.h"
#include <queue>
#include <set>
#include "llrefcount.h"
#include "llpointer.h"
#include "lleventtimer.h"
@ -48,6 +48,8 @@ public:
virtual U8 getMediaDataCount() const = 0;
// Get the media data at index, as an LLSD
virtual LLSD getMediaDataLLSD(U8 index) const = 0;
// Return true if the current URL for the face in the media data matches the specified URL.
virtual bool isCurrentMediaUrl(U8 index, const std::string &url) const = 0;
// Get this object's UUID
virtual LLUUID getID() const = 0;
// Navigate back to previous URL
@ -96,16 +98,16 @@ public:
F32 getRetryTimerDelay() const { return mRetryTimerDelay; }
// Returns true iff the queue is empty
bool isEmpty() const;
virtual bool isEmpty() const;
// Returns true iff the given object is in the queue
bool isInQueue(const LLMediaDataClientObject::ptr_t &object);
virtual bool isInQueue(const LLMediaDataClientObject::ptr_t &object);
// Remove the given object from the queue. Returns true iff the given object is removed.
bool removeFromQueue(const LLMediaDataClientObject::ptr_t &object);
virtual void removeFromQueue(const LLMediaDataClientObject::ptr_t &object);
// Called only by the Queue timer and tests (potentially)
bool processQueueTimer();
virtual bool processQueueTimer();
protected:
// Destructor
@ -122,6 +124,8 @@ protected:
// and must create the correct type of responder.
virtual Responder *createResponder() = 0;
virtual std::string getURL() { return ""; }
enum Type {
GET,
UPDATE,
@ -131,15 +135,14 @@ protected:
protected:
// The only way to create one of these is through a subclass.
Request(Type in_type, LLMediaDataClientObject *obj, LLMediaDataClient *mdc);
Request(Type in_type, LLMediaDataClientObject *obj, LLMediaDataClient *mdc, S32 face = -1);
public:
LLMediaDataClientObject *getObject() const { return mObject; }
U32 getNum() const { return mNum; }
U32 getRetryCount() const { return mRetryCount; }
void incRetryCount() { mRetryCount++; };
Type getType() const { return mType; };
bool isMarkedSent() const { return mMarkedSent; }
void incRetryCount() { mRetryCount++; }
Type getType() const { return mType; }
F64 getScore() const { return mScore; }
// Note: may return empty string!
@ -153,13 +156,26 @@ protected:
F32 getRetryTimerDelay() const;
U32 getMaxNumRetries() const;
bool isNew() const { return mObject.notNull() ? mObject->isNew() : false; }
bool isObjectValid() const { return mObject.notNull() ? (!mObject->isDead()) : false; }
void markSent(bool flag);
bool isObjectValid() const { return mObject.notNull() && (!mObject->isDead()); }
bool isNew() const { return isObjectValid() && mObject->isNew(); }
void updateScore();
void markDead();
bool isDead();
void startTracking();
void stopTracking();
friend std::ostream& operator<<(std::ostream &s, const Request &q);
const LLUUID &getID() const { return mObjectID; }
S32 getFace() const { return mFace; }
bool isMatch (const Request* other, Type match_type = ANY) const
{
return ((match_type == ANY) || (mType == other->mType)) &&
(mFace == other->mFace) &&
(mObjectID == other->mObjectID);
}
protected:
LLMediaDataClientObject::ptr_t mObject;
private:
@ -169,7 +185,9 @@ protected:
static U32 sNum;
U32 mRetryCount;
F64 mScore;
bool mMarkedSent;
LLUUID mObjectID;
S32 mFace;
// Back pointer to the MDC...not a ref!
LLMediaDataClient *mMDC;
@ -189,39 +207,63 @@ protected:
request_ptr_t &getRequest() { return mRequest; }
private:
class RetryTimer : public LLEventTimer
{
public:
RetryTimer(F32 time, Responder *);
virtual BOOL tick();
private:
// back-pointer
boost::intrusive_ptr<Responder> mResponder;
};
request_ptr_t mRequest;
};
class RetryTimer : public LLEventTimer
{
public:
RetryTimer(F32 time, request_ptr_t);
virtual BOOL tick();
private:
// back-pointer
request_ptr_t mRequest;
};
protected:
typedef std::list<request_ptr_t> request_queue_t;
typedef std::set<request_ptr_t> request_set_t;
// Subclasses must override to return a cap name
virtual const char *getCapabilityName() const = 0;
// Puts the request into a queue, appropriately handling duplicates, etc.
virtual void enqueue(Request*) = 0;
virtual bool request_needs_purge(request_ptr_t request);
virtual void sortQueue();
virtual void serviceQueue();
virtual void enqueue(Request*);
virtual request_queue_t *getQueue() { return &mQueue; };
// Gets the next request, removing it from the queue
virtual request_ptr_t dequeue();
virtual bool canServiceRequest(request_ptr_t request) { return true; };
// Returns a request to the head of the queue (should only be used for requests that came from dequeue
virtual void pushBack(request_ptr_t request);
void trackRequest(request_ptr_t request);
void stopTrackingRequest(request_ptr_t request);
request_queue_t mQueue;
const F32 mQueueTimerDelay;
const F32 mRetryTimerDelay;
const U32 mMaxNumRetries;
const U32 mMaxSortedQueueSize;
const U32 mMaxRoundRobinQueueSize;
// Set for keeping track of requests that aren't in either queue. This includes:
// Requests that have been sent and are awaiting a response (pointer held by the Responder)
// Requests that are waiting for their retry timers to fire (pointer held by the retry timer)
request_set_t mUnQueuedRequests;
void startQueueTimer();
void stopQueueTimer();
private:
typedef std::list<request_ptr_t> request_queue_t;
// Return whether the given object is/was in the queue
static LLMediaDataClient::request_ptr_t findOrRemove(request_queue_t &queue, const LLMediaDataClientObject::ptr_t &obj, bool remove, Request::Type type);
// Comparator for sorting
static bool compareRequests(const request_ptr_t &o1, const request_ptr_t &o2);
static F64 getObjectScore(const LLMediaDataClientObject::ptr_t &obj);
friend std::ostream& operator<<(std::ostream &s, const Request &q);
@ -237,24 +279,10 @@ private:
LLPointer<LLMediaDataClient> mMDC;
};
void startQueueTimer();
void stopQueueTimer();
void setIsRunning(bool val) { mQueueTimerIsRunning = val; }
void swapCurrentQueue();
request_queue_t *getCurrentQueue();
const F32 mQueueTimerDelay;
const F32 mRetryTimerDelay;
const U32 mMaxNumRetries;
const U32 mMaxSortedQueueSize;
const U32 mMaxRoundRobinQueueSize;
bool mQueueTimerIsRunning;
request_queue_t mSortedQueue;
request_queue_t mRoundRobinQueue;
bool mCurrentQueueIsTheSortedQueue;
};
@ -262,14 +290,15 @@ private:
class LLObjectMediaDataClient : public LLMediaDataClient
{
public:
LOG_CLASS(LLObjectMediaDataClient);
LLObjectMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
U32 max_retries = MAX_RETRIES,
U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE,
U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE)
: LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries)
: LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries),
mCurrentQueueIsTheSortedQueue(true)
{}
virtual ~LLObjectMediaDataClient() {}
void fetchMedia(LLMediaDataClientObject *object);
void updateMedia(LLMediaDataClientObject *object);
@ -289,11 +318,29 @@ public:
/*virtual*/ LLSD getPayload() const;
/*virtual*/ Responder *createResponder();
};
// Returns true iff the queue is empty
virtual bool isEmpty() const;
// Returns true iff the given object is in the queue
virtual bool isInQueue(const LLMediaDataClientObject::ptr_t &object);
// Remove the given object from the queue. Returns true iff the given object is removed.
virtual void removeFromQueue(const LLMediaDataClientObject::ptr_t &object);
virtual bool processQueueTimer();
virtual bool canServiceRequest(request_ptr_t request);
protected:
// Subclasses must override to return a cap name
virtual const char *getCapabilityName() const;
virtual request_queue_t *getQueue();
// Puts the request into the appropriate queue
virtual void enqueue(Request*);
class Responder : public LLMediaDataClient::Responder
{
public:
@ -301,6 +348,16 @@ protected:
: LLMediaDataClient::Responder(request) {}
virtual void result(const LLSD &content);
};
private:
// The Get/Update data client needs a second queue to avoid object updates starving load-ins.
void swapCurrentQueue();
request_queue_t mRoundRobinQueue;
bool mCurrentQueueIsTheSortedQueue;
// Comparator for sorting
static bool compareRequestScores(const request_ptr_t &o1, const request_ptr_t &o2);
void sortQueue();
};
@ -308,6 +365,7 @@ protected:
class LLObjectMediaNavigateClient : public LLMediaDataClient
{
public:
LOG_CLASS(LLObjectMediaNavigateClient);
// NOTE: from llmediaservice.h
static const int ERROR_PERMISSION_DENIED_CODE = 8002;
@ -318,18 +376,20 @@ public:
U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE)
: LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries)
{}
virtual ~LLObjectMediaNavigateClient() {}
void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url);
// Puts the request into the appropriate queue
virtual void enqueue(Request*);
class RequestNavigate: public Request
{
public:
RequestNavigate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc, U8 texture_index, const std::string &url);
/*virtual*/ LLSD getPayload() const;
/*virtual*/ Responder *createResponder();
/*virtual*/ std::string getURL() { return mURL; }
private:
U8 mTextureIndex;
std::string mURL;
};

View File

@ -93,8 +93,14 @@ static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes");
class LLMediaDataClientObjectImpl : public LLMediaDataClientObject
{
public:
LLMediaDataClientObjectImpl(LLVOVolume *obj, bool isNew) : mObject(obj), mNew(isNew) {}
LLMediaDataClientObjectImpl() { mObject = NULL; }
LLMediaDataClientObjectImpl(LLVOVolume *obj, bool isNew) : mObject(obj), mNew(isNew)
{
mObject->addMDCImpl();
}
~LLMediaDataClientObjectImpl()
{
mObject->removeMDCImpl();
}
virtual U8 getMediaDataCount() const
{ return mObject->getNumTEs(); }
@ -119,6 +125,18 @@ public:
}
return result;
}
virtual bool isCurrentMediaUrl(U8 index, const std::string &url) const
{
LLTextureEntry *te = mObject->getTE(index);
if (te)
{
if (te->getMediaData())
{
return (te->getMediaData()->getCurrentURL() == url);
}
}
return url.empty();
}
virtual LLUUID getID() const
{ return mObject->getID(); }
@ -193,6 +211,7 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re
mMediaImplList.resize(getNumTEs());
mLastFetchedMediaVersion = -1;
mIndexInTex = 0;
mMDCImplCount = 0;
}
LLVOVolume::~LLVOVolume()
@ -218,9 +237,12 @@ void LLVOVolume::markDead()
{
if (!mDead)
{
LLMediaDataClientObject::ptr_t obj = new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this), false);
if (sObjectMediaClient) sObjectMediaClient->removeFromQueue(obj);
if (sObjectMediaNavigateClient) sObjectMediaNavigateClient->removeFromQueue(obj);
if(getMDCImplCount() > 0)
{
LLMediaDataClientObject::ptr_t obj = new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this), false);
if (sObjectMediaClient) sObjectMediaClient->removeFromQueue(obj);
if (sObjectMediaNavigateClient) sObjectMediaNavigateClient->removeFromQueue(obj);
}
// Detach all media impls from this object
for(U32 i = 0 ; i < mMediaImplList.size() ; i++)

View File

@ -274,6 +274,10 @@ public:
// Returns the "last fetched" media version, or -1 if not fetched yet
S32 getLastFetchedMediaVersion() const { return mLastFetchedMediaVersion; }
void addMDCImpl() { ++mMDCImplCount; }
void removeMDCImpl() { --mMDCImplCount; }
S32 getMDCImplCount() { return mMDCImplCount; }
protected:
S32 computeLODDetail(F32 distance, F32 radius);
@ -307,6 +311,7 @@ private:
media_list_t mMediaImplList;
S32 mLastFetchedMediaVersion; // as fetched from the server, starts as -1
S32 mIndexInTex;
S32 mMDCImplCount;
// statics
public:
static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop

View File

@ -70,8 +70,8 @@
#define MEDIA_DATA "\
<array> \
<string>foo</string> \
<string>bar</string> \
<string>http://foo.example.com</string> \
<string>http://bar.example.com</string> \
<string>baz</string> \
</array>"
@ -167,6 +167,8 @@ public:
{ return mRep["media_data"].size(); }
virtual LLSD getMediaDataLLSD(U8 index) const
{ return mRep["media_data"][(LLSD::Integer)index]; }
virtual bool isCurrentMediaUrl(U8 index, const std::string &url) const
{ return (mRep["media_data"][(LLSD::Integer)index].asString() == url); }
virtual LLUUID getID() const
{ return mRep["uuid"]; }
virtual void mediaNavigateBounceBack(U8 index)
@ -567,10 +569,6 @@ namespace tut
mdc->fetchMedia(o2);
mdc->fetchMedia(o3);
mdc->fetchMedia(o4);
// and mark the second and fourth ones dead.
dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o2))->markDead();
dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o4))->markDead();
ensure("is in queue 1", mdc->isInQueue(o1));
ensure("is in queue 2", mdc->isInQueue(o2));
@ -578,9 +576,22 @@ namespace tut
ensure("is in queue 4", mdc->isInQueue(o4));
ensure("post records", gPostRecords->size(), 0);
// and mark the second and fourth ones dead. Call removeFromQueue when marking dead, since this is what LLVOVolume will do.
dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o2))->markDead();
mdc->removeFromQueue(o2);
dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o4))->markDead();
mdc->removeFromQueue(o4);
// The removeFromQueue calls should remove the second and fourth ones
ensure("is in queue 1", mdc->isInQueue(o1));
ensure("is not in queue 2", !mdc->isInQueue(o2));
ensure("is in queue 3", mdc->isInQueue(o3));
ensure("is not in queue 4", !mdc->isInQueue(o4));
ensure("post records", gPostRecords->size(), 0);
::pump_timers();
// The first tick should remove the second and fourth ones, and process the first one
// The first tick should process the first item
ensure("is not in queue 1", !mdc->isInQueue(o1));
ensure("is not in queue 2", !mdc->isInQueue(o2));
ensure("is in queue 3", mdc->isInQueue(o3));
@ -701,7 +712,7 @@ namespace tut
// queue up all 4 objects. The first two should be in the sorted
// queue [2 1], the second in the round-robin queue. The queues
// are serviced interleaved, so we should expect:
// 2, 4, 1, 3
// 2, 3, 1, 4
mdc->fetchMedia(o1);
mdc->fetchMedia(o2);
mdc->fetchMedia(o3);
@ -720,8 +731,8 @@ namespace tut
++tick_num;
// 1 The first tick should remove object 2
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 1);
@ -730,22 +741,21 @@ namespace tut
::pump_timers();
++tick_num;
// 2 The second tick should send object 4, but it will still be
// "in the queue"
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
// 2 The second tick should send object 3
ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 2);
ensure(STR(tick_num) + ". post object id", (*gPostRecords)[1]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_4));
ensure(STR(tick_num) + ". post object id", (*gPostRecords)[1]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_3));
::pump_timers();
++tick_num;
// 3 The third tick should remove object 1
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 3);
ensure(STR(tick_num) + ". post object id", (*gPostRecords)[2]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_1));
@ -753,22 +763,20 @@ namespace tut
::pump_timers();
++tick_num;
// 4 The fourth tick should send object 3, but it will still be
// "in the queue"
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
// 4 The fourth tick should send object 4
ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is in queue 3", mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is in queue 4", mdc->isInQueue(o4));
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 4);
ensure(STR(tick_num) + ". post object id", (*gPostRecords)[3]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_3));
ensure(STR(tick_num) + ". post object id", (*gPostRecords)[3]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_4));
::pump_timers();
++tick_num;
// 5 The fifth tick should now identify objects 3 and 4 as no longer
// needing "updating", and remove them from the queue
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
// 5 The fifth tick should not change the state of anything.
ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1));
ensure(STR(tick_num) + ". is not in queue 2", !mdc->isInQueue(o2));
ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3));
ensure(STR(tick_num) + ". is not in queue 4", !mdc->isInQueue(o4));
ensure(STR(tick_num) + ". post records", gPostRecords->size(), 4);
@ -918,7 +926,7 @@ namespace tut
// But, we need to clear the queue, or else we won't destroy MDC...
// this is a strange interplay between the queue timer and the MDC
ensure("o2 couldn't be removed from queue", mdc->removeFromQueue(o2));
mdc->removeFromQueue(o2);
// tick
::pump_timers();
}
@ -927,4 +935,41 @@ namespace tut
ensure("refcount of o3", o3->getNumRefs(), 1);
ensure("refcount of o4", o4->getNumRefs(), 1);
}
template<> template<>
void mediadataclient_object_t::test<13>()
{
//
// Test supression of redundant navigates.
//
LOG_TEST(13);
LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"1.0","true"));
{
LLPointer<LLObjectMediaNavigateClient> mdc = new LLObjectMediaNavigateClient(NO_PERIOD,NO_PERIOD);
const char *TEST_URL = "http://foo.example.com";
const char *TEST_URL_2 = "http://example.com";
mdc->navigate(o1, 0, TEST_URL);
mdc->navigate(o1, 1, TEST_URL);
mdc->navigate(o1, 0, TEST_URL_2);
mdc->navigate(o1, 1, TEST_URL_2);
// This should add two requests to the queue, one for face 0 of the object and one for face 1.
ensure("before pump: 1 is in queue", mdc->isInQueue(o1));
::pump_timers();
ensure("after first pump: 1 is in queue", mdc->isInQueue(o1));
::pump_timers();
ensure("after second pump: 1 is not in queue", !mdc->isInQueue(o1));
ensure("first post has correct url", (*gPostRecords)[0]["body"][LLMediaEntry::CURRENT_URL_KEY].asString(), std::string(TEST_URL_2));
ensure("second post has correct url", (*gPostRecords)[1]["body"][LLMediaEntry::CURRENT_URL_KEY].asString(), std::string(TEST_URL_2));
}
}
}