llmediadataclient.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134
  1. /**
  2. * @file llmediadataclient.cpp
  3. * @brief class for queuing up requests for media data
  4. *
  5. * $LicenseInfo:firstyear=2010&license=viewergpl$
  6. *
  7. * Copyright (c) 2010, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "boost/lexical_cast.hpp"
  34. #include "llmediadataclient.h"
  35. #include "llhttpconstants.h"
  36. #include "llmediaentry.h"
  37. #include "llsdutil.h"
  38. #include "lltextureentry.h"
  39. #include "llviewercontrol.h"
  40. #include "llviewerregion.h"
  41. // When making a request:
  42. // - Obtain the "overall interest score" of the object. This would be the sum
  43. // of the impls' interest scores.
  44. // - Put the request onto a queue sorted by this score (highest score at the
  45. // front of the queue).
  46. // - On a timer, once a second, pull off the head of the queue and send the
  47. // request.
  48. // - Any request that gets a 503 still goes through the retry logic
  49. //
  50. // What's up with this queuing code ?
  51. //
  52. // First, a bit of background:
  53. //
  54. // Media on a prim was added into the system in the viewer 2.0 timeframe. In
  55. // order to avoid changing the network format of objects, an unused field in
  56. // the object (the "MediaURL" string) was repurposed to indicate that the
  57. // object had media data, and also hold a sequence number and the UUID of the
  58. // agent who last updated the data. The actual media data for objects is
  59. // accessed via the "ObjectMedia" capability.
  60. // Due to concerns about sim performance, requests to this capability are
  61. // rate-limited to 5 requests every 5 seconds per agent.
  62. // The initial implementation of LLMediaDataClient used a single queue to
  63. // manage requests to the "ObjectMedia" cap. Requests to the cap were queued
  64. // so that objects closer to the avatar were loaded in first, since they were
  65. // most likely to be the ones the media performance manager would load.
  66. // This worked in some cases, but we found that it was possible for a scripted
  67. // object that constantly updated its media data to starve other objects,
  68. // since the same queue contained both requests to load previously unseen media
  69. // data and requests to fetch media data in response to object updates.
  70. // The solution for this we came up with was to have two queues. The sorted
  71. // queue contains requests to fetch media data for objects that don't have it
  72. // yet, and the round-robin queue contains requests to update media data for
  73. // objects that have already completed their initial load. When both queues
  74. // are non-empty, the code ping-pongs between them so that updates cannot
  75. // completely block initial load-in.
  76. // << operators
  77. std::ostream& operator<<(std::ostream& s,
  78. const LLMediaDataClient::request_queue_t& q);
  79. std::ostream& operator<<(std::ostream& s,
  80. const LLMediaDataClient::Request& q);
  81. ///////////////////////////////////////////////////////////////////////////////
  82. // PredicateMatchRequest class
  83. // Unary predicate for matching requests in collections by either the request
  84. // or by UUID
  85. ///////////////////////////////////////////////////////////////////////////////
  86. class PredicateMatchRequest
  87. {
  88. public:
  89. PredicateMatchRequest(const LLMediaDataClient::Request::ptr_t& request,
  90. LLMediaDataClient::Request::Type match_type =
  91. LLMediaDataClient::Request::ANY)
  92. : mRequest(request),
  93. mMatchType(match_type)
  94. {
  95. }
  96. PredicateMatchRequest(const LLUUID& id,
  97. LLMediaDataClient::Request::Type match_type =
  98. LLMediaDataClient::Request::ANY)
  99. : mId(id),
  100. mMatchType(match_type)
  101. {
  102. }
  103. LL_INLINE PredicateMatchRequest(const PredicateMatchRequest& other)
  104. {
  105. mRequest = other.mRequest;
  106. mMatchType = other.mMatchType;
  107. mId = other.mId;
  108. }
  109. LL_INLINE bool operator()(const LLMediaDataClient::Request::ptr_t& test) const
  110. {
  111. if (mRequest)
  112. {
  113. return mRequest->isMatch(test, mMatchType);
  114. }
  115. if (mId.notNull())
  116. {
  117. return mId == test->getID() &&
  118. (mMatchType == LLMediaDataClient::Request::ANY ||
  119. mMatchType == test->getType());
  120. }
  121. return false;
  122. }
  123. private:
  124. LLMediaDataClient::Request::ptr_t mRequest;
  125. LLMediaDataClient::Request::Type mMatchType;
  126. LLUUID mId;
  127. };
  128. template <typename T>
  129. static void mark_dead_and_remove_if(T& c, const PredicateMatchRequest& match)
  130. {
  131. for (typename T::iterator it = c.begin(); it != c.end(); )
  132. {
  133. if (match(*it))
  134. {
  135. (*it)->markDead();
  136. it = c.erase(it);
  137. }
  138. else
  139. {
  140. ++it;
  141. }
  142. }
  143. }
  144. ///////////////////////////////////////////////////////////////////////////////
  145. // LLMediaDataClient class
  146. ///////////////////////////////////////////////////////////////////////////////
  147. LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay,
  148. F32 retry_timer_delay,
  149. U32 max_retries,
  150. U32 max_sorted_queue_size,
  151. U32 max_round_robin_queue_size)
  152. : mQueueTimerDelay(queue_timer_delay),
  153. mRetryTimerDelay(retry_timer_delay),
  154. mMaxNumRetries(max_retries),
  155. mMaxSortedQueueSize(max_sorted_queue_size),
  156. mMaxRoundRobinQueueSize(max_round_robin_queue_size),
  157. mQueueTimerIsRunning(false),
  158. mHttpRequest(new LLCore::HttpRequest()),
  159. mHttpHeaders(new LLCore::HttpHeaders()),
  160. mHttpOpts(new LLCore::HttpOptions()),
  161. mHttpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID)
  162. {
  163. }
  164. //virtual
  165. LLMediaDataClient::~LLMediaDataClient()
  166. {
  167. stopQueueTimer();
  168. }
  169. bool LLMediaDataClient::isEmpty() const
  170. {
  171. return mQueue.empty();
  172. }
  173. bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t& object)
  174. {
  175. PredicateMatchRequest upred(object->getID());
  176. return std::find_if(mQueue.begin(), mQueue.end(), upred) != mQueue.end() ||
  177. std::find_if(mUnQueuedRequests.begin(), mUnQueuedRequests.end(),
  178. upred) != mUnQueuedRequests.end();
  179. }
  180. void LLMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t& object)
  181. {
  182. LL_DEBUGS("MediaDataClient") << "removing requests matching ID "
  183. << object->getID() << LL_ENDL;
  184. PredicateMatchRequest upred(object->getID());
  185. mark_dead_and_remove_if(mQueue, upred);
  186. mark_dead_and_remove_if(mUnQueuedRequests, upred);
  187. }
  188. void LLMediaDataClient::startQueueTimer()
  189. {
  190. if (!mQueueTimerIsRunning)
  191. {
  192. LL_DEBUGS("MediaDataClient") << "starting queue timer (delay="
  193. << mQueueTimerDelay << " seconds)"
  194. << LL_ENDL;
  195. // LLEventTimer automagically takes care of the lifetime of this object
  196. new QueueTimer(mQueueTimerDelay, this);
  197. }
  198. else
  199. {
  200. LL_DEBUGS("MediaDataClient") << "Queue timer is already running"
  201. << LL_ENDL;
  202. }
  203. }
  204. bool LLMediaDataClient::processQueueTimer()
  205. {
  206. if (isDoneProcessing())
  207. {
  208. return true;
  209. }
  210. LL_DEBUGS("MediaDataClient") << "QueueTimer::tick() started, queue size is: "
  211. << mQueue.size() << LL_ENDL;
  212. serviceQueue();
  213. serviceHttp();
  214. LL_DEBUGS("MediaDataClient") << "QueueTimer::tick() finished, queue size is: "
  215. << mQueue.size() << LL_ENDL;
  216. return isDoneProcessing();
  217. }
  218. LLMediaDataClient::Request::ptr_t LLMediaDataClient::dequeue()
  219. {
  220. Request::ptr_t request;
  221. request_queue_t* queue_p = getQueue();
  222. if (queue_p->empty())
  223. {
  224. LL_DEBUGS("MediaDataClient") << "Queue empty: " << *queue_p
  225. << LL_ENDL;
  226. }
  227. else
  228. {
  229. request = queue_p->front();
  230. if (canServiceRequest(request))
  231. {
  232. // We will be returning this request, so remove it from the queue.
  233. queue_p->pop_front();
  234. }
  235. else
  236. {
  237. // Do not return this request: it is not ready to be serviced.
  238. request.reset();
  239. }
  240. }
  241. return request;
  242. }
  243. void LLMediaDataClient::pushBack(Request::ptr_t request)
  244. {
  245. request_queue_t* queue_p = getQueue();
  246. queue_p->push_front(request);
  247. }
  248. void LLMediaDataClient::trackRequest(Request::ptr_t request)
  249. {
  250. request_set_t::iterator iter = mUnQueuedRequests.find(request);
  251. if (iter != mUnQueuedRequests.end())
  252. {
  253. llwarns << "Tracking already tracked request: " << *request << llendl;
  254. }
  255. else
  256. {
  257. mUnQueuedRequests.insert(request);
  258. }
  259. }
  260. void LLMediaDataClient::stopTrackingRequest(Request::ptr_t request)
  261. {
  262. request_set_t::iterator iter = mUnQueuedRequests.find(request);
  263. if (iter != mUnQueuedRequests.end())
  264. {
  265. mUnQueuedRequests.erase(iter);
  266. }
  267. else
  268. {
  269. llwarns << "Removing an untracked request: " << *request << llendl;
  270. }
  271. }
  272. // Peels one off of the items from the queue and executes it
  273. void LLMediaDataClient::serviceQueue()
  274. {
  275. Request::ptr_t request;
  276. bool dead_request;
  277. do
  278. {
  279. request = dequeue();
  280. if (!request)
  281. {
  282. // Queue is empty.
  283. return;
  284. }
  285. dead_request = request->isDead();
  286. if (dead_request)
  287. {
  288. llinfos << "Skipping dead request " << *request << llendl;
  289. }
  290. }
  291. while (dead_request);
  292. // Try to send the HTTP message to the cap url
  293. const std::string& url = request->getCapability();
  294. if (!url.empty())
  295. {
  296. const LLSD& sd_payload = request->getPayload();
  297. llinfos << "Sending request for " << *request << llendl;
  298. // Add this request to the non-queued tracking list
  299. trackRequest(request);
  300. // And make the post
  301. LLCore::HttpHandler::ptr_t handler = request->createHandler();
  302. LLCore::HttpHandle handle =
  303. LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, mHttpPolicy, url,
  304. sd_payload, mHttpOpts,
  305. mHttpHeaders, handler);
  306. if (handle == LLCORE_HTTP_HANDLE_INVALID)
  307. {
  308. LLCore::HttpStatus status = mHttpRequest->getStatus();
  309. llwarns << "Failed POST request to: " << url << " - Reason: "
  310. << status.toString() << llendl;
  311. }
  312. }
  313. // Cap url does not exist.
  314. else if (request->getRetryCount() < mMaxNumRetries)
  315. {
  316. llwarns << "Could not send request " << *request
  317. << " (empty cap url), will retry." << llendl;
  318. // Put this request back at the head of its queue, and retry next time
  319. // the queue timer fires.
  320. request->incRetryCount();
  321. pushBack(request);
  322. }
  323. else
  324. {
  325. // This request has exceeded its maximum retry count. It will be
  326. // dropped.
  327. llwarns << "Could not send request " << *request << " for "
  328. << mMaxNumRetries << " tries, dropping request." << llendl;
  329. }
  330. }
  331. // Dumps the queue
  332. std::ostream& operator<<(std::ostream& s,
  333. const LLMediaDataClient::request_queue_t& q)
  334. {
  335. S32 i = 0;
  336. LLMediaDataClient::request_queue_t::const_iterator iter = q.begin();
  337. LLMediaDataClient::request_queue_t::const_iterator end = q.end();
  338. while (iter != end)
  339. {
  340. s << "\t" << i++ << "]: " << (*iter)->getID().asString() << "("
  341. << (*iter++)->getObject()->getMediaInterest() << ")";
  342. }
  343. return s;
  344. }
  345. ///////////////////////////////////////////////////////////////////////////////
  346. // LLMediaDataClient::QueueTimer sub-class
  347. // Queue of LLMediaDataClientObject smart pointers to request media for.
  348. ///////////////////////////////////////////////////////////////////////////////
  349. LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient* mdc)
  350. : LLEventTimer(time),
  351. mMDC(mdc)
  352. {
  353. mMDC->setIsRunning(true);
  354. }
  355. //virtual
  356. bool LLMediaDataClient::QueueTimer::tick()
  357. {
  358. bool result = true;
  359. if (mMDC.notNull())
  360. {
  361. result = mMDC->processQueueTimer();
  362. if (result)
  363. {
  364. // This timer will not fire again.
  365. mMDC->setIsRunning(false);
  366. mMDC = NULL;
  367. }
  368. }
  369. return result;
  370. }
  371. ///////////////////////////////////////////////////////////////////////////////
  372. // LLMediaDataClient::RetryTimer::RetryTimer sub-class
  373. ///////////////////////////////////////////////////////////////////////////////
  374. LLMediaDataClient::RetryTimer::RetryTimer(F32 time, Request::ptr_t request)
  375. : LLEventTimer(time),
  376. mRequest(request)
  377. {
  378. mRequest->startTracking();
  379. }
  380. //virtual
  381. bool LLMediaDataClient::RetryTimer::tick()
  382. {
  383. mRequest->stopTracking();
  384. if (mRequest->isDead())
  385. {
  386. llinfos << "RetryTimer fired for dead request: " << *mRequest
  387. << ", aborting." << llendl;
  388. }
  389. else
  390. {
  391. llinfos << "RetryTimer fired for: " << *mRequest << ", retrying."
  392. << llendl;
  393. mRequest->reEnqueue();
  394. }
  395. // Release the ref to the request.
  396. mRequest.reset();
  397. // Do not fire again
  398. return true;
  399. }
  400. ///////////////////////////////////////////////////////////////////////////////
  401. // LLMediaDataClient::Request sub-class
  402. ///////////////////////////////////////////////////////////////////////////////
  403. //static
  404. U32 LLMediaDataClient::Request::sNum = 0;
  405. LLMediaDataClient::Request::Request(Type in_type, LLMediaDataClientObject* obj,
  406. LLMediaDataClient* mdc, S32 face)
  407. : mType(in_type),
  408. mObject(obj),
  409. mNum(++sNum),
  410. mRetryCount(0),
  411. mMDC(mdc),
  412. mScore(0.0),
  413. mFace(face)
  414. {
  415. mObjectID = mObject->getID();
  416. }
  417. const char* LLMediaDataClient::Request::getCapName() const
  418. {
  419. return mMDC ? mMDC->getCapabilityName() : "";
  420. }
  421. const std::string& LLMediaDataClient::Request::getCapability() const
  422. {
  423. return mMDC ? getObject()->getCapabilityUrl(getCapName())
  424. : LLStringUtil::null;
  425. }
  426. const char* LLMediaDataClient::Request::getTypeAsString() const
  427. {
  428. Type t = getType();
  429. switch (t)
  430. {
  431. case GET:
  432. return "GET";
  433. case UPDATE:
  434. return "UPDATE";
  435. case NAVIGATE:
  436. return "NAVIGATE";
  437. case ANY:
  438. return "ANY";
  439. }
  440. return "";
  441. }
  442. void LLMediaDataClient::Request::reEnqueue()
  443. {
  444. if (mMDC)
  445. {
  446. mMDC->enqueue(shared_from_this());
  447. }
  448. }
  449. F32 LLMediaDataClient::Request::getRetryTimerDelay() const
  450. {
  451. return mMDC ? mMDC->mRetryTimerDelay : 0.f;
  452. }
  453. U32 LLMediaDataClient::Request::getMaxNumRetries() const
  454. {
  455. return mMDC ? mMDC->mMaxNumRetries : 0U;
  456. }
  457. void LLMediaDataClient::Request::updateScore()
  458. {
  459. F64 tmp = mObject->getMediaInterest();
  460. if (tmp != mScore)
  461. {
  462. LL_DEBUGS("MediaDataClient") << "Score for " << mObject->getID()
  463. << " changed from " << mScore << " to "
  464. << tmp << LL_ENDL;
  465. mScore = tmp;
  466. }
  467. }
  468. void LLMediaDataClient::Request::markDead()
  469. {
  470. mMDC = NULL;
  471. }
  472. bool LLMediaDataClient::Request::isDead()
  473. {
  474. return !mMDC || mObject->isDead();
  475. }
  476. void LLMediaDataClient::Request::startTracking()
  477. {
  478. if (mMDC)
  479. {
  480. mMDC->trackRequest(shared_from_this());
  481. }
  482. }
  483. void LLMediaDataClient::Request::stopTracking()
  484. {
  485. if (mMDC)
  486. {
  487. mMDC->stopTrackingRequest(shared_from_this());
  488. }
  489. }
  490. std::ostream& operator<<(std::ostream& s, const LLMediaDataClient::Request& r)
  491. {
  492. s << "request: num=" << r.getNum() << " type=" << r.getTypeAsString()
  493. << " ID=" << r.getID() << " face=" << r.getFace() << " #retries="
  494. << r.getRetryCount();
  495. return s;
  496. }
  497. ///////////////////////////////////////////////////////////////////////////////
  498. // LLMediaDataClient::Handler
  499. ///////////////////////////////////////////////////////////////////////////////
  500. LLMediaDataClient::Handler::Handler(const Request::ptr_t& request)
  501. : mRequest(request)
  502. {
  503. }
  504. //virtual
  505. void LLMediaDataClient::Handler::onSuccess(LLCore::HttpResponse* response,
  506. const LLSD& content)
  507. {
  508. mRequest->stopTracking();
  509. if (mRequest->isDead())
  510. {
  511. llwarns << "dead request " << *mRequest << llendl;
  512. }
  513. else
  514. {
  515. LL_DEBUGS("MediaDataClient") << *mRequest << " - Result: " << content
  516. << LL_ENDL;
  517. }
  518. }
  519. //virtual
  520. void LLMediaDataClient::Handler::onFailure(LLCore::HttpResponse* response,
  521. LLCore::HttpStatus status)
  522. {
  523. mRequest->stopTracking();
  524. if (status == gStatusUnavailable)
  525. {
  526. F32 retry_timeout = mRequest->getRetryTimerDelay();
  527. mRequest->incRetryCount();
  528. if (mRequest->getRetryCount() < mRequest->getMaxNumRetries())
  529. {
  530. llinfos << *mRequest << " got SERVICE_UNAVAILABLE... Retrying in "
  531. << retry_timeout << " seconds" << llendl;
  532. // Start timer (instances are automagically tracked by
  533. // InstanceTracker<> and LLEventTimer)
  534. new RetryTimer(F32(retry_timeout), mRequest);
  535. }
  536. else
  537. {
  538. llinfos << *mRequest << " got SERVICE_UNAVAILABLE... Retry count "
  539. << mRequest->getRetryCount() << " exceeds "
  540. << mRequest->getMaxNumRetries() << ", not retrying"
  541. << llendl;
  542. }
  543. }
  544. else
  545. {
  546. llwarns << *mRequest << " - HTTP error: " << status.toString()
  547. << llendl;
  548. }
  549. }
  550. ///////////////////////////////////////////////////////////////////////////////
  551. // LLObjectMediaDataClient sub-class
  552. // Sub-class of LLMediaDataClient for the ObjectMedia cap
  553. ///////////////////////////////////////////////////////////////////////////////
  554. void LLObjectMediaDataClient::fetchMedia(LLMediaDataClientObject* object)
  555. {
  556. // Create a get request and put it in the queue.
  557. enqueue(Request::ptr_t(new RequestGet(object, this)));
  558. }
  559. const char* LLObjectMediaDataClient::getCapabilityName() const
  560. {
  561. return "ObjectMedia";
  562. }
  563. LLObjectMediaDataClient::request_queue_t* LLObjectMediaDataClient::getQueue()
  564. {
  565. return mCurrentQueueIsTheSortedQueue ? &mQueue : &mRoundRobinQueue;
  566. }
  567. void LLObjectMediaDataClient::sortQueue()
  568. {
  569. if (!mQueue.empty())
  570. {
  571. // Score all elements in the sorted queue.
  572. for (request_queue_t::iterator iter = mQueue.begin(),
  573. end = mQueue.end();
  574. iter != end; ++iter)
  575. {
  576. (*iter)->updateScore();
  577. }
  578. // Re-sort the list...
  579. mQueue.sort(compareRequestScores);
  580. // ...then cull items over the max
  581. U32 size = mQueue.size();
  582. if (size > mMaxSortedQueueSize)
  583. {
  584. U32 num_to_cull = size - mMaxSortedQueueSize;
  585. llwarns_once << "Sorted queue maxed out, culling " << num_to_cull
  586. << " items" << LL_ENDL;
  587. while (num_to_cull-- > 0)
  588. {
  589. mQueue.back()->markDead();
  590. mQueue.pop_back();
  591. }
  592. }
  593. }
  594. }
  595. //static
  596. bool LLObjectMediaDataClient::compareRequestScores(const Request::ptr_t& o1,
  597. const Request::ptr_t& o2)
  598. {
  599. if (!o2)
  600. {
  601. return true;
  602. }
  603. if (!o1)
  604. {
  605. return false;
  606. }
  607. return o1->getScore() > o2->getScore();
  608. }
  609. void LLObjectMediaDataClient::enqueue(Request::ptr_t request)
  610. {
  611. if (!request) return;
  612. static LLCachedControl<bool> media_enabled(gSavedSettings,
  613. "EnableStreamingMedia");
  614. if (!media_enabled)
  615. {
  616. LL_DEBUGS("MediaDataClient") << "Media disabled: ignoring request "
  617. << *request << LL_ENDL;
  618. return;
  619. }
  620. if (request->isDead())
  621. {
  622. LL_DEBUGS("MediaDataClient") << "Not queuing dead request "
  623. << *request << LL_ENDL;
  624. return;
  625. }
  626. // Invariants: new requests always go into the sorted queue.
  627. bool is_new = request->isNew();
  628. if (!is_new && request->getType() == Request::GET)
  629. {
  630. // For GET requests that are not new, if a matching request is already
  631. // in the round robin queue, in flight, or being retried, leave it at
  632. // its current position.
  633. PredicateMatchRequest upred(request->getID(), Request::GET);
  634. request_queue_t::iterator iter = std::find_if(mRoundRobinQueue.begin(),
  635. mRoundRobinQueue.end(),
  636. upred);
  637. request_set_t::iterator iter2 = std::find_if(mUnQueuedRequests.begin(),
  638. mUnQueuedRequests.end(),
  639. upred);
  640. if (iter != mRoundRobinQueue.end() || iter2 != mUnQueuedRequests.end())
  641. {
  642. LL_DEBUGS("MediaDataClient") << "ALREADY THERE: NOT Queuing request for "
  643. << *request << LL_ENDL;
  644. return;
  645. }
  646. }
  647. // *TODO: should an UPDATE cause pending GET requests for the same object
  648. // to be removed from the queue ? If the update will cause an object
  649. // update message to be sent out at some point in the future, then yes.
  650. // Remove any existing requests of this type for this object
  651. PredicateMatchRequest upred(request->getID(), request->getType());
  652. mark_dead_and_remove_if(mQueue, upred);
  653. mark_dead_and_remove_if(mRoundRobinQueue, upred);
  654. mark_dead_and_remove_if(mUnQueuedRequests, upred);
  655. if (is_new)
  656. {
  657. LL_DEBUGS("MediaDataClient") << "Queuing SORTED request for "
  658. << *request << LL_ENDL;
  659. mQueue.push_back(request);
  660. }
  661. else
  662. {
  663. if (mRoundRobinQueue.size() > mMaxRoundRobinQueueSize)
  664. {
  665. llwarns_sparse << "Round Robin queue maxed out !" << llendl;
  666. //LL_DEBUGS("MediaDataClient") << "Not queuing " << *request << LL_ENDL;
  667. return;
  668. }
  669. LL_DEBUGS("MediaDataClient") << "Queuing RR request for " << *request
  670. << LL_ENDL;
  671. // Push the request on the pending queue
  672. mRoundRobinQueue.push_back(request);
  673. }
  674. // Start the timer if not already running
  675. startQueueTimer();
  676. }
  677. bool LLObjectMediaDataClient::canServiceRequest(Request::ptr_t request)
  678. {
  679. if (mCurrentQueueIsTheSortedQueue &&
  680. !request->getObject()->isInterestingEnough())
  681. {
  682. LL_DEBUGS("MediaDataClient") << "Not fetching " << *request
  683. << ": not interesting enough."
  684. << LL_ENDL;
  685. return false;
  686. }
  687. return true;
  688. }
  689. void LLObjectMediaDataClient::swapCurrentQueue()
  690. {
  691. // Swap
  692. mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue;
  693. // If it is empty, swap back
  694. if (getQueue()->empty())
  695. {
  696. mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue;
  697. }
  698. }
  699. bool LLObjectMediaDataClient::isEmpty() const
  700. {
  701. return mQueue.empty() && mRoundRobinQueue.empty();
  702. }
  703. bool LLObjectMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t& object)
  704. {
  705. // First, call parent impl.
  706. if (LLMediaDataClient::isInQueue(object))
  707. {
  708. return true;
  709. }
  710. return std::find_if(mRoundRobinQueue.begin(), mRoundRobinQueue.end(),
  711. PredicateMatchRequest(object->getID())) !=
  712. mRoundRobinQueue.end();
  713. }
  714. void LLObjectMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t& object)
  715. {
  716. // First, call parent impl.
  717. LLMediaDataClient::removeFromQueue(object);
  718. mark_dead_and_remove_if(mRoundRobinQueue,
  719. PredicateMatchRequest(object->getID()));
  720. }
  721. bool LLObjectMediaDataClient::processQueueTimer()
  722. {
  723. if (isDoneProcessing())
  724. {
  725. return true;
  726. }
  727. LL_DEBUGS("MediaDataClient") << "Started, SORTED queue size is: "
  728. << mQueue.size() << ", RR queue size is: "
  729. << mRoundRobinQueue.size() << LL_ENDL;
  730. #if 0
  731. purgeDeadRequests();
  732. #endif
  733. sortQueue();
  734. serviceQueue();
  735. serviceHttp();
  736. swapCurrentQueue();
  737. LL_DEBUGS("MediaDataClient") << "finished, SORTED queue size is: "
  738. << mQueue.size() << ", RR queue size is: "
  739. << mRoundRobinQueue.size() << LL_ENDL;
  740. return isDoneProcessing();
  741. }
  742. LLObjectMediaDataClient::RequestGet::RequestGet(LLMediaDataClientObject* obj,
  743. LLMediaDataClient* mdc)
  744. : LLMediaDataClient::Request(LLMediaDataClient::Request::GET, obj, mdc)
  745. {
  746. }
  747. LLSD LLObjectMediaDataClient::RequestGet::getPayload() const
  748. {
  749. LLSD result;
  750. result["verb"] = "GET";
  751. result[LLTextureEntry::OBJECT_ID_KEY] = mObject->getID();
  752. return result;
  753. }
  754. LLCore::HttpHandler::ptr_t LLObjectMediaDataClient::RequestGet::createHandler()
  755. {
  756. return LLCore::HttpHandler::ptr_t(new LLObjectMediaDataClient::Handler(shared_from_this()));
  757. }
  758. void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject* object)
  759. {
  760. // Create an update request and put it in the queue.
  761. enqueue(Request::ptr_t(new RequestUpdate(object, this)));
  762. }
  763. LLObjectMediaDataClient::RequestUpdate::RequestUpdate(LLMediaDataClientObject* obj,
  764. LLMediaDataClient* mdc)
  765. : LLMediaDataClient::Request(LLMediaDataClient::Request::UPDATE, obj, mdc)
  766. {
  767. }
  768. LLSD LLObjectMediaDataClient::RequestUpdate::getPayload() const
  769. {
  770. LLSD result;
  771. result["verb"] = "UPDATE";
  772. result[LLTextureEntry::OBJECT_ID_KEY] = mObject->getID();
  773. LLSD object_media_data;
  774. for (S32 i = 0, count = mObject->getMediaDataCount(); i < count ; ++i)
  775. {
  776. object_media_data.append(mObject->getMediaDataLLSD(i));
  777. }
  778. result[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data;
  779. return result;
  780. }
  781. LLCore::HttpHandler::ptr_t LLObjectMediaDataClient::RequestUpdate::createHandler()
  782. {
  783. // This just uses the base class' handler.
  784. return LLCore::HttpHandler::ptr_t(new LLMediaDataClient::Handler(shared_from_this()));
  785. }
  786. //virtual
  787. void LLObjectMediaDataClient::Handler::onSuccess(LLCore::HttpResponse* response,
  788. const LLSD& content)
  789. {
  790. LLMediaDataClient::Handler::onSuccess(response, content);
  791. if (getRequest()->isDead())
  792. {
  793. // warning emitted from base method.
  794. return;
  795. }
  796. if (!content.isMap())
  797. {
  798. onFailure(response,
  799. LLCore::HttpStatus(HTTP_INTERNAL_ERROR,
  800. "Malformed response contents"));
  801. return;
  802. }
  803. // This handler is only used for GET requests, not UPDATE.
  804. LL_DEBUGS("MediaDataClient") << *(getRequest()) << " GET returned: "
  805. << content << LL_ENDL;
  806. // Look for an error
  807. if (content.has("error"))
  808. {
  809. const LLSD& error = content["error"];
  810. llwarns << *(getRequest())
  811. << " Error getting media data for object: code = "
  812. << error["code"].asString() << ": "
  813. << error["message"].asString() << llendl;
  814. // *TODO: Warn user ?
  815. return;
  816. }
  817. // Check the data
  818. const LLUUID& object_id = content[LLTextureEntry::OBJECT_ID_KEY];
  819. if (object_id != getRequest()->getObject()->getID())
  820. {
  821. // NOT good, wrong object id !
  822. llwarns << *(getRequest()) << " DROPPING response with wrong object id ("
  823. << object_id << ")" << llendl;
  824. return;
  825. }
  826. // Otherwise, update with object media data
  827. getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY],
  828. content[LLTextureEntry::MEDIA_VERSION_KEY]);
  829. }
  830. ///////////////////////////////////////////////////////////////////////////////
  831. // LLObjectMediaNavigateClient class
  832. // Subclass of LLMediaDataClient for the ObjectMediaNavigate cap
  833. ///////////////////////////////////////////////////////////////////////////////
  834. const char* LLObjectMediaNavigateClient::getCapabilityName() const
  835. {
  836. return "ObjectMediaNavigate";
  837. }
  838. void LLObjectMediaNavigateClient::enqueue(Request::ptr_t request)
  839. {
  840. if (!request) return;
  841. static LLCachedControl<bool> media_enabled(gSavedSettings,
  842. "EnableStreamingMedia");
  843. if (!media_enabled)
  844. {
  845. LL_DEBUGS("MediaDataClient") << "Media disabled: ignoring request "
  846. << *request << LL_ENDL;
  847. return;
  848. }
  849. if (request->isDead())
  850. {
  851. LL_DEBUGS("MediaDataClient") << "Not queuing dead request "
  852. << *request << LL_ENDL;
  853. return;
  854. }
  855. PredicateMatchRequest upred(request);
  856. // If there is already a matching request in the queue, remove it.
  857. request_queue_t::iterator iter = std::find_if(mQueue.begin(), mQueue.end(),
  858. upred);
  859. if (iter != mQueue.end())
  860. {
  861. LL_DEBUGS("MediaDataClient") << "Removing matching queued request "
  862. << **iter << LL_ENDL;
  863. mQueue.erase(iter);
  864. }
  865. else
  866. {
  867. request_set_t::iterator set_iter = std::find_if(mUnQueuedRequests.begin(),
  868. mUnQueuedRequests.end(),
  869. upred);
  870. if (set_iter != mUnQueuedRequests.end())
  871. {
  872. LL_DEBUGS("MediaDataClient") << "Removing matching unqueued request "
  873. << **set_iter << LL_ENDL;
  874. mUnQueuedRequests.erase(set_iter);
  875. }
  876. }
  877. #if 0
  878. // Sadly, this does not work. It ends up creating a race condition when the
  879. // user navigates and then hits the "back" button where the navigate-back
  880. // appears to be spurious and does not get broadcast.
  881. if (request->getObject()->isCurrentMediaUrl(request->getFace(),
  882. request->getURL()))
  883. {
  884. // This navigate request is trying to send the face to the current URL.
  885. // Drop it.
  886. LL_DEBUGS("MediaDataClient") << "Dropping spurious request "
  887. << *request << LL_ENDL;
  888. }
  889. else
  890. #endif
  891. {
  892. LL_DEBUGS("MediaDataClient") << "queuing new request " << (*request)
  893. << LL_ENDL;
  894. mQueue.push_back(request);
  895. // Start the timer if not already running
  896. startQueueTimer();
  897. }
  898. }
  899. void LLObjectMediaNavigateClient::navigate(LLMediaDataClientObject* object,
  900. U8 texture_index,
  901. const std::string& url)
  902. {
  903. // Create a get request and put it in the queue.
  904. enqueue(Request::ptr_t(new RequestNavigate(object, this, texture_index,
  905. url)));
  906. }
  907. LLObjectMediaNavigateClient::RequestNavigate::RequestNavigate(LLMediaDataClientObject* obj,
  908. LLMediaDataClient* mdc,
  909. U8 texture_index,
  910. const std::string& url)
  911. : LLMediaDataClient::Request(LLMediaDataClient::Request::NAVIGATE, obj, mdc,
  912. (S32)texture_index),
  913. mURL(url)
  914. {
  915. }
  916. LLSD LLObjectMediaNavigateClient::RequestNavigate::getPayload() const
  917. {
  918. LLSD result;
  919. result[LLTextureEntry::OBJECT_ID_KEY] = getID();
  920. result[LLMediaEntry::CURRENT_URL_KEY] = mURL;
  921. result[LLTextureEntry::TEXTURE_INDEX_KEY] = (LLSD::Integer)getFace();
  922. return result;
  923. }
  924. LLCore::HttpHandler::ptr_t LLObjectMediaNavigateClient::RequestNavigate::createHandler()
  925. {
  926. return LLCore::HttpHandler::ptr_t(new LLObjectMediaNavigateClient::Handler(shared_from_this()));
  927. }
  928. //virtual
  929. void LLObjectMediaNavigateClient::Handler::onSuccess(LLCore::HttpResponse* response,
  930. const LLSD& content)
  931. {
  932. LLMediaDataClient::Handler::onSuccess(response, content);
  933. if (getRequest()->isDead())
  934. {
  935. // Warning emitted from base method.
  936. return;
  937. }
  938. llinfos << *(getRequest()) << " - NAVIGATE returned: " << content
  939. << llendl;
  940. if (content.has("error"))
  941. {
  942. const LLSD& error = content["error"];
  943. S32 error_code = error["code"];
  944. if (error_code == ERROR_PERMISSION_DENIED_CODE)
  945. {
  946. mediaNavigateBounceBack();
  947. }
  948. else
  949. {
  950. llwarns << *(getRequest()) << " Error navigating: code = "
  951. << error["code"].asString() << ": "
  952. << error["message"].asString() << llendl;
  953. }
  954. // *TODO: Warn user ?
  955. }
  956. else
  957. {
  958. // No action required.
  959. LL_DEBUGS("MediaDataClient") << *(getRequest()) << " - " << content
  960. << LL_ENDL;
  961. }
  962. }
  963. //virtual
  964. void LLObjectMediaNavigateClient::Handler::onFailure(LLCore::HttpResponse* response,
  965. LLCore::HttpStatus status)
  966. {
  967. LLMediaDataClient::Handler::onFailure(response, status);
  968. if (getRequest()->isDead())
  969. {
  970. // Warning emitted from base method.
  971. return;
  972. }
  973. if (status != gStatusUnavailable)
  974. {
  975. mediaNavigateBounceBack();
  976. }
  977. }
  978. void LLObjectMediaNavigateClient::Handler::mediaNavigateBounceBack()
  979. {
  980. llwarns << *(getRequest()) << " Navigation denied: bounce back" << llendl;
  981. const LLSD& payload = getRequest()->getPayload();
  982. // Bounce the face back
  983. getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]);
  984. }