summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/app_settings/settings.xml22
-rwxr-xr-xindra/newview/llmediadataclient.cpp782
-rwxr-xr-xindra/newview/llmediadataclient.h90
-rw-r--r--indra/newview/llvovolume.cpp79
-rw-r--r--indra/newview/llvovolume.h8
-rw-r--r--indra/newview/tests/llmediadataclient_test.cpp405
6 files changed, 920 insertions, 466 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index c43032a3cf..9ef74364ac 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -5505,6 +5505,28 @@
<key>Value</key>
<real>5.0</real>
</map>
+ <key>PrimMediaMaxSortedQueueSize</key>
+ <map>
+ <key>Comment</key>
+ <string>Maximum number of objects the viewer will load media for initially</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>100000</integer>
+ </map>
+ <key>PrimMediaMaxRoundRobinQueueSize</key>
+ <map>
+ <key>Comment</key>
+ <string>Maximum number of objects the viewer will continuously update media for</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>100000</integer>
+ </map>
<key>ProbeHardwareOnStartup</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llmediadataclient.cpp b/indra/newview/llmediadataclient.cpp
index 3c337961e1..0a20a4a1c9 100755
--- a/indra/newview/llmediadataclient.cpp
+++ b/indra/newview/llmediadataclient.cpp
@@ -61,248 +61,332 @@
const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s)
const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 5.0; // secs
const U32 LLMediaDataClient::MAX_RETRIES = 4;
+const U32 LLMediaDataClient::MAX_SORTED_QUEUE_SIZE = 10000;
+const U32 LLMediaDataClient::MAX_ROUND_ROBIN_QUEUE_SIZE = 10000;
//////////////////////////////////////////////////////////////////////////////////////
//
-// LLMediaDataClient::Request
+// LLMediaDataClient
//
//////////////////////////////////////////////////////////////////////////////////////
-/*static*/U32 LLMediaDataClient::Request::sNum = 0;
-LLMediaDataClient::Request::Request(const std::string &cap_name,
- const LLSD& sd_payload,
- LLMediaDataClientObject *obj,
- LLMediaDataClient *mdc)
- : mCapName(cap_name),
- mPayload(sd_payload),
- mObject(obj),
- mNum(++sNum),
- mRetryCount(0),
- mMDC(mdc)
+LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay,
+ F32 retry_timer_delay,
+ U32 max_retries,
+ U32 max_sorted_queue_size,
+ U32 max_round_robin_queue_size)
+ : mQueueTimerDelay(queue_timer_delay),
+ mRetryTimerDelay(retry_timer_delay),
+ mMaxNumRetries(max_retries),
+ mMaxSortedQueueSize(max_sorted_queue_size),
+ mMaxRoundRobinQueueSize(max_round_robin_queue_size),
+ mQueueTimerIsRunning(false),
+ mCurrentQueueIsTheSortedQueue(true)
{
}
-LLMediaDataClient::Request::~Request()
+LLMediaDataClient::~LLMediaDataClient()
{
- LL_DEBUGS("LLMediaDataClient") << "~Request" << (*this) << LL_ENDL;
- mMDC = NULL;
- mObject = NULL;
-}
+ stopQueueTimer();
-
-std::string LLMediaDataClient::Request::getCapability() const
-{
- return getObject()->getCapabilityUrl(getCapName());
+ // This should clear the queue, and hopefully call all the destructors.
+ LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " <<
+ (isEmpty() ? "<empty> " : "<not empty> ") << LL_ENDL;
+
+ mSortedQueue.clear();
+ mRoundRobinQueue.clear();
}
-// Helper function to get the "type" of request, which just pokes around to
-// discover it.
-LLMediaDataClient::Request::Type LLMediaDataClient::Request::getType() const
+bool LLMediaDataClient::isEmpty() const
{
- if (mCapName == "ObjectMediaNavigate")
- {
- return NAVIGATE;
- }
- else if (mCapName == "ObjectMedia")
- {
- const std::string &verb = mPayload["verb"];
- if (verb == "GET")
- {
- return GET;
- }
- else if (verb == "UPDATE")
- {
- return UPDATE;
- }
- }
- llassert(false);
- return GET;
+ return mSortedQueue.empty() && mRoundRobinQueue.empty();
}
-const char *LLMediaDataClient::Request::getTypeAsString() const
+bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object)
{
- Type t = getType();
- switch (t)
- {
- case GET:
- return "GET";
- break;
- case UPDATE:
- return "UPDATE";
- break;
- case NAVIGATE:
- return "NAVIGATE";
- break;
- }
- return "";
+ return (LLMediaDataClient::findOrRemove(mSortedQueue, object, false/*remove*/, LLMediaDataClient::Request::ANY).notNull()
+ || (LLMediaDataClient::findOrRemove(mRoundRobinQueue, object, false/*remove*/, LLMediaDataClient::Request::ANY).notNull()));
}
-
-void LLMediaDataClient::Request::reEnqueue() const
+bool LLMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object)
{
- // I sure hope this doesn't deref a bad pointer:
- mMDC->enqueue(this);
+ bool removedFromSortedQueue = LLMediaDataClient::findOrRemove(mSortedQueue, object, true/*remove*/, LLMediaDataClient::Request::ANY).notNull();
+ bool removedFromRoundRobinQueue = LLMediaDataClient::findOrRemove(mRoundRobinQueue, object, true/*remove*/, LLMediaDataClient::Request::ANY).notNull();
+ return removedFromSortedQueue || removedFromRoundRobinQueue;
}
-F32 LLMediaDataClient::Request::getRetryTimerDelay() const
+//static
+LLMediaDataClient::request_ptr_t LLMediaDataClient::findOrRemove(request_queue_t &queue, const LLMediaDataClientObject::ptr_t &obj, bool remove, LLMediaDataClient::Request::Type type)
{
- return (mMDC == NULL) ? LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY :
- mMDC->mRetryTimerDelay;
+ request_ptr_t result;
+ request_queue_t::iterator iter = queue.begin();
+ request_queue_t::iterator end = queue.end();
+ while (iter != end)
+ {
+ if (obj->getID() == (*iter)->getObject()->getID() && (type == LLMediaDataClient::Request::ANY || type == (*iter)->getType()))
+ {
+ result = *iter;
+ if (remove) queue.erase(iter);
+ break;
+ }
+ iter++;
+ }
+ return result;
}
-U32 LLMediaDataClient::Request::getMaxNumRetries() const
+void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload)
{
- return (mMDC == NULL) ? LLMediaDataClient::MAX_RETRIES : mMDC->mMaxNumRetries;
+ if (object.isNull() || ! object->hasMedia()) return;
+
+ // Push the object on the queue
+ enqueue(new Request(getCapabilityName(), payload, object, this));
}
-std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r)
+void LLMediaDataClient::enqueue(const Request *request)
{
- s << "<request>"
- << "<num>" << r.getNum() << "</num>"
- << "<type>" << r.getTypeAsString() << "</type>"
- << "<object_id>" << r.getObject()->getID() << "</object_id>"
- << "<num_retries>" << r.getRetryCount() << "</num_retries>"
- << "</request> ";
- return s;
+ if (request->isNew())
+ {
+ // Add to sorted queue
+ if (LLMediaDataClient::findOrRemove(mSortedQueue, request->getObject(), true/*remove*/, request->getType()).notNull())
+ {
+ LL_DEBUGS("LLMediaDataClient") << "REMOVING OLD request for " << *request << " ALREADY THERE!" << LL_ENDL;
+ }
+
+ LL_DEBUGS("LLMediaDataClient") << "Queuing SORTED request for " << *request << LL_ENDL;
+
+ // Sadly, we have to const-cast because items put into the queue are not const
+ mSortedQueue.push_back(const_cast<LLMediaDataClient::Request*>(request));
+
+ LL_DEBUGS("LLMediaDataClient") << "SORTED queue:" << mSortedQueue << LL_ENDL;
+ }
+ else {
+ if (mRoundRobinQueue.size() > mMaxRoundRobinQueueSize)
+ {
+ LL_INFOS_ONCE("LLMediaDataClient") << "RR QUEUE MAXED OUT!!!" << LL_ENDL;
+ LL_DEBUGS("LLMediaDataClient") << "Not queuing " << *request << LL_ENDL;
+ return;
+ }
+
+ // ROUND ROBIN: if it is there, and it is a GET request, leave it. If not, put at front!
+ request_ptr_t existing_request;
+ if (request->getType() == Request::GET)
+ {
+ existing_request = LLMediaDataClient::findOrRemove(mRoundRobinQueue, request->getObject(), false/*remove*/, request->getType());
+ }
+ if (existing_request.isNull())
+ {
+ LL_DEBUGS("LLMediaDataClient") << "Queuing RR request for " << *request << LL_ENDL;
+ // Push the request on the pending queue
+ // Sadly, we have to const-cast because items put into the queue are not const
+ mRoundRobinQueue.push_front(const_cast<LLMediaDataClient::Request*>(request));
+
+ LL_DEBUGS("LLMediaDataClient") << "RR queue:" << mRoundRobinQueue << LL_ENDL;
+ }
+ else
+ {
+ LL_DEBUGS("LLMediaDataClient") << "ALREADY THERE: NOT Queuing request for " << *request << LL_ENDL;
+
+ existing_request->markSent(false);
+ }
+ }
+ // Start the timer if not already running
+ startQueueTimer();
}
-
-//////////////////////////////////////////////////////////////////////////////////////
-//
-// LLMediaDataClient::Responder::RetryTimer
-//
-//////////////////////////////////////////////////////////////////////////////////////
-
-LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr)
- : LLEventTimer(time), mResponder(mdr)
+void LLMediaDataClient::startQueueTimer()
{
+ if (! mQueueTimerIsRunning)
+ {
+ LL_DEBUGS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL;
+ // LLEventTimer automagically takes care of the lifetime of this object
+ new QueueTimer(mQueueTimerDelay, this);
+ }
+ else {
+ LL_DEBUGS("LLMediaDataClient") << "not starting queue timer (it's already running, right???)" << LL_ENDL;
+ }
}
-// virtual
-LLMediaDataClient::Responder::RetryTimer::~RetryTimer()
+void LLMediaDataClient::stopQueueTimer()
{
- LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL;
-
- // XXX This is weird: Instead of doing the work in tick() (which re-schedules
- // a timer, which might be risky), do it here, in the destructor. Yes, it is very odd.
- // Instead of retrying, we just put the request back onto the queue
- LL_INFOS("LLMediaDataClient") << "RetryTimer fired for: " << *(mResponder->getRequest()) << "retrying" << LL_ENDL;
- mResponder->getRequest()->reEnqueue();
-
- // Release the ref to the responder.
- mResponder = NULL;
+ mQueueTimerIsRunning = false;
}
-// virtual
-BOOL LLMediaDataClient::Responder::RetryTimer::tick()
+bool LLMediaDataClient::processQueueTimer()
{
- // Don't fire again
- return TRUE;
+ sortQueue();
+
+ if(!isEmpty())
+ {
+ LL_INFOS("LLMediaDataClient") << "QueueTimer::tick() started, SORTED queue size is: " << mSortedQueue.size()
+ << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL;
+ LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, SORTED queue is: " << mSortedQueue << LL_ENDL;
+ LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, RR queue is: " << mRoundRobinQueue << LL_ENDL;
+ }
+
+ serviceQueue();
+
+ LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, SORTED queue size is: " << mSortedQueue.size()
+ << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL;
+ LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, SORTED queue is: " << mSortedQueue << LL_ENDL;
+ LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, RR queue is: " << mRoundRobinQueue << LL_ENDL;
+
+ return isEmpty();
}
-
-//////////////////////////////////////////////////////////////////////////////////////
-//
-// LLMediaDataClient::Responder
-//
-//////////////////////////////////////////////////////////////////////////////////////
-
-LLMediaDataClient::Responder::Responder(const request_ptr_t &request)
- : mRequest(request)
+void LLMediaDataClient::sortQueue()
{
+ if(!mSortedQueue.empty())
+ {
+ // Score all items first
+ request_queue_t::iterator iter = mSortedQueue.begin();
+ request_queue_t::iterator end = mSortedQueue.end();
+ while (iter != end)
+ {
+ (*iter)->updateScore();
+ iter++;
+ }
+
+ // Re-sort the list...
+ // NOTE: should this be a stable_sort? If so we need to change to using a vector.
+ mSortedQueue.sort(LLMediaDataClient::compareRequests);
+
+ // ...then cull items over the max
+ U32 size = mSortedQueue.size();
+ if (size > mMaxSortedQueueSize)
+ {
+ U32 num_to_cull = (size - mMaxSortedQueueSize);
+ LL_INFOS("LLMediaDataClient") << "sorted queue MAXED OUT! Culling "
+ << num_to_cull << " items" << LL_ENDL;
+ while (num_to_cull-- > 0)
+ {
+ mSortedQueue.pop_back();
+ }
+ }
+ }
}
-LLMediaDataClient::Responder::~Responder()
+// static
+bool LLMediaDataClient::compareRequests(const request_ptr_t &o1, const request_ptr_t &o2)
{
- LL_DEBUGS("LLMediaDataClient") << "~Responder" << *(getRequest()) << LL_ENDL;
- mRequest = NULL;
+ if (o2.isNull()) return true;
+ if (o1.isNull()) return false;
+ return ( o1->getScore() > o2->getScore() );
}
-/*virtual*/
-void LLMediaDataClient::Responder::error(U32 status, const std::string& reason)
-{
- if (status == HTTP_SERVICE_UNAVAILABLE)
+void LLMediaDataClient::serviceQueue()
+{
+ request_queue_t *queue_p = getCurrentQueue();
+
+ // quick retry loop for cases where we shouldn't wait for the next timer tick
+ while(true)
{
- F32 retry_timeout = mRequest->getRetryTimerDelay();
-
- mRequest->incRetryCount();
+ if (queue_p->empty())
+ {
+ LL_DEBUGS("LLMediaDataClient") << "queue empty: " << (*queue_p) << LL_ENDL;
+ break;
+ }
- if (mRequest->getRetryCount() < mRequest->getMaxNumRetries())
+ // Peel one off of the items from the queue, and execute request
+ request_ptr_t request = queue_p->front();
+ llassert(!request.isNull());
+ const LLMediaDataClientObject *object = (request.isNull()) ? NULL : request->getObject();
+ llassert(NULL != object);
+
+ // Check for conditions that would make us just pop and rapidly loop through
+ // the queue.
+ if(request.isNull() ||
+ request->isMarkedSent() ||
+ NULL == object ||
+ object->isDead() ||
+ !object->hasMedia())
{
- LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL;
+ if (request.isNull())
+ {
+ LL_INFOS("LLMediaDataClient") << "Skipping NULL request" << LL_ENDL;
+ }
+ else {
+ LL_INFOS("LLMediaDataClient") << "Skipping : " << *request << " "
+ << ((request->isMarkedSent()) ? " request is marked sent" :
+ ((NULL == object) ? " object is NULL " :
+ ((object->isDead()) ? "object is dead" :
+ ((!object->hasMedia()) ? "object has no media!" : "BADNESS!")))) << LL_ENDL;
+ }
+ queue_p->pop_front();
+ continue; // jump back to the start of the quick retry loop
+ }
+
+ // Next, ask if this is "interesting enough" to fetch. If not, just stop
+ // and wait for the next timer go-round. Only do this for the sorted
+ // queue.
+ if (mCurrentQueueIsTheSortedQueue && !object->isInterestingEnough())
+ {
+ LL_DEBUGS("LLMediaDataClient") << "Not fetching " << *request << ": not interesting enough" << LL_ENDL;
+ break;
+ }
+
+ // Finally, try to send the HTTP message to the cap url
+ std::string url = request->getCapability();
+ bool maybe_retry = false;
+ if (!url.empty())
+ {
+ const LLSD &sd_payload = request->getPayload();
+ LL_INFOS("LLMediaDataClient") << "Sending request for " << *request << LL_ENDL;
+
+ // Call the subclass for creating the responder
+ LLHTTPClient::post(url, sd_payload, createResponder(request));
+ }
+ else {
+ LL_INFOS("LLMediaDataClient") << "NOT Sending request for " << *request << ": empty cap url!" << LL_ENDL;
+ maybe_retry = true;
+ }
- // Start timer (instances are automagically tracked by
- // InstanceTracker<> and LLEventTimer)
- new RetryTimer(F32(retry_timeout/*secs*/), this);
+ bool exceeded_retries = request->getRetryCount() > mMaxNumRetries;
+ if (maybe_retry && ! exceeded_retries) // Try N times before giving up
+ {
+ // We got an empty cap, but in that case we will retry again next
+ // timer fire.
+ request->incRetryCount();
}
else {
- LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retry count " <<
- mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL;
+ if (exceeded_retries)
+ {
+ LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for "
+ << mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL;
+ // XXX Should we bring up a warning dialog??
+ }
+
+ queue_p->pop_front();
+
+ if (! mCurrentQueueIsTheSortedQueue) {
+ // Round robin
+ request->markSent(true);
+ mRoundRobinQueue.push_back(request);
+ }
}
+
+ // end of quick loop -- any cases where we want to loop will use 'continue' to jump back to the start.
+ break;
}
- else {
- std::string msg = boost::lexical_cast<std::string>(status) + ": " + reason;
- LL_WARNS("LLMediaDataClient") << *mRequest << " http error(" << msg << ")" << LL_ENDL;
- }
+
+ swapCurrentQueue();
}
-
-/*virtual*/
-void LLMediaDataClient::Responder::result(const LLSD& content)
+void LLMediaDataClient::swapCurrentQueue()
{
- LL_INFOS("LLMediaDataClient") << *mRequest << "result : " << ll_print_sd(content) << LL_ENDL;
+ // Swap
+ mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue;
+ // If its empty, swap back
+ if (getCurrentQueue()->empty())
+ {
+ mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue;
+ }
}
-
-//////////////////////////////////////////////////////////////////////////////////////
-//
-// LLMediaDataClient::Comparator
-//
-//////////////////////////////////////////////////////////////////////////////////////
-
-// static
-bool LLMediaDataClient::compareRequests(const request_ptr_t &o1, const request_ptr_t &o2)
+LLMediaDataClient::request_queue_t *LLMediaDataClient::getCurrentQueue()
{
- if (o2.isNull()) return true;
- if (o1.isNull()) return false;
-
- // The score is intended to be a measure of how close an object is or
- // how much screen real estate (interest) it takes up
- // Further away = lower score.
- // Lesser interest = lower score
- // For instance, here are some cases:
- // 1: Two items with no impl, closest one wins
- // 2: Two items with an impl: interest should rule, but distance is
- // still taken into account (i.e. something really close might take
- // precedence over a large item far away)
- // 3: One item with an impl, another without: item with impl wins
- // (XXX is that what we want?)
- // Calculate the scores for each.
- F64 o1_score = getObjectScore(o1->getObject());
- F64 o2_score = getObjectScore(o2->getObject());
- return ( o1_score > o2_score );
-}
-
-// static
-F64 LLMediaDataClient::getObjectScore(const LLMediaDataClientObject::ptr_t &obj)
-{
- // *TODO: make this less expensive?
- F64 dist = obj->getDistanceFromAvatar() + 0.1; // avoids div by 0
- // square the distance so that they are in the same "unit magnitude" as
- // the interest (which is an area)
- dist *= dist;
- F64 interest = obj->getTotalMediaInterest() + 1.0;
-
- return interest/dist;
+ return (mCurrentQueueIsTheSortedQueue) ? &mSortedQueue : &mRoundRobinQueue;
}
-//////////////////////////////////////////////////////////////////////////////////////
-//
-// LLMediaDataClient::PriorityQueue
-// Queue of LLMediaDataClientObject smart pointers to request media for.
-//
-//////////////////////////////////////////////////////////////////////////////////////
-
// dump the queue
std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q)
{
@@ -318,22 +402,6 @@ std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue
return s;
}
-// find the given object in the queue.
-bool LLMediaDataClient::find(const LLMediaDataClientObject::ptr_t &obj) const
-{
- request_queue_t::const_iterator iter = pRequestQueue->begin();
- request_queue_t::const_iterator end = pRequestQueue->end();
- while (iter != end)
- {
- if (obj->getID() == (*iter)->getObject()->getID())
- {
- return true;
- }
- iter++;
- }
- return false;
-}
-
//////////////////////////////////////////////////////////////////////////////////////
//
// LLMediaDataClient::QueueTimer
@@ -342,7 +410,7 @@ bool LLMediaDataClient::find(const LLMediaDataClientObject::ptr_t &obj) const
//////////////////////////////////////////////////////////////////////////////////////
LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc)
- : LLEventTimer(time), mMDC(mdc)
+: LLEventTimer(time), mMDC(mdc)
{
mMDC->setIsRunning(true);
}
@@ -357,177 +425,224 @@ LLMediaDataClient::QueueTimer::~QueueTimer()
// virtual
BOOL LLMediaDataClient::QueueTimer::tick()
{
- if (NULL == mMDC->pRequestQueue)
- {
- // Shutting down? stop.
- LL_DEBUGS("LLMediaDataClient") << "queue gone" << LL_ENDL;
- return TRUE;
- }
-
- request_queue_t &queue = *(mMDC->pRequestQueue);
+ if (mMDC.isNull()) return TRUE;
+ return mMDC->processQueueTimer();
+}
- if(!queue.empty())
- {
- LL_INFOS("LLMediaDataClient") << "QueueTimer::tick() started, queue is: " << queue << LL_ENDL;
- // Re-sort the list every time...
- // XXX Is this really what we want?
- queue.sort(LLMediaDataClient::compareRequests);
- }
+//////////////////////////////////////////////////////////////////////////////////////
+//
+// LLMediaDataClient::Responder::RetryTimer
+//
+//////////////////////////////////////////////////////////////////////////////////////
+
+LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr)
+: LLEventTimer(time), mResponder(mdr)
+{
+}
+
+// virtual
+LLMediaDataClient::Responder::RetryTimer::~RetryTimer()
+{
+ LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL;
- // quick retry loop for cases where we shouldn't wait for the next timer tick
- while(true)
- {
- if (queue.empty())
- {
- LL_DEBUGS("LLMediaDataClient") << "queue empty: " << queue << LL_ENDL;
- return TRUE;
- }
+ // XXX This is weird: Instead of doing the work in tick() (which re-schedules
+ // a timer, which might be risky), do it here, in the destructor. Yes, it is very odd.
+ // Instead of retrying, we just put the request back onto the queue
+ LL_INFOS("LLMediaDataClient") << "RetryTimer fired for: " << *(mResponder->getRequest()) << " retrying" << LL_ENDL;
+ mResponder->getRequest()->reEnqueue();
- // Peel one off of the items from the queue, and execute request
- request_ptr_t request = queue.front();
- llassert(!request.isNull());
- const LLMediaDataClientObject *object = (request.isNull()) ? NULL : request->getObject();
- bool performed_request = false;
- bool error = false;
- llassert(NULL != object);
+ // Release the ref to the responder.
+ mResponder = NULL;
+}
- if(object->isDead())
- {
- // This object has been marked dead. Pop it and move on to the next item in the queue immediately.
- LL_INFOS("LLMediaDataClient") << "Skipping " << *request << ": object is dead!" << LL_ENDL;
- queue.pop_front();
- continue; // jump back to the start of the quick retry loop
- }
+// virtual
+BOOL LLMediaDataClient::Responder::RetryTimer::tick()
+{
+ // Don't fire again
+ return TRUE;
+}
- if (NULL != object && object->hasMedia())
- {
- std::string url = request->getCapability();
- if (!url.empty())
- {
- const LLSD &sd_payload = request->getPayload();
- LL_INFOS("LLMediaDataClient") << "Sending request for " << *request << LL_ENDL;
- // Call the subclass for creating the responder
- LLHTTPClient::post(url, sd_payload, mMDC->createResponder(request));
- performed_request = true;
- }
- else {
- LL_INFOS("LLMediaDataClient") << "NOT Sending request for " << *request << ": empty cap url!" << LL_ENDL;
- }
- }
- else {
- if (request.isNull())
- {
- LL_WARNS("LLMediaDataClient") << "Not Sending request: NULL request!" << LL_ENDL;
- }
- else if (NULL == object)
- {
- LL_WARNS("LLMediaDataClient") << "Not Sending request for " << *request << " NULL object!" << LL_ENDL;
- }
- else if (!object->hasMedia())
- {
- LL_WARNS("LLMediaDataClient") << "Not Sending request for " << *request << " hasMedia() is false!" << LL_ENDL;
- }
- error = true;
- }
- bool exceeded_retries = request->getRetryCount() > mMDC->mMaxNumRetries;
- if (performed_request || exceeded_retries || error) // Try N times before giving up
- {
- if (exceeded_retries)
- {
- LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for "
- << mMDC->mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL;
- // XXX Should we bring up a warning dialog??
- }
- queue.pop_front();
- }
- else {
- request->incRetryCount();
- }
-
- // end of quick retry loop -- any cases where we want to loop will use 'continue' to jump back to the start.
- break;
- }
+//////////////////////////////////////////////////////////////////////////////////////
+//
+// LLMediaDataClient::Request
+//
+//////////////////////////////////////////////////////////////////////////////////////
+/*static*/U32 LLMediaDataClient::Request::sNum = 0;
- LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, queue is now: " << (*(mMDC->pRequestQueue)) << LL_ENDL;
+LLMediaDataClient::Request::Request(const char *cap_name,
+ const LLSD& sd_payload,
+ LLMediaDataClientObject *obj,
+ LLMediaDataClient *mdc)
+: mCapName(cap_name),
+ mPayload(sd_payload),
+ mObject(obj),
+ mNum(++sNum),
+ mRetryCount(0),
+ mMDC(mdc),
+ mMarkedSent(false),
+ mScore((F64)0.0)
+{
+}
- return queue.empty();
+LLMediaDataClient::Request::~Request()
+{
+ LL_DEBUGS("LLMediaDataClient") << "~Request" << (*this) << LL_ENDL;
+ mMDC = NULL;
+ mObject = NULL;
}
-
-void LLMediaDataClient::startQueueTimer()
+
+
+std::string LLMediaDataClient::Request::getCapability() const
{
- if (! mQueueTimerIsRunning)
+ return getObject()->getCapabilityUrl(getCapName());
+}
+
+// Helper function to get the "type" of request, which just pokes around to
+// discover it.
+LLMediaDataClient::Request::Type LLMediaDataClient::Request::getType() const
+{
+ if (0 == strcmp(mCapName, "ObjectMediaNavigate"))
{
- LL_INFOS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL;
- // LLEventTimer automagically takes care of the lifetime of this object
- new QueueTimer(mQueueTimerDelay, this);
+ return NAVIGATE;
}
- else {
- LL_DEBUGS("LLMediaDataClient") << "not starting queue timer (it's already running, right???)" << LL_ENDL;
+ else if (0 == strcmp(mCapName, "ObjectMedia"))
+ {
+ const std::string &verb = mPayload["verb"];
+ if (verb == "GET")
+ {
+ return GET;
+ }
+ else if (verb == "UPDATE")
+ {
+ return UPDATE;
+ }
}
+ llassert(false);
+ return GET;
}
-
-void LLMediaDataClient::stopQueueTimer()
+
+const char *LLMediaDataClient::Request::getTypeAsString() const
{
- mQueueTimerIsRunning = false;
+ Type t = getType();
+ switch (t)
+ {
+ case GET:
+ return "GET";
+ break;
+ case UPDATE:
+ return "UPDATE";
+ break;
+ case NAVIGATE:
+ return "NAVIGATE";
+ break;
+ case ANY:
+ return "ANY";
+ break;
+ }
+ return "";
}
-
-void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload)
+
+
+void LLMediaDataClient::Request::reEnqueue() const
{
- if (object.isNull() || ! object->hasMedia()) return;
+ // I sure hope this doesn't deref a bad pointer:
+ mMDC->enqueue(this);
+}
- // Push the object on the priority queue
- enqueue(new Request(getCapabilityName(), payload, object, this));
+F32 LLMediaDataClient::Request::getRetryTimerDelay() const
+{
+ return (mMDC == NULL) ? LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY :
+ mMDC->mRetryTimerDelay;
}
-void LLMediaDataClient::enqueue(const Request *request)
+U32 LLMediaDataClient::Request::getMaxNumRetries() const
{
- LL_INFOS("LLMediaDataClient") << "Queuing request for " << *request << LL_ENDL;
- // Push the request on the priority queue
- // Sadly, we have to const-cast because items put into the queue are not const
- pRequestQueue->push_back(const_cast<LLMediaDataClient::Request*>(request));
- LL_DEBUGS("LLMediaDataClient") << "Queue:" << (*pRequestQueue) << LL_ENDL;
- // Start the timer if not already running
- startQueueTimer();
+ return (mMDC == NULL) ? LLMediaDataClient::MAX_RETRIES : mMDC->mMaxNumRetries;
+}
+
+void LLMediaDataClient::Request::markSent(bool flag)
+{
+ if (mMarkedSent != flag)
+ {
+ mMarkedSent = flag;
+ if (!mMarkedSent)
+ {
+ mNum = ++sNum;
+ }
+ }
+}
+
+void LLMediaDataClient::Request::updateScore()
+{
+ F64 tmp = mObject->getMediaInterest();
+ if (tmp != mScore)
+ {
+ LL_DEBUGS("LLMediaDataClient") << "Score for " << mObject->getID() << " changed from " << mScore << " to " << tmp << LL_ENDL;
+ mScore = tmp;
+ }
+}
+
+std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r)
+{
+ s << "request: num=" << r.getNum()
+ << " type=" << r.getTypeAsString()
+ << " ID=" << r.getObject()->getID()
+ << " #retries=" << r.getRetryCount();
+ return s;
}
+
//////////////////////////////////////////////////////////////////////////////////////
//
-// LLMediaDataClient
+// LLMediaDataClient::Responder
//
//////////////////////////////////////////////////////////////////////////////////////
-LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay,
- F32 retry_timer_delay,
- U32 max_retries)
- : mQueueTimerDelay(queue_timer_delay),
- mRetryTimerDelay(retry_timer_delay),
- mMaxNumRetries(max_retries),
- mQueueTimerIsRunning(false)
+LLMediaDataClient::Responder::Responder(const request_ptr_t &request)
+: mRequest(request)
{
- pRequestQueue = new request_queue_t();
}
-LLMediaDataClient::~LLMediaDataClient()
+LLMediaDataClient::Responder::~Responder()
{
- stopQueueTimer();
-
- // This should clear the queue, and hopefully call all the destructors.
- LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " <<
- (pRequestQueue->empty() ? "<empty> " : "<not empty> ") << (*pRequestQueue) << LL_ENDL;
- delete pRequestQueue;
- pRequestQueue = NULL;
+ LL_DEBUGS("LLMediaDataClient") << "~Responder" << *(getRequest()) << LL_ENDL;
+ mRequest = NULL;
}
-bool LLMediaDataClient::isEmpty() const
+/*virtual*/
+void LLMediaDataClient::Responder::error(U32 status, const std::string& reason)
{
- return (NULL == pRequestQueue) ? true : pRequestQueue->empty();
+ if (status == HTTP_SERVICE_UNAVAILABLE)
+ {
+ F32 retry_timeout = mRequest->getRetryTimerDelay();
+
+ mRequest->incRetryCount();
+
+ if (mRequest->getRetryCount() < mRequest->getMaxNumRetries())
+ {
+ LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL;
+
+ // Start timer (instances are automagically tracked by
+ // InstanceTracker<> and LLEventTimer)
+ new RetryTimer(F32(retry_timeout/*secs*/), this);
+ }
+ else {
+ LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retry count " <<
+ mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL;
+ }
+ }
+ else {
+ std::string msg = boost::lexical_cast<std::string>(status) + ": " + reason;
+ LL_WARNS("LLMediaDataClient") << *mRequest << " http error(" << msg << ")" << LL_ENDL;
+ }
}
-bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object) const
+/*virtual*/
+void LLMediaDataClient::Responder::result(const LLSD& content)
{
- return (NULL == pRequestQueue) ? false : find(object);
+ LL_DEBUGS("LLMediaDataClient") << *mRequest << " result : " << ll_print_sd(content) << LL_ENDL;
}
//////////////////////////////////////////////////////////////////////////////////////
@@ -569,7 +684,7 @@ void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object)
}
sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data;
- LL_INFOS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_print_sd(sd_payload) << LL_ENDL;
+ LL_DEBUGS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_print_sd(sd_payload) << LL_ENDL;
request(object, sd_payload);
}
@@ -581,7 +696,7 @@ void LLObjectMediaDataClient::Responder::result(const LLSD& content)
llassert(type == LLMediaDataClient::Request::GET || type == LLMediaDataClient::Request::UPDATE)
if (type == LLMediaDataClient::Request::GET)
{
- LL_INFOS("LLMediaDataClient") << *(getRequest()) << "GET returned: " << ll_print_sd(content) << LL_ENDL;
+ LL_DEBUGS("LLMediaDataClient") << *(getRequest()) << " GET returned: " << ll_print_sd(content) << LL_ENDL;
// Look for an error
if (content.has("error"))
@@ -598,12 +713,13 @@ void LLObjectMediaDataClient::Responder::result(const LLSD& content)
if (object_id != getRequest()->getObject()->getID())
{
// NOT good, wrong object id!!
- LL_WARNS("LLMediaDataClient") << *(getRequest()) << "DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL;
+ LL_WARNS("LLMediaDataClient") << *(getRequest()) << " DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL;
return;
}
// Otherwise, update with object media data
- getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY]);
+ getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY],
+ content[LLTextureEntry::MEDIA_VERSION_KEY]);
}
}
else if (type == LLMediaDataClient::Request::UPDATE)
diff --git a/indra/newview/llmediadataclient.h b/indra/newview/llmediadataclient.h
index 812e9cbdec..75d32e707b 100755
--- a/indra/newview/llmediadataclient.h
+++ b/indra/newview/llmediadataclient.h
@@ -55,15 +55,19 @@ public:
// Does this object have media?
virtual bool hasMedia() const = 0;
// Update the object's media data to the given array
- virtual void updateObjectMediaData(LLSD const &media_data_array) = 0;
- // Return the distance from the object to the avatar
- virtual F64 getDistanceFromAvatar() const = 0;
+ virtual void updateObjectMediaData(LLSD const &media_data_array, const std::string &version_string) = 0;
// Return the total "interest" of the media (on-screen area)
- virtual F64 getTotalMediaInterest() const = 0;
+ virtual F64 getMediaInterest() const = 0;
// Return the given cap url
virtual std::string getCapabilityUrl(const std::string &name) const = 0;
// Return whether the object has been marked dead
virtual bool isDead() const = 0;
+ // Returns a media version number for the object
+ virtual U32 getMediaVersion() const = 0;
+ // Returns whether the object is "interesting enough" to fetch
+ virtual bool isInterestingEnough() const = 0;
+ // Returns whether we've seen this object yet or not
+ virtual bool isNew() const = 0;
// smart pointer
typedef LLPointer<LLMediaDataClientObject> ptr_t;
@@ -79,11 +83,15 @@ public:
const static F32 QUEUE_TIMER_DELAY;// = 1.0; // seconds(s)
const static F32 UNAVAILABLE_RETRY_TIMER_DELAY;// = 5.0; // secs
const static U32 MAX_RETRIES;// = 4;
+ const static U32 MAX_SORTED_QUEUE_SIZE;// = 10000;
+ const static U32 MAX_ROUND_ROBIN_QUEUE_SIZE;// = 10000;
// Constructor
LLMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
- U32 max_retries = MAX_RETRIES);
+ 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);
// Make the request
void request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload);
@@ -94,7 +102,13 @@ public:
bool isEmpty() const;
// Returns true iff the given object is in the queue
- bool isInQueue(const LLMediaDataClientObject::ptr_t &object) const;
+ 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);
+
+ // Called only by the Queue timer and tests (potentially)
+ bool processQueueTimer();
protected:
// Destructor
@@ -107,11 +121,12 @@ protected:
enum Type {
GET,
UPDATE,
- NAVIGATE
+ NAVIGATE,
+ ANY
};
- Request(const std::string &cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc);
- const std::string &getCapName() const { return mCapName; }
+ Request(const char *cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc);
+ const char *getCapName() const { return mCapName; }
const LLSD &getPayload() const { return mPayload; }
LLMediaDataClientObject *getObject() const { return mObject; }
@@ -132,6 +147,12 @@ protected:
F32 getRetryTimerDelay() const;
U32 getMaxNumRetries() const;
+ bool isNew() const { return mObject.notNull() ? mObject->isNew() : false; }
+ void markSent(bool flag);
+ bool isMarkedSent() const { return mMarkedSent; }
+ void updateScore();
+ F64 getScore() const { return mScore; }
+
public:
friend std::ostream& operator<<(std::ostream &s, const Request &q);
@@ -139,14 +160,16 @@ protected:
virtual ~Request(); // use unref();
private:
- std::string mCapName;
+ const char *mCapName;
LLSD mPayload;
LLMediaDataClientObject::ptr_t mObject;
// Simple tracking
- const U32 mNum;
+ U32 mNum;
static U32 sNum;
U32 mRetryCount;
-
+ F64 mScore;
+ bool mMarkedSent;
+
// Back pointer to the MDC...not a ref!
LLMediaDataClient *mMDC;
};
@@ -184,18 +207,23 @@ protected:
};
protected:
-
- void enqueue(const Request*);
-
+
// Subclasses must override this factory method to return a new responder
virtual Responder *createResponder(const request_ptr_t &request) const = 0;
// Subclasses must override to return a cap name
virtual const char *getCapabilityName() const = 0;
-private:
+ virtual void sortQueue();
+ virtual void serviceQueue();
+private:
typedef std::list<request_ptr_t> request_queue_t;
+
+ void enqueue(const Request*);
+
+ // 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);
@@ -215,34 +243,40 @@ private:
// back-pointer
LLPointer<LLMediaDataClient> mMDC;
};
-
- // Return whether the given object is in the queue
- bool find(const LLMediaDataClientObject::ptr_t &obj) const;
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 *pRequestQueue;
+ request_queue_t mSortedQueue;
+ request_queue_t mRoundRobinQueue;
+ bool mCurrentQueueIsTheSortedQueue;
};
-// MediaDataResponder specific for the ObjectMedia cap
+// MediaDataClient specific for the ObjectMedia cap
class LLObjectMediaDataClient : public LLMediaDataClient
{
public:
LLObjectMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
- U32 max_retries = MAX_RETRIES)
+ 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)
{}
- ~LLObjectMediaDataClient() {}
+ virtual ~LLObjectMediaDataClient() {}
void fetchMedia(LLMediaDataClientObject *object);
void updateMedia(LLMediaDataClientObject *object);
@@ -264,7 +298,7 @@ protected:
};
-// MediaDataResponder specific for the ObjectMediaNavigate cap
+// MediaDataClient specific for the ObjectMediaNavigate cap
class LLObjectMediaNavigateClient : public LLMediaDataClient
{
public:
@@ -273,10 +307,12 @@ public:
LLObjectMediaNavigateClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
- U32 max_retries = MAX_RETRIES)
+ 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)
{}
- ~LLObjectMediaNavigateClient() {}
+ virtual ~LLObjectMediaNavigateClient() {}
void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url);
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index cf61994fea..b92d024ac9 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -92,7 +92,7 @@ static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes");
class LLMediaDataClientObjectImpl : public LLMediaDataClientObject
{
public:
- LLMediaDataClientObjectImpl(LLVOVolume *obj) : mObject(obj) {}
+ LLMediaDataClientObjectImpl(LLVOVolume *obj, bool isNew) : mObject(obj), mNew(isNew) {}
LLMediaDataClientObjectImpl() { mObject = NULL; }
virtual U8 getMediaDataCount() const
@@ -128,14 +128,19 @@ public:
virtual bool hasMedia() const
{ return mObject->hasMedia(); }
- virtual void updateObjectMediaData(LLSD const &data)
- { mObject->updateObjectMediaData(data); }
-
- virtual F64 getDistanceFromAvatar() const
- { return mObject->getRenderPosition().length(); }
+ virtual void updateObjectMediaData(LLSD const &data, const std::string &version_string)
+ { mObject->updateObjectMediaData(data, version_string); }
- virtual F64 getTotalMediaInterest() const
- { return mObject->getTotalMediaInterest(); }
+ virtual F64 getMediaInterest() const
+ {
+ F64 tmp = mObject->getTotalMediaInterest();
+ return (tmp < 0.0) ? mObject->getPixelArea() : tmp;
+ }
+ virtual bool isInterestingEnough() const
+ {
+ // TODO: use performance manager to control this
+ return true;
+ }
virtual std::string getCapabilityUrl(const std::string &name) const
{ return mObject->getRegion()->getCapability(name); }
@@ -143,8 +148,15 @@ public:
virtual bool isDead() const
{ return mObject->isDead(); }
+ virtual U32 getMediaVersion() const
+ { return LLTextureEntry::getVersionFromMediaVersionString(mObject->getMediaURL()); }
+
+ virtual bool isNew() const
+ { return mNew; }
+
private:
LLPointer<LLVOVolume> mObject;
+ bool mNew;
};
@@ -165,6 +177,7 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re
mSpotLightPriority = 0.f;
mMediaImplList.resize(getNumTEs());
+ mLastFetchedMediaVersion = -1;
}
LLVOVolume::~LLVOVolume()
@@ -190,7 +203,9 @@ void LLVOVolume::markDead()
{
if (!mDead)
{
- // TODO: tell LLMediaDataClient to remove this object from its queue
+ LLMediaDataClientObject::ptr_t obj = new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this), false);
+ sObjectMediaClient->removeFromQueue(obj);
+ sObjectMediaNavigateClient->removeFromQueue(obj);
// Detach all media impls from this object
for(U32 i = 0 ; i < mMediaImplList.size() ; i++)
@@ -210,8 +225,12 @@ void LLVOVolume::initClass()
const F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay");
const F32 retry_timer_delay = gSavedSettings.getF32("PrimMediaRetryTimerDelay");
const U32 max_retries = gSavedSettings.getU32("PrimMediaMaxRetries");
- sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries);
- sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay, max_retries);
+ const U32 max_sorted_queue_size = gSavedSettings.getU32("PrimMediaMaxSortedQueueSize");
+ const U32 max_round_robin_queue_size = gSavedSettings.getU32("PrimMediaMaxRoundRobinQueueSize");
+ sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries,
+ max_sorted_queue_size, max_round_robin_queue_size);
+ sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay,
+ max_retries, max_sorted_queue_size, max_round_robin_queue_size);
}
// static
@@ -406,7 +425,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
// If the media changed at all, request new media data
LL_DEBUGS("MediaOnAPrim") << "Media update: " << getID() << ": retval=" << retval << " Media URL: " <<
((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL;
- requestMediaDataUpdate();
+ requestMediaDataUpdate(retval & MEDIA_FLAGS_CHANGED);
}
else {
LL_INFOS("MediaOnAPrim") << "Ignoring media update for: " << getID() << " Media URL: " <<
@@ -1698,16 +1717,16 @@ LLVector3 LLVOVolume::getApproximateFaceNormal(U8 face_id)
return result;
}
-void LLVOVolume::requestMediaDataUpdate()
+void LLVOVolume::requestMediaDataUpdate(bool isNew)
{
- sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this));
+ sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this, isNew));
}
bool LLVOVolume::isMediaDataBeingFetched() const
{
// I know what I'm doing by const_casting this away: this is just
// a wrapper class that is only going to do a lookup.
- return sObjectMediaClient->isInQueue(new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this)));
+ return sObjectMediaClient->isInQueue(new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this), false));
}
void LLVOVolume::cleanUpMediaImpls()
@@ -1725,18 +1744,25 @@ void LLVOVolume::cleanUpMediaImpls()
}
}
-void LLVOVolume::updateObjectMediaData(const LLSD &media_data_array)
+void LLVOVolume::updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version)
{
// media_data_array is an array of media entry maps
+ // media_version is the version string in the response.
+ U32 fetched_version = LLTextureEntry::getVersionFromMediaVersionString(media_version);
- //llinfos << "updating:" << this->getID() << " " << ll_pretty_print_sd(media_data_array) << llendl;
-
- LLSD::array_const_iterator iter = media_data_array.beginArray();
- LLSD::array_const_iterator end = media_data_array.endArray();
- U8 texture_index = 0;
- for (; iter != end; ++iter, ++texture_index)
+ // Only update it if it is newer!
+ if ( (S32)fetched_version > mLastFetchedMediaVersion)
{
- syncMediaData(texture_index, *iter, false/*merge*/, false/*ignore_agent*/);
+ mLastFetchedMediaVersion = fetched_version;
+ //llinfos << "updating:" << this->getID() << " " << ll_pretty_print_sd(media_data_array) << llendl;
+
+ LLSD::array_const_iterator iter = media_data_array.beginArray();
+ LLSD::array_const_iterator end = media_data_array.endArray();
+ U8 texture_index = 0;
+ for (; iter != end; ++iter, ++texture_index)
+ {
+ syncMediaData(texture_index, *iter, false/*merge*/, false/*ignore_agent*/);
+ }
}
}
@@ -1904,7 +1930,7 @@ void LLVOVolume::mediaNavigated(LLViewerMediaImpl *impl, LLPluginClassMedia* plu
llinfos << "broadcasting navigate with URI " << new_location << llendl;
- sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this), face_index, new_location);
+ sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this, false), face_index, new_location);
}
}
@@ -1968,7 +1994,7 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin,
void LLVOVolume::sendMediaDataUpdate()
{
- sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this));
+ sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this, false));
}
void LLVOVolume::removeMediaImpl(S32 texture_index)
@@ -2063,7 +2089,7 @@ viewer_media_t LLVOVolume::getMediaImpl(U8 face_id) const
F64 LLVOVolume::getTotalMediaInterest() const
{
- F64 interest = (F64)0.0;
+ F64 interest = (F64)-1.0; // means not interested;
int i = 0;
const int end = getNumTEs();
for ( ; i < end; ++i)
@@ -2071,6 +2097,7 @@ F64 LLVOVolume::getTotalMediaInterest() const
const viewer_media_t &impl = getMediaImpl(i);
if (!impl.isNull())
{
+ if (interest == (F64)-1.0) interest = (F64)0.0;
interest += impl->getInterest();
}
}
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index 06e214b41e..0655c13d5a 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -238,7 +238,7 @@ public:
// Update this object's media data with the given media data array
// (typically this is only called upon a response from a server request)
- void updateObjectMediaData(const LLSD &media_data_array);
+ void updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version);
// Bounce back media at the given index to its current URL (or home URL, if current URL is empty)
void mediaNavigateBounceBack(U8 texture_index);
@@ -270,13 +270,16 @@ public:
// Returns 'true' iff the media data for this object is in flight
bool isMediaDataBeingFetched() const;
+ // Returns the "last fetched" media version, or -1 if not fetched yet
+ S32 getLastFetchedMediaVersion() const { return mLastFetchedMediaVersion; }
+
protected:
S32 computeLODDetail(F32 distance, F32 radius);
BOOL calcLOD();
LLFace* addFace(S32 face_index);
void updateTEData();
- void requestMediaDataUpdate();
+ void requestMediaDataUpdate(bool isNew);
void cleanUpMediaImpls();
void addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) ;
void removeMediaImpl(S32 texture_index) ;
@@ -300,6 +303,7 @@ private:
LLPointer<LLViewerFetchedTexture> mSculptTexture;
LLPointer<LLViewerFetchedTexture> mLightTexture;
media_list_t mMediaImplList;
+ S32 mLastFetchedMediaVersion; // as fetched from the server, starts as -1
// statics
public:
diff --git a/indra/newview/tests/llmediadataclient_test.cpp b/indra/newview/tests/llmediadataclient_test.cpp
index 6ff2c9446e..33d413bd21 100644
--- a/indra/newview/tests/llmediadataclient_test.cpp
+++ b/indra/newview/tests/llmediadataclient_test.cpp
@@ -75,15 +75,13 @@
<string>baz</string> \
</array>"
-#define _DATA_URLS(ID,DIST,INT,URL1,URL2) " \
+#define _DATA_URLS(ID,INTEREST,NEW,URL1,URL2) " \
<llsd> \
<map> \
<key>uuid</key> \
<string>" ID "</string> \
- <key>distance</key> \
- <real>" DIST "</real> \
<key>interest</key> \
- <real>" INT "</real> \
+ <real>" INTEREST "</real> \
<key>cap_urls</key> \
<map> \
<key>ObjectMedia</key> \
@@ -93,21 +91,26 @@
</map> \
<key>media_data</key> \
" MEDIA_DATA " \
+ <key>is_dead</key> \
+ <boolean>false</boolean> \
+ <key>is_new</key> \
+ <boolean>" NEW "</boolean> \
</map> \
</llsd>"
-#define _DATA(ID,DIST,INT) _DATA_URLS(ID,DIST,INT,FAKE_OBJECT_MEDIA_CAP_URL,FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL)
+#define _DATA(ID,INTEREST,NEW) _DATA_URLS(ID,INTEREST,NEW,FAKE_OBJECT_MEDIA_CAP_URL,FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL)
-const char *DATA = _DATA(VALID_OBJECT_ID,"1.0","1.0");
+const char *DATA = _DATA(VALID_OBJECT_ID,"1.0","true");
#define STR(I) boost::lexical_cast<std::string>(I)
#define LOG_TEST(N) LL_DEBUGS("LLMediaDataClient") << "\n" << \
"================================================================================\n" << \
-"===================================== TEST " #N " ===================================\n" << \
+"==================================== TEST " #N " ===================================\n" << \
"================================================================================\n" << LL_ENDL;
LLSD *gPostRecords = NULL;
+F64 gMinimumInterestLevel = (F64)0.0;
// stubs:
void LLHTTPClient::post(
@@ -125,21 +128,20 @@ void LLHTTPClient::post(
gPostRecords->append(record);
// Magic URL that triggers a 503:
+ LLSD result;
+ result[LLTextureEntry::OBJECT_ID_KEY] = body[LLTextureEntry::OBJECT_ID_KEY];
if ( url == FAKE_OBJECT_MEDIA_CAP_URL_503 )
{
responder->error(HTTP_SERVICE_UNAVAILABLE, "fake reason");
+ return;
}
else if (url == FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR)
{
- LLSD result;
LLSD error;
error["code"] = LLObjectMediaNavigateClient::ERROR_PERMISSION_DENIED_CODE;
result["error"] = error;
- responder->result(result);
- }
- else {
- responder->result(LLSD());
- }
+ }
+ responder->result(result);
}
const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f;
@@ -152,13 +154,12 @@ public:
std::istringstream d(data);
LLSDSerialize::fromXML(mRep, d);
mNumBounceBacks = 0;
- mDead = false;
// std::cout << ll_pretty_print_sd(mRep) << std::endl;
// std::cout << "ID: " << getID() << std::endl;
}
LLMediaDataClientObjectTest(const LLSD &rep)
- : mRep(rep), mNumBounceBacks(0), mDead(false) {}
+ : mRep(rep), mNumBounceBacks(0) {}
~LLMediaDataClientObjectTest()
{ LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClientObjectTest" << LL_ENDL; }
@@ -169,43 +170,47 @@ public:
virtual LLUUID getID() const
{ return mRep["uuid"]; }
virtual void mediaNavigateBounceBack(U8 index)
- {
- mNumBounceBacks++;
- }
+ { mNumBounceBacks++; }
virtual bool hasMedia() const
{ return mRep.has("media_data"); }
- virtual void updateObjectMediaData(LLSD const &media_data_array)
- { mRep["media_data"] = media_data_array; }
+ virtual void updateObjectMediaData(LLSD const &media_data_array, const std::string &media_version)
+ { mRep["media_data"] = media_data_array; mRep["media_version"] = media_version; }
+
+ virtual F64 getMediaInterest() const
+ { return (LLSD::Real)mRep["interest"]; }
- virtual F64 getDistanceFromAvatar() const
- { return (LLSD::Real)mRep["distance"]; }
+ virtual bool isInterestingEnough() const
+ { return getMediaInterest() > gMinimumInterestLevel; }
- virtual F64 getTotalMediaInterest() const
- { return (LLSD::Real)mRep["interest"]; }
-
virtual std::string getCapabilityUrl(const std::string &name) const
{ return mRep["cap_urls"][name]; }
virtual bool isDead() const
- { return mDead; }
-
- void setDistanceFromAvatar(F64 val)
- { mRep["distance"] = val; }
+ { return mRep["is_dead"]; }
+
+ virtual U32 getMediaVersion() const
+ { return (LLSD::Integer)mRep["media_version"]; }
+
+ virtual bool isNew() const
+ { return mRep["is_new"]; }
- void setTotalMediaInterest(F64 val)
+ void setMediaInterest(F64 val)
{ mRep["interest"] = val; }
int getNumBounceBacks() const
{ return mNumBounceBacks; }
void markDead()
- { mDead = true; }
+ { mRep["is_dead"] = true; }
+
+ void markOld()
+ { mRep["is_new"] = false; }
+
private:
LLSD mRep;
int mNumBounceBacks;
- bool mDead;
};
// This special timer delay should ensure that the timer will fire on the very
@@ -224,10 +229,11 @@ namespace tut
{
mediadataclient() {
gPostRecords = &mLLSD;
+ gMinimumInterestLevel = (F64)0.0;
- //LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
- //LLError::setClassLevel("LLMediaDataClient", LLError::LEVEL_DEBUG);
- //LLError::setTagLevel("MediaOnAPrim", LLError::LEVEL_DEBUG);
+// LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
+// LLError::setClassLevel("LLMediaDataClient", LLError::LEVEL_DEBUG);
+// LLError::setTagLevel("MediaOnAPrim", LLError::LEVEL_DEBUG);
}
LLSD mLLSD;
};
@@ -378,11 +384,11 @@ namespace tut
LOG_TEST(4);
LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(
- _DATA(VALID_OBJECT_ID_1,"3.0","1.0"));
+ _DATA(VALID_OBJECT_ID_1,"1.0","true"));
LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(
- _DATA(VALID_OBJECT_ID_2,"1.0","1.0"));
+ _DATA(VALID_OBJECT_ID_2,"3.0","true"));
LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(
- _DATA(VALID_OBJECT_ID_3,"2.0","1.0"));
+ _DATA(VALID_OBJECT_ID_3,"2.0","true"));
{
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
const char *ORDERED_OBJECT_IDS[] = { VALID_OBJECT_ID_2, VALID_OBJECT_ID_3, VALID_OBJECT_ID_1 };
@@ -428,8 +434,7 @@ namespace tut
LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(
_DATA_URLS(VALID_OBJECT_ID,
- "1.0",
- "1.0",
+ "1.0","true",
FAKE_OBJECT_MEDIA_CAP_URL_503,
FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL));
int num_refs_start = o->getNumRefs();
@@ -484,8 +489,7 @@ namespace tut
LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(
_DATA_URLS(VALID_OBJECT_ID,
- "1.0",
- "1.0",
+ "1.0","true",
FAKE_OBJECT_MEDIA_CAP_URL,
FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR));
{
@@ -517,9 +521,9 @@ namespace tut
LOG_TEST(7);
LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(
- _DATA(VALID_OBJECT_ID_1,"3.0","1.0"));
+ _DATA(VALID_OBJECT_ID_1,"3.0","true"));
LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(
- _DATA(VALID_OBJECT_ID_2,"1.0","1.0"));
+ _DATA(VALID_OBJECT_ID_2,"1.0","true"));
int num_refs_start = o1->getNumRefs();
{
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
@@ -551,10 +555,10 @@ namespace tut
// Test queue handling of objects that are marked dead.
LOG_TEST(8);
- LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"1.0","1.0"));
- LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"2.0","1.0"));
- LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"3.0","1.0"));
- LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"4.0","1.0"));
+ LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"4.0","true"));
+ LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"3.0","true"));
+ LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"2.0","true"));
+ LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"1.0","true"));
{
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
@@ -616,10 +620,11 @@ namespace tut
//
LOG_TEST(9);
- LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"10.0","1.0"));
- LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"20.0","1.0"));
- LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"30.0","1.0"));
- LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"40.0","1.0"));
+ LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"40.0","true"));
+ LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"30.0","true"));
+ LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"20.0","true"));
+ LLMediaDataClientObjectTest *object4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"10.0","true"));
+ LLMediaDataClientObject::ptr_t o4 = object4;
{
LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
@@ -630,53 +635,52 @@ namespace tut
mdc->fetchMedia(o3);
mdc->fetchMedia(o4);
- int test_num = 0;
+ int tick_num = 0;
- ensure(STR(test_num) + ". is in queue 1", mdc->isInQueue(o1));
- ensure(STR(test_num) + ". is in queue 2", mdc->isInQueue(o2));
- ensure(STR(test_num) + ". is in queue 3", mdc->isInQueue(o3));
- ensure(STR(test_num) + ". is in queue 4", mdc->isInQueue(o4));
- ensure(STR(test_num) + ". post records", gPostRecords->size(), 0);
+ ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
+ ensure(STR(tick_num) + ". is 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(), 0);
::pump_timers();
- ++test_num;
+ ++tick_num;
// The first tick should remove the first one
- ensure(STR(test_num) + ". is not in queue 1", !mdc->isInQueue(o1));
- ensure(STR(test_num) + ". is in queue 2", mdc->isInQueue(o2));
- ensure(STR(test_num) + ". is in queue 3", mdc->isInQueue(o3));
- ensure(STR(test_num) + ". is in queue 4", mdc->isInQueue(o4));
- ensure(STR(test_num) + ". post records", gPostRecords->size(), 1);
+ ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1));
+ ensure(STR(tick_num) + ". is 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);
// Now, pretend that object 4 moved relative to the avatar such
// that it is now closest
- static_cast<LLMediaDataClientObjectTest*>(
- static_cast<LLMediaDataClientObject*>(o4))->setDistanceFromAvatar(5.0);
+ object4->setMediaInterest(50.0);
::pump_timers();
- ++test_num;
+ ++tick_num;
// The second tick should still pick off item 2, but then re-sort
// have picked off object 4
- ensure(STR(test_num) + ". is in queue 2", mdc->isInQueue(o2));
- ensure(STR(test_num) + ". is in queue 3", mdc->isInQueue(o3));
- ensure(STR(test_num) + ". is not in queue 4", !mdc->isInQueue(o4));
- ensure(STR(test_num) + ". post records", gPostRecords->size(), 2);
+ ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
+ ensure(STR(tick_num) + ". is 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(), 2);
::pump_timers();
- ++test_num;
+ ++tick_num;
// The third tick should pick off object 2
- ensure(STR(test_num) + ". is not in queue 2", !mdc->isInQueue(o2));
- ensure(STR(test_num) + ". is in queue 3", mdc->isInQueue(o3));
- ensure(STR(test_num) + ". post records", gPostRecords->size(), 3);
+ 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) + ". post records", gPostRecords->size(), 3);
// The fourth tick should pick off object 3
::pump_timers();
- ++test_num;
+ ++tick_num;
- ensure(STR(test_num) + ". is not in queue 3", !mdc->isInQueue(o3));
- ensure(STR(test_num) + ". post records", gPostRecords->size(), 4);
+ ensure(STR(tick_num) + ". is not in queue 3", !mdc->isInQueue(o3));
+ ensure(STR(tick_num) + ". post records", gPostRecords->size(), 4);
ensure("queue empty", mdc->isEmpty());
}
@@ -686,4 +690,249 @@ namespace tut
ensure("refcount of o4", o4->getNumRefs(), 1);
}
+
+ template<> template<>
+ void mediadataclient_object_t::test<10>()
+ {
+ //
+ // Test using the "round-robin" queue
+ //
+ LOG_TEST(10);
+
+ LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"1.0","true"));
+ LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"2.0","true"));
+ LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"3.0","false"));
+ LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"4.0","false"));
+ {
+ LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
+
+ // 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
+ mdc->fetchMedia(o1);
+ mdc->fetchMedia(o2);
+ mdc->fetchMedia(o3);
+ mdc->fetchMedia(o4);
+
+ int tick_num = 0;
+
+ // 0
+ ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
+ ensure(STR(tick_num) + ". is 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(), 0);
+
+ ::pump_timers();
+ ++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 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);
+ ensure(STR(tick_num) + ". post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_2));
+
+ ::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));
+ 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 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));
+
+ ::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 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));
+
+ ::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));
+ 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) + ". 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));
+
+ ::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));
+ ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1));
+ 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);
+
+ ::pump_timers();
+
+ // Whew....better be empty
+ ensure("queue empty", mdc->isEmpty());
+ }
+ ensure("refcount of o1", o1->getNumRefs(), 1);
+ ensure("refcount of o2", o2->getNumRefs(), 1);
+ ensure("refcount of o3", o3->getNumRefs(), 1);
+ ensure("refcount of o4", o4->getNumRefs(), 1);
+ }
+
+
+ template<> template<>
+ void mediadataclient_object_t::test<11>()
+ {
+ //
+ // Test LLMediaDataClient's destructor
+ //
+ LOG_TEST(11);
+
+ LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA);
+ int num_refs_start = o->getNumRefs();
+ {
+ LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
+ mdc->fetchMedia(o);
+ // must tick enough times to clear refcount of mdc
+ ::pump_timers();
+ }
+ // Make sure everyone's destroyed properly
+ ensure("REF COUNT", o->getNumRefs(), num_refs_start);
+ }
+
+ template<> template<>
+ void mediadataclient_object_t::test<12>()
+ {
+ //
+ // Test the "not interesting enough" call
+ //
+ LOG_TEST(12);
+
+ LLMediaDataClientObjectTest *object1 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_1,"1.0","true"));
+ LLMediaDataClientObject::ptr_t o1 = object1;
+ LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_2,"2.0","true"));
+ LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_3,"3.0","true"));
+ LLMediaDataClientObject::ptr_t o4 = new LLMediaDataClientObjectTest(_DATA(VALID_OBJECT_ID_4,"4.0","true"));
+ {
+ LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(NO_PERIOD,NO_PERIOD);
+
+ // queue up all 4 objects. The first two are "interesting enough".
+ // Firing the timer 4 times should therefore leave them.
+ // Note that they should be sorted 4,3,2,1
+ // Then, we'll make one "interesting enough", fire the timer a few
+ // times, and make sure only it gets pulled off the queue
+ gMinimumInterestLevel = 2.5;
+ mdc->fetchMedia(o1);
+ mdc->fetchMedia(o2);
+ mdc->fetchMedia(o3);
+ mdc->fetchMedia(o4);
+
+ int tick_num = 0;
+
+ // 0
+ ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
+ ensure(STR(tick_num) + ". is 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(), 0);
+
+ ::pump_timers();
+ ++tick_num;
+
+ // 1 The first tick should remove object 4
+ ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
+ ensure(STR(tick_num) + ". is in queue 2", mdc->isInQueue(o2));
+ ensure(STR(tick_num) + ". is 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(), 1);
+ ensure(STR(tick_num) + ". post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_4));
+
+ ::pump_timers();
+ ++tick_num;
+
+ // 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 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(), 2);
+ 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 not pull off anything
+ ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
+ ensure(STR(tick_num) + ". is 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(), 2);
+
+ ::pump_timers();
+ ++tick_num;
+
+ // 4 The fourth tick (for good measure) should not pull off anything
+ ensure(STR(tick_num) + ". is in queue 1", mdc->isInQueue(o1));
+ ensure(STR(tick_num) + ". is 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(), 2);
+
+ // Okay, now futz with object 1's interest, such that it is now
+ // "interesting enough"
+ object1->setMediaInterest((F64)5.0);
+
+ // This should sort so that the queue is now [1 2]
+ ::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 1", !mdc->isInQueue(o1));
+ ensure(STR(tick_num) + ". is 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(), 3);
+ ensure(STR(tick_num) + ". post object id", (*gPostRecords)[2]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID_1));
+
+ ::pump_timers();
+ ++tick_num;
+
+ // 6 The sixth tick should not pull off anything
+ ensure(STR(tick_num) + ". is not in queue 1", !mdc->isInQueue(o1));
+ ensure(STR(tick_num) + ". is 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(), 3);
+
+ ::pump_timers();
+ ++tick_num;
+
+ // Whew....better NOT be empty ... o2 should still be there
+ ensure("queue not empty", !mdc->isEmpty());
+
+ // 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));
+ // tick
+ ::pump_timers();
+ }
+ ensure("refcount of o1", o1->getNumRefs(), 1);
+ ensure("refcount of o2", o2->getNumRefs(), 1);
+ ensure("refcount of o3", o3->getNumRefs(), 1);
+ ensure("refcount of o4", o4->getNumRefs(), 1);
+ }
}