/indra/newview/llmediadataclient.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 1070 lines · 722 code · 176 blank · 172 comment · 107 complexity · fab296ae8ceaefb9a25fa3d64843bf43 MD5 · raw file

  1. /**
  2. * @file llmediadataclient.cpp
  3. * @brief class for queueing up requests for media data
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llmediadataclient.h"
  28. #if LL_MSVC
  29. // disable boost::lexical_cast warning
  30. #pragma warning (disable:4702)
  31. #endif
  32. #include <boost/lexical_cast.hpp>
  33. #include "llhttpstatuscodes.h"
  34. #include "llsdutil.h"
  35. #include "llmediaentry.h"
  36. #include "lltextureentry.h"
  37. #include "llviewerregion.h"
  38. //
  39. // When making a request
  40. // - obtain the "overall interest score" of the object.
  41. // This would be the sum of the impls' interest scores.
  42. // - put the request onto a queue sorted by this score
  43. // (highest score at the front of the queue)
  44. // - On a timer, once a second, pull off the head of the queue and send
  45. // the request.
  46. // - Any request that gets a 503 still goes through the retry logic
  47. //
  48. /***************************************************************************************************************
  49. What's up with this queueing code?
  50. First, a bit of background:
  51. Media on a prim was added into the system in the Viewer 2.0 timeframe. In order to avoid changing the
  52. network format of objects, an unused field in the object (the "MediaURL" string) was repurposed to
  53. indicate that the object had media data, and also hold a sequence number and the UUID of the agent
  54. who last updated the data. The actual media data for objects is accessed via the "ObjectMedia" capability.
  55. Due to concerns about sim performance, requests to this capability are rate-limited to 5 requests every
  56. 5 seconds per agent.
  57. The initial implementation of LLMediaDataClient used a single queue to manage requests to the "ObjectMedia" cap.
  58. Requests to the cap were queued so that objects closer to the avatar were loaded in first, since they were most
  59. likely to be the ones the media performance manager would load.
  60. This worked in some cases, but we found that it was possible for a scripted object that constantly updated its
  61. media data to starve other objects, since the same queue contained both requests to load previously unseen media
  62. data and requests to fetch media data in response to object updates.
  63. The solution for this we came up with was to have two queues. The sorted queue contains requests to fetch media
  64. data for objects that don't have it yet, and the round-robin queue contains requests to update media data for
  65. objects that have already completed their initial load. When both queues are non-empty, the code ping-pongs
  66. between them so that updates can't completely block initial load-in.
  67. **************************************************************************************************************/
  68. //
  69. // Forward decls
  70. //
  71. const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s)
  72. const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 5.0; // secs
  73. const U32 LLMediaDataClient::MAX_RETRIES = 4;
  74. const U32 LLMediaDataClient::MAX_SORTED_QUEUE_SIZE = 10000;
  75. const U32 LLMediaDataClient::MAX_ROUND_ROBIN_QUEUE_SIZE = 10000;
  76. // << operators
  77. std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q);
  78. std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &q);
  79. template <typename T>
  80. static typename T::iterator find_matching_request(T &c, const LLMediaDataClient::Request *request, LLMediaDataClient::Request::Type match_type)
  81. {
  82. for(typename T::iterator iter = c.begin(); iter != c.end(); ++iter)
  83. {
  84. if(request->isMatch(*iter, match_type))
  85. {
  86. return iter;
  87. }
  88. }
  89. return c.end();
  90. }
  91. template <typename T>
  92. static typename T::iterator find_matching_request(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type)
  93. {
  94. for(typename T::iterator iter = c.begin(); iter != c.end(); ++iter)
  95. {
  96. if(((*iter)->getID() == id) && ((match_type == LLMediaDataClient::Request::ANY) || (match_type == (*iter)->getType())))
  97. {
  98. return iter;
  99. }
  100. }
  101. return c.end();
  102. }
  103. // NOTE: remove_matching_requests will not work correctly for containers where deleting an element may invalidate iterators
  104. // to other elements in the container (such as std::vector).
  105. // If the implementation is changed to use a container with this property, this will need to be revisited.
  106. template <typename T>
  107. static void remove_matching_requests(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type)
  108. {
  109. for(typename T::iterator iter = c.begin(); iter != c.end();)
  110. {
  111. typename T::value_type i = *iter;
  112. typename T::iterator next = iter;
  113. next++;
  114. if((i->getID() == id) && ((match_type == LLMediaDataClient::Request::ANY) || (match_type == i->getType())))
  115. {
  116. i->markDead();
  117. c.erase(iter);
  118. }
  119. iter = next;
  120. }
  121. }
  122. //////////////////////////////////////////////////////////////////////////////////////
  123. //
  124. // LLMediaDataClient
  125. //
  126. //////////////////////////////////////////////////////////////////////////////////////
  127. LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay,
  128. F32 retry_timer_delay,
  129. U32 max_retries,
  130. U32 max_sorted_queue_size,
  131. U32 max_round_robin_queue_size)
  132. : mQueueTimerDelay(queue_timer_delay),
  133. mRetryTimerDelay(retry_timer_delay),
  134. mMaxNumRetries(max_retries),
  135. mMaxSortedQueueSize(max_sorted_queue_size),
  136. mMaxRoundRobinQueueSize(max_round_robin_queue_size),
  137. mQueueTimerIsRunning(false)
  138. {
  139. }
  140. LLMediaDataClient::~LLMediaDataClient()
  141. {
  142. stopQueueTimer();
  143. }
  144. bool LLMediaDataClient::isEmpty() const
  145. {
  146. return mQueue.empty();
  147. }
  148. bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object)
  149. {
  150. if(find_matching_request(mQueue, object->getID()) != mQueue.end())
  151. return true;
  152. if(find_matching_request(mUnQueuedRequests, object->getID()) != mUnQueuedRequests.end())
  153. return true;
  154. return false;
  155. }
  156. void LLMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object)
  157. {
  158. LL_DEBUGS("LLMediaDataClient") << "removing requests matching ID " << object->getID() << LL_ENDL;
  159. remove_matching_requests(mQueue, object->getID());
  160. remove_matching_requests(mUnQueuedRequests, object->getID());
  161. }
  162. void LLMediaDataClient::startQueueTimer()
  163. {
  164. if (! mQueueTimerIsRunning)
  165. {
  166. LL_DEBUGS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL;
  167. // LLEventTimer automagically takes care of the lifetime of this object
  168. new QueueTimer(mQueueTimerDelay, this);
  169. }
  170. else {
  171. LL_DEBUGS("LLMediaDataClient") << "queue timer is already running" << LL_ENDL;
  172. }
  173. }
  174. void LLMediaDataClient::stopQueueTimer()
  175. {
  176. mQueueTimerIsRunning = false;
  177. }
  178. bool LLMediaDataClient::processQueueTimer()
  179. {
  180. if(isEmpty())
  181. return true;
  182. LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, queue size is: " << mQueue.size() << LL_ENDL;
  183. LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() started, SORTED queue is: " << mQueue << LL_ENDL;
  184. serviceQueue();
  185. LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, queue size is: " << mQueue.size() << LL_ENDL;
  186. LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() finished, SORTED queue is: " << mQueue << LL_ENDL;
  187. return isEmpty();
  188. }
  189. LLMediaDataClient::request_ptr_t LLMediaDataClient::dequeue()
  190. {
  191. request_ptr_t request;
  192. request_queue_t *queue_p = getQueue();
  193. if (queue_p->empty())
  194. {
  195. LL_DEBUGS("LLMediaDataClient") << "queue empty: " << (*queue_p) << LL_ENDL;
  196. }
  197. else
  198. {
  199. request = queue_p->front();
  200. if(canServiceRequest(request))
  201. {
  202. // We will be returning this request, so remove it from the queue.
  203. queue_p->pop_front();
  204. }
  205. else
  206. {
  207. // Don't return this request -- it's not ready to be serviced.
  208. request = NULL;
  209. }
  210. }
  211. return request;
  212. }
  213. void LLMediaDataClient::pushBack(request_ptr_t request)
  214. {
  215. request_queue_t *queue_p = getQueue();
  216. queue_p->push_front(request);
  217. }
  218. void LLMediaDataClient::trackRequest(request_ptr_t request)
  219. {
  220. request_set_t::iterator iter = mUnQueuedRequests.find(request);
  221. if(iter != mUnQueuedRequests.end())
  222. {
  223. LL_WARNS("LLMediaDataClient") << "Tracking already tracked request: " << *request << LL_ENDL;
  224. }
  225. else
  226. {
  227. mUnQueuedRequests.insert(request);
  228. }
  229. }
  230. void LLMediaDataClient::stopTrackingRequest(request_ptr_t request)
  231. {
  232. request_set_t::iterator iter = mUnQueuedRequests.find(request);
  233. if (iter != mUnQueuedRequests.end())
  234. {
  235. mUnQueuedRequests.erase(iter);
  236. }
  237. else
  238. {
  239. LL_WARNS("LLMediaDataClient") << "Removing an untracked request: " << *request << LL_ENDL;
  240. }
  241. }
  242. void LLMediaDataClient::serviceQueue()
  243. {
  244. // Peel one off of the items from the queue and execute it
  245. request_ptr_t request;
  246. do
  247. {
  248. request = dequeue();
  249. if(request.isNull())
  250. {
  251. // Queue is empty.
  252. return;
  253. }
  254. if(request->isDead())
  255. {
  256. LL_INFOS("LLMediaDataClient") << "Skipping dead request " << *request << LL_ENDL;
  257. continue;
  258. }
  259. } while(false);
  260. // try to send the HTTP message to the cap url
  261. std::string url = request->getCapability();
  262. if (!url.empty())
  263. {
  264. const LLSD &sd_payload = request->getPayload();
  265. LL_INFOS("LLMediaDataClient") << "Sending request for " << *request << LL_ENDL;
  266. // Add this request to the non-queued tracking list
  267. trackRequest(request);
  268. // and make the post
  269. LLHTTPClient::post(url, sd_payload, request->createResponder());
  270. }
  271. else
  272. {
  273. // Cap url doesn't exist.
  274. if(request->getRetryCount() < mMaxNumRetries)
  275. {
  276. LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " (empty cap url), will retry." << LL_ENDL;
  277. // Put this request back at the head of its queue, and retry next time the queue timer fires.
  278. request->incRetryCount();
  279. pushBack(request);
  280. }
  281. else
  282. {
  283. // This request has exceeded its maxumim retry count. It will be dropped.
  284. LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " << mMaxNumRetries << " tries, dropping request." << LL_ENDL;
  285. }
  286. }
  287. }
  288. // dump the queue
  289. std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q)
  290. {
  291. int i = 0;
  292. LLMediaDataClient::request_queue_t::const_iterator iter = q.begin();
  293. LLMediaDataClient::request_queue_t::const_iterator end = q.end();
  294. while (iter != end)
  295. {
  296. s << "\t" << i << "]: " << (*iter)->getID().asString() << "(" << (*iter)->getObject()->getMediaInterest() << ")";
  297. iter++;
  298. i++;
  299. }
  300. return s;
  301. }
  302. //////////////////////////////////////////////////////////////////////////////////////
  303. //
  304. // LLMediaDataClient::QueueTimer
  305. // Queue of LLMediaDataClientObject smart pointers to request media for.
  306. //
  307. //////////////////////////////////////////////////////////////////////////////////////
  308. LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc)
  309. : LLEventTimer(time), mMDC(mdc)
  310. {
  311. mMDC->setIsRunning(true);
  312. }
  313. // virtual
  314. BOOL LLMediaDataClient::QueueTimer::tick()
  315. {
  316. BOOL result = TRUE;
  317. if (!mMDC.isNull())
  318. {
  319. result = mMDC->processQueueTimer();
  320. if(result)
  321. {
  322. // This timer won't fire again.
  323. mMDC->setIsRunning(false);
  324. mMDC = NULL;
  325. }
  326. }
  327. return result;
  328. }
  329. //////////////////////////////////////////////////////////////////////////////////////
  330. //
  331. // LLMediaDataClient::Responder::RetryTimer
  332. //
  333. //////////////////////////////////////////////////////////////////////////////////////
  334. LLMediaDataClient::RetryTimer::RetryTimer(F32 time, request_ptr_t request)
  335. : LLEventTimer(time), mRequest(request)
  336. {
  337. mRequest->startTracking();
  338. }
  339. // virtual
  340. BOOL LLMediaDataClient::RetryTimer::tick()
  341. {
  342. mRequest->stopTracking();
  343. if(mRequest->isDead())
  344. {
  345. LL_INFOS("LLMediaDataClient") << "RetryTimer fired for dead request: " << *mRequest << ", aborting." << LL_ENDL;
  346. }
  347. else
  348. {
  349. LL_INFOS("LLMediaDataClient") << "RetryTimer fired for: " << *mRequest << ", retrying." << LL_ENDL;
  350. mRequest->reEnqueue();
  351. }
  352. // Release the ref to the request.
  353. mRequest = NULL;
  354. // Don't fire again
  355. return TRUE;
  356. }
  357. //////////////////////////////////////////////////////////////////////////////////////
  358. //
  359. // LLMediaDataClient::Request
  360. //
  361. //////////////////////////////////////////////////////////////////////////////////////
  362. /*static*/U32 LLMediaDataClient::Request::sNum = 0;
  363. LLMediaDataClient::Request::Request(Type in_type,
  364. LLMediaDataClientObject *obj,
  365. LLMediaDataClient *mdc,
  366. S32 face)
  367. : mType(in_type),
  368. mObject(obj),
  369. mNum(++sNum),
  370. mRetryCount(0),
  371. mMDC(mdc),
  372. mScore((F64)0.0),
  373. mFace(face)
  374. {
  375. mObjectID = mObject->getID();
  376. }
  377. const char *LLMediaDataClient::Request::getCapName() const
  378. {
  379. if(mMDC)
  380. return mMDC->getCapabilityName();
  381. return "";
  382. }
  383. std::string LLMediaDataClient::Request::getCapability() const
  384. {
  385. if(mMDC)
  386. {
  387. return getObject()->getCapabilityUrl(getCapName());
  388. }
  389. return "";
  390. }
  391. const char *LLMediaDataClient::Request::getTypeAsString() const
  392. {
  393. Type t = getType();
  394. switch (t)
  395. {
  396. case GET:
  397. return "GET";
  398. break;
  399. case UPDATE:
  400. return "UPDATE";
  401. break;
  402. case NAVIGATE:
  403. return "NAVIGATE";
  404. break;
  405. case ANY:
  406. return "ANY";
  407. break;
  408. }
  409. return "";
  410. }
  411. void LLMediaDataClient::Request::reEnqueue()
  412. {
  413. if(mMDC)
  414. {
  415. mMDC->enqueue(this);
  416. }
  417. }
  418. F32 LLMediaDataClient::Request::getRetryTimerDelay() const
  419. {
  420. if(mMDC)
  421. return mMDC->mRetryTimerDelay;
  422. return 0.0f;
  423. }
  424. U32 LLMediaDataClient::Request::getMaxNumRetries() const
  425. {
  426. if(mMDC)
  427. return mMDC->mMaxNumRetries;
  428. return 0;
  429. }
  430. void LLMediaDataClient::Request::updateScore()
  431. {
  432. F64 tmp = mObject->getMediaInterest();
  433. if (tmp != mScore)
  434. {
  435. LL_DEBUGS("LLMediaDataClient") << "Score for " << mObject->getID() << " changed from " << mScore << " to " << tmp << LL_ENDL;
  436. mScore = tmp;
  437. }
  438. }
  439. void LLMediaDataClient::Request::markDead()
  440. {
  441. mMDC = NULL;
  442. }
  443. bool LLMediaDataClient::Request::isDead()
  444. {
  445. return ((mMDC == NULL) || mObject->isDead());
  446. }
  447. void LLMediaDataClient::Request::startTracking()
  448. {
  449. if(mMDC)
  450. mMDC->trackRequest(this);
  451. }
  452. void LLMediaDataClient::Request::stopTracking()
  453. {
  454. if(mMDC)
  455. mMDC->stopTrackingRequest(this);
  456. }
  457. std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r)
  458. {
  459. s << "request: num=" << r.getNum()
  460. << " type=" << r.getTypeAsString()
  461. << " ID=" << r.getID()
  462. << " face=" << r.getFace()
  463. << " #retries=" << r.getRetryCount();
  464. return s;
  465. }
  466. //////////////////////////////////////////////////////////////////////////////////////
  467. //
  468. // LLMediaDataClient::Responder
  469. //
  470. //////////////////////////////////////////////////////////////////////////////////////
  471. LLMediaDataClient::Responder::Responder(const request_ptr_t &request)
  472. : mRequest(request)
  473. {
  474. }
  475. /*virtual*/
  476. void LLMediaDataClient::Responder::error(U32 status, const std::string& reason)
  477. {
  478. mRequest->stopTracking();
  479. if(mRequest->isDead())
  480. {
  481. LL_WARNS("LLMediaDataClient") << "dead request " << *mRequest << LL_ENDL;
  482. return;
  483. }
  484. if (status == HTTP_SERVICE_UNAVAILABLE)
  485. {
  486. F32 retry_timeout = mRequest->getRetryTimerDelay();
  487. mRequest->incRetryCount();
  488. if (mRequest->getRetryCount() < mRequest->getMaxNumRetries())
  489. {
  490. LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL;
  491. // Start timer (instances are automagically tracked by
  492. // InstanceTracker<> and LLEventTimer)
  493. new RetryTimer(F32(retry_timeout/*secs*/), mRequest);
  494. }
  495. else
  496. {
  497. LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retry count "
  498. << mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL;
  499. }
  500. }
  501. else
  502. {
  503. std::string msg = boost::lexical_cast<std::string>(status) + ": " + reason;
  504. LL_WARNS("LLMediaDataClient") << *mRequest << " http error(" << msg << ")" << LL_ENDL;
  505. }
  506. }
  507. /*virtual*/
  508. void LLMediaDataClient::Responder::result(const LLSD& content)
  509. {
  510. mRequest->stopTracking();
  511. if(mRequest->isDead())
  512. {
  513. LL_WARNS("LLMediaDataClient") << "dead request " << *mRequest << LL_ENDL;
  514. return;
  515. }
  516. LL_DEBUGS("LLMediaDataClientResponse") << *mRequest << " result : " << ll_print_sd(content) << LL_ENDL;
  517. }
  518. //////////////////////////////////////////////////////////////////////////////////////
  519. //
  520. // LLObjectMediaDataClient
  521. // Subclass of LLMediaDataClient for the ObjectMedia cap
  522. //
  523. //////////////////////////////////////////////////////////////////////////////////////
  524. void LLObjectMediaDataClient::fetchMedia(LLMediaDataClientObject *object)
  525. {
  526. // Create a get request and put it in the queue.
  527. enqueue(new RequestGet(object, this));
  528. }
  529. const char *LLObjectMediaDataClient::getCapabilityName() const
  530. {
  531. return "ObjectMedia";
  532. }
  533. LLObjectMediaDataClient::request_queue_t *LLObjectMediaDataClient::getQueue()
  534. {
  535. return (mCurrentQueueIsTheSortedQueue) ? &mQueue : &mRoundRobinQueue;
  536. }
  537. void LLObjectMediaDataClient::sortQueue()
  538. {
  539. if(!mQueue.empty())
  540. {
  541. // score all elements in the sorted queue.
  542. for(request_queue_t::iterator iter = mQueue.begin(); iter != mQueue.end(); iter++)
  543. {
  544. (*iter)->updateScore();
  545. }
  546. // Re-sort the list...
  547. mQueue.sort(compareRequestScores);
  548. // ...then cull items over the max
  549. U32 size = mQueue.size();
  550. if (size > mMaxSortedQueueSize)
  551. {
  552. U32 num_to_cull = (size - mMaxSortedQueueSize);
  553. LL_INFOS_ONCE("LLMediaDataClient") << "sorted queue MAXED OUT! Culling "
  554. << num_to_cull << " items" << LL_ENDL;
  555. while (num_to_cull-- > 0)
  556. {
  557. mQueue.back()->markDead();
  558. mQueue.pop_back();
  559. }
  560. }
  561. }
  562. }
  563. // static
  564. bool LLObjectMediaDataClient::compareRequestScores(const request_ptr_t &o1, const request_ptr_t &o2)
  565. {
  566. if (o2.isNull()) return true;
  567. if (o1.isNull()) return false;
  568. return ( o1->getScore() > o2->getScore() );
  569. }
  570. void LLObjectMediaDataClient::enqueue(Request *request)
  571. {
  572. if(request->isDead())
  573. {
  574. LL_DEBUGS("LLMediaDataClient") << "not queueing dead request " << *request << LL_ENDL;
  575. return;
  576. }
  577. // Invariants:
  578. // new requests always go into the sorted queue.
  579. //
  580. bool is_new = request->isNew();
  581. if(!is_new && (request->getType() == Request::GET))
  582. {
  583. // For GET requests that are not new, if a matching request is already in the round robin queue,
  584. // in flight, or being retried, leave it at its current position.
  585. request_queue_t::iterator iter = find_matching_request(mRoundRobinQueue, request->getID(), Request::GET);
  586. request_set_t::iterator iter2 = find_matching_request(mUnQueuedRequests, request->getID(), Request::GET);
  587. if( (iter != mRoundRobinQueue.end()) || (iter2 != mUnQueuedRequests.end()) )
  588. {
  589. LL_DEBUGS("LLMediaDataClient") << "ALREADY THERE: NOT Queuing request for " << *request << LL_ENDL;
  590. return;
  591. }
  592. }
  593. // TODO: should an UPDATE cause pending GET requests for the same object to be removed from the queue?
  594. // IF the update will cause an object update message to be sent out at some point in the future, it probably should.
  595. // Remove any existing requests of this type for this object
  596. remove_matching_requests(mQueue, request->getID(), request->getType());
  597. remove_matching_requests(mRoundRobinQueue, request->getID(), request->getType());
  598. remove_matching_requests(mUnQueuedRequests, request->getID(), request->getType());
  599. if (is_new)
  600. {
  601. LL_DEBUGS("LLMediaDataClient") << "Queuing SORTED request for " << *request << LL_ENDL;
  602. mQueue.push_back(request);
  603. LL_DEBUGS("LLMediaDataClientQueue") << "SORTED queue:" << mQueue << LL_ENDL;
  604. }
  605. else
  606. {
  607. if (mRoundRobinQueue.size() > mMaxRoundRobinQueueSize)
  608. {
  609. LL_INFOS_ONCE("LLMediaDataClient") << "RR QUEUE MAXED OUT!!!" << LL_ENDL;
  610. LL_DEBUGS("LLMediaDataClient") << "Not queuing " << *request << LL_ENDL;
  611. return;
  612. }
  613. LL_DEBUGS("LLMediaDataClient") << "Queuing RR request for " << *request << LL_ENDL;
  614. // Push the request on the pending queue
  615. mRoundRobinQueue.push_back(request);
  616. LL_DEBUGS("LLMediaDataClientQueue") << "RR queue:" << mRoundRobinQueue << LL_ENDL;
  617. }
  618. // Start the timer if not already running
  619. startQueueTimer();
  620. }
  621. bool LLObjectMediaDataClient::canServiceRequest(request_ptr_t request)
  622. {
  623. if(mCurrentQueueIsTheSortedQueue)
  624. {
  625. if(!request->getObject()->isInterestingEnough())
  626. {
  627. LL_DEBUGS("LLMediaDataClient") << "Not fetching " << *request << ": not interesting enough" << LL_ENDL;
  628. return false;
  629. }
  630. }
  631. return true;
  632. };
  633. void LLObjectMediaDataClient::swapCurrentQueue()
  634. {
  635. // Swap
  636. mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue;
  637. // If its empty, swap back
  638. if (getQueue()->empty())
  639. {
  640. mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue;
  641. }
  642. }
  643. bool LLObjectMediaDataClient::isEmpty() const
  644. {
  645. return mQueue.empty() && mRoundRobinQueue.empty();
  646. }
  647. bool LLObjectMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object)
  648. {
  649. // First, call parent impl.
  650. if(LLMediaDataClient::isInQueue(object))
  651. return true;
  652. if(find_matching_request(mRoundRobinQueue, object->getID()) != mRoundRobinQueue.end())
  653. return true;
  654. return false;
  655. }
  656. void LLObjectMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object)
  657. {
  658. // First, call parent impl.
  659. LLMediaDataClient::removeFromQueue(object);
  660. remove_matching_requests(mRoundRobinQueue, object->getID());
  661. }
  662. bool LLObjectMediaDataClient::processQueueTimer()
  663. {
  664. if(isEmpty())
  665. return true;
  666. LL_DEBUGS("LLMediaDataClient") << "started, SORTED queue size is: " << mQueue.size()
  667. << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL;
  668. LL_DEBUGS("LLMediaDataClientQueue") << " SORTED queue is: " << mQueue << LL_ENDL;
  669. LL_DEBUGS("LLMediaDataClientQueue") << " RR queue is: " << mRoundRobinQueue << LL_ENDL;
  670. // purgeDeadRequests();
  671. sortQueue();
  672. LL_DEBUGS("LLMediaDataClientQueue") << "after sort, SORTED queue is: " << mQueue << LL_ENDL;
  673. serviceQueue();
  674. swapCurrentQueue();
  675. LL_DEBUGS("LLMediaDataClient") << "finished, SORTED queue size is: " << mQueue.size()
  676. << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL;
  677. LL_DEBUGS("LLMediaDataClientQueue") << " SORTED queue is: " << mQueue << LL_ENDL;
  678. LL_DEBUGS("LLMediaDataClientQueue") << " RR queue is: " << mRoundRobinQueue << LL_ENDL;
  679. return isEmpty();
  680. }
  681. LLObjectMediaDataClient::RequestGet::RequestGet(LLMediaDataClientObject *obj, LLMediaDataClient *mdc):
  682. LLMediaDataClient::Request(LLMediaDataClient::Request::GET, obj, mdc)
  683. {
  684. }
  685. LLSD LLObjectMediaDataClient::RequestGet::getPayload() const
  686. {
  687. LLSD result;
  688. result["verb"] = "GET";
  689. result[LLTextureEntry::OBJECT_ID_KEY] = mObject->getID();
  690. return result;
  691. }
  692. LLMediaDataClient::Responder *LLObjectMediaDataClient::RequestGet::createResponder()
  693. {
  694. return new LLObjectMediaDataClient::Responder(this);
  695. }
  696. void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object)
  697. {
  698. // Create an update request and put it in the queue.
  699. enqueue(new RequestUpdate(object, this));
  700. }
  701. LLObjectMediaDataClient::RequestUpdate::RequestUpdate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc):
  702. LLMediaDataClient::Request(LLMediaDataClient::Request::UPDATE, obj, mdc)
  703. {
  704. }
  705. LLSD LLObjectMediaDataClient::RequestUpdate::getPayload() const
  706. {
  707. LLSD result;
  708. result["verb"] = "UPDATE";
  709. result[LLTextureEntry::OBJECT_ID_KEY] = mObject->getID();
  710. LLSD object_media_data;
  711. int i = 0;
  712. int end = mObject->getMediaDataCount();
  713. for ( ; i < end ; ++i)
  714. {
  715. object_media_data.append(mObject->getMediaDataLLSD(i));
  716. }
  717. result[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data;
  718. return result;
  719. }
  720. LLMediaDataClient::Responder *LLObjectMediaDataClient::RequestUpdate::createResponder()
  721. {
  722. // This just uses the base class's responder.
  723. return new LLMediaDataClient::Responder(this);
  724. }
  725. /*virtual*/
  726. void LLObjectMediaDataClient::Responder::result(const LLSD& content)
  727. {
  728. getRequest()->stopTracking();
  729. if(getRequest()->isDead())
  730. {
  731. LL_WARNS("LLMediaDataClient") << "dead request " << *(getRequest()) << LL_ENDL;
  732. return;
  733. }
  734. // This responder is only used for GET requests, not UPDATE.
  735. LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << " GET returned: " << ll_print_sd(content) << LL_ENDL;
  736. // Look for an error
  737. if (content.has("error"))
  738. {
  739. const LLSD &error = content["error"];
  740. LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error getting media data for object: code=" <<
  741. error["code"].asString() << ": " << error["message"].asString() << LL_ENDL;
  742. // XXX Warn user?
  743. }
  744. else
  745. {
  746. // Check the data
  747. const LLUUID &object_id = content[LLTextureEntry::OBJECT_ID_KEY];
  748. if (object_id != getRequest()->getObject()->getID())
  749. {
  750. // NOT good, wrong object id!!
  751. LL_WARNS("LLMediaDataClient") << *(getRequest()) << " DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL;
  752. return;
  753. }
  754. // Otherwise, update with object media data
  755. getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY],
  756. content[LLTextureEntry::MEDIA_VERSION_KEY]);
  757. }
  758. }
  759. //////////////////////////////////////////////////////////////////////////////////////
  760. //
  761. // LLObjectMediaNavigateClient
  762. // Subclass of LLMediaDataClient for the ObjectMediaNavigate cap
  763. //
  764. //////////////////////////////////////////////////////////////////////////////////////
  765. const char *LLObjectMediaNavigateClient::getCapabilityName() const
  766. {
  767. return "ObjectMediaNavigate";
  768. }
  769. void LLObjectMediaNavigateClient::enqueue(Request *request)
  770. {
  771. if(request->isDead())
  772. {
  773. LL_DEBUGS("LLMediaDataClient") << "not queueing dead request " << *request << LL_ENDL;
  774. return;
  775. }
  776. // If there's already a matching request in the queue, remove it.
  777. request_queue_t::iterator iter = find_matching_request(mQueue, request);
  778. if(iter != mQueue.end())
  779. {
  780. LL_DEBUGS("LLMediaDataClient") << "removing matching queued request " << (**iter) << LL_ENDL;
  781. mQueue.erase(iter);
  782. }
  783. else
  784. {
  785. request_set_t::iterator set_iter = find_matching_request(mUnQueuedRequests, request);
  786. if(set_iter != mUnQueuedRequests.end())
  787. {
  788. LL_DEBUGS("LLMediaDataClient") << "removing matching unqueued request " << (**set_iter) << LL_ENDL;
  789. mUnQueuedRequests.erase(set_iter);
  790. }
  791. }
  792. #if 0
  793. // Sadly, this doesn't work. It ends up creating a race condition when the user navigates and then hits the "back" button
  794. // where the navigate-back appears to be spurious and doesn't get broadcast.
  795. if(request->getObject()->isCurrentMediaUrl(request->getFace(), request->getURL()))
  796. {
  797. // This navigate request is trying to send the face to the current URL. Drop it.
  798. LL_DEBUGS("LLMediaDataClient") << "dropping spurious request " << (*request) << LL_ENDL;
  799. }
  800. else
  801. #endif
  802. {
  803. LL_DEBUGS("LLMediaDataClient") << "queueing new request " << (*request) << LL_ENDL;
  804. mQueue.push_back(request);
  805. // Start the timer if not already running
  806. startQueueTimer();
  807. }
  808. }
  809. void LLObjectMediaNavigateClient::navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url)
  810. {
  811. // LL_INFOS("LLMediaDataClient") << "navigate() initiated: " << ll_print_sd(sd_payload) << LL_ENDL;
  812. // Create a get request and put it in the queue.
  813. enqueue(new RequestNavigate(object, this, texture_index, url));
  814. }
  815. LLObjectMediaNavigateClient::RequestNavigate::RequestNavigate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc, U8 texture_index, const std::string &url):
  816. LLMediaDataClient::Request(LLMediaDataClient::Request::NAVIGATE, obj, mdc, (S32)texture_index),
  817. mURL(url)
  818. {
  819. }
  820. LLSD LLObjectMediaNavigateClient::RequestNavigate::getPayload() const
  821. {
  822. LLSD result;
  823. result[LLTextureEntry::OBJECT_ID_KEY] = getID();
  824. result[LLMediaEntry::CURRENT_URL_KEY] = mURL;
  825. result[LLTextureEntry::TEXTURE_INDEX_KEY] = (LLSD::Integer)getFace();
  826. return result;
  827. }
  828. LLMediaDataClient::Responder *LLObjectMediaNavigateClient::RequestNavigate::createResponder()
  829. {
  830. return new LLObjectMediaNavigateClient::Responder(this);
  831. }
  832. /*virtual*/
  833. void LLObjectMediaNavigateClient::Responder::error(U32 status, const std::string& reason)
  834. {
  835. getRequest()->stopTracking();
  836. if(getRequest()->isDead())
  837. {
  838. LL_WARNS("LLMediaDataClient") << "dead request " << *(getRequest()) << LL_ENDL;
  839. return;
  840. }
  841. // Bounce back (unless HTTP_SERVICE_UNAVAILABLE, in which case call base
  842. // class
  843. if (status == HTTP_SERVICE_UNAVAILABLE)
  844. {
  845. LLMediaDataClient::Responder::error(status, reason);
  846. }
  847. else
  848. {
  849. // bounce the face back
  850. LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: http code=" << status << LL_ENDL;
  851. const LLSD &payload = getRequest()->getPayload();
  852. // bounce the face back
  853. getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]);
  854. }
  855. }
  856. /*virtual*/
  857. void LLObjectMediaNavigateClient::Responder::result(const LLSD& content)
  858. {
  859. getRequest()->stopTracking();
  860. if(getRequest()->isDead())
  861. {
  862. LL_WARNS("LLMediaDataClient") << "dead request " << *(getRequest()) << LL_ENDL;
  863. return;
  864. }
  865. LL_INFOS("LLMediaDataClient") << *(getRequest()) << " NAVIGATE returned " << ll_print_sd(content) << LL_ENDL;
  866. if (content.has("error"))
  867. {
  868. const LLSD &error = content["error"];
  869. int error_code = error["code"];
  870. if (ERROR_PERMISSION_DENIED_CODE == error_code)
  871. {
  872. LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Navigation denied: bounce back" << LL_ENDL;
  873. const LLSD &payload = getRequest()->getPayload();
  874. // bounce the face back
  875. getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]);
  876. }
  877. else
  878. {
  879. LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" <<
  880. error["code"].asString() << ": " << error["message"].asString() << LL_ENDL;
  881. }
  882. // XXX Warn user?
  883. }
  884. else
  885. {
  886. // No action required.
  887. LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << " result : " << ll_print_sd(content) << LL_ENDL;
  888. }
  889. }