lllandmarklist.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /**
  2. * @file lllandmarklist.cpp
  3. * @brief Landmark asset list class
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewergpl$
  6. *
  7. * Copyright (c) 2002-2009, 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 "lllandmarklist.h"
  34. #include "llassetstorage.h"
  35. #include "llfilesystem.h"
  36. #include "llnotifications.h"
  37. #include "llmessage.h"
  38. #include "llagent.h"
  39. #include "llappviewer.h" // For gFrameTimeSeconds
  40. #include "llviewerstats.h"
  41. // Global
  42. LLLandmarkList gLandmarkList;
  43. // This limit is mostly arbitrary, but it should be below DEFAULT_QUEUE_SIZE
  44. // pool size, which is 4096, to not overfill the pool if user has more than 4K
  45. // of landmarks, and it should leave some space for other simultaneous asset
  46. // requests.
  47. constexpr S32 MAX_SIMULTANEOUS_REQUESTS = 512;
  48. LLLandmarkList::~LLLandmarkList()
  49. {
  50. for (auto it = mList.begin(), end = mList.end(); it != end; ++it)
  51. {
  52. delete it->second;
  53. }
  54. mList.clear();
  55. }
  56. LLLandmark* LLLandmarkList::getAsset(const LLUUID& asset_id,
  57. loaded_callback_t cb)
  58. {
  59. LLLandmark* landmark = get_ptr_in_map(mList, asset_id);
  60. if (landmark)
  61. {
  62. LLVector3d pos;
  63. if (cb && !landmark->getGlobalPos(pos))
  64. {
  65. // The landmark is not yet completely loaded
  66. mLoadedCallbackMap.emplace(asset_id, cb);
  67. }
  68. return landmark;
  69. }
  70. else if (gAssetStoragep && !mBadList.count(asset_id) &&
  71. !mWaitList.count(asset_id))
  72. {
  73. constexpr F32 rerequest_time = 30.f; // 30 seconds between requests
  74. landmark_requested_list_t::iterator iter =
  75. mRequestedList.find(asset_id);
  76. if (iter == mRequestedList.end() ||
  77. gFrameTimeSeconds - iter->second >= rerequest_time)
  78. {
  79. if (cb)
  80. {
  81. mLoadedCallbackMap.emplace(asset_id, cb);
  82. }
  83. if (mRequestedList.size() > MAX_SIMULTANEOUS_REQUESTS)
  84. {
  85. // Postpone download until queue is not full any more
  86. mWaitList.emplace(asset_id);
  87. return NULL;
  88. }
  89. // Add to mRequestedList before calling getAssetData()
  90. mRequestedList[asset_id] = gFrameTimeSeconds;
  91. // Note that getAssetData() can callback immediately and cleans
  92. // mRequestedList.
  93. gAssetStoragep->getAssetData(asset_id, LLAssetType::AT_LANDMARK,
  94. processGetAssetReply, NULL);
  95. }
  96. }
  97. return NULL;
  98. }
  99. //static
  100. void LLLandmarkList::processGetAssetReply(const LLUUID& asset_id,
  101. LLAssetType::EType type,
  102. void* user_data, S32 status,
  103. LLExtStat ext_status)
  104. {
  105. if (status == 0)
  106. {
  107. LLFileSystem file(asset_id);
  108. S32 file_length = file.getSize();
  109. if (file_length <= 0)
  110. {
  111. llwarns << "Bad cached file length for asset Id " << asset_id
  112. << ": " << file_length << llendl;
  113. gNotifications.add("UnableToLoadLandmark");
  114. gLandmarkList.markBadAsset(asset_id);
  115. }
  116. else
  117. {
  118. std::vector<char> buffer(file_length + 1);
  119. file.read((U8*)&buffer[0], file_length);
  120. buffer[file_length] = 0;
  121. LLLandmark* landmark =
  122. LLLandmark::constructFromString(&buffer[0], buffer.size());
  123. if (landmark)
  124. {
  125. gLandmarkList.mList[asset_id] = landmark;
  126. gLandmarkList.mRequestedList.erase(asset_id);
  127. LLVector3d pos;
  128. if (!landmark->getGlobalPos(pos))
  129. {
  130. LLUUID region_id;
  131. if (landmark->getRegionID(region_id))
  132. {
  133. // NOTE: the callback will be called when we get the
  134. // region handle.
  135. LLLandmark::requestRegionHandle(gMessageSystemp,
  136. gAgent.getRegionHost(),
  137. region_id,
  138. boost::bind(&LLLandmarkList::onRegionHandle,
  139. &gLandmarkList,
  140. asset_id));
  141. }
  142. else
  143. {
  144. gLandmarkList.makeCallbacks(asset_id);
  145. }
  146. }
  147. }
  148. else
  149. {
  150. llwarns << "Corrupted cached file for asset Id " << asset_id
  151. << llendl;
  152. gNotifications.add("UnableToLoadLandmark");
  153. gLandmarkList.markBadAsset(asset_id);
  154. }
  155. }
  156. }
  157. else
  158. {
  159. gViewerStats.incStat(LLViewerStats::ST_DOWNLOAD_FAILED);
  160. if (status == LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE)
  161. {
  162. gNotifications.add("LandmarkMissing");
  163. }
  164. else
  165. {
  166. gNotifications.add("UnableToLoadLandmark");
  167. }
  168. gLandmarkList.markBadAsset(asset_id);
  169. }
  170. // LLAssetStorage::getAssetData() may fire our callback immediately,
  171. // causing a recursion which is suboptimal for very large wait list.
  172. //'scheduling' indicates that we are inside request and should not be
  173. // launching more requests.
  174. static bool scheduling = false;
  175. if (!scheduling && gAssetStoragep && !gLandmarkList.mWaitList.empty())
  176. {
  177. scheduling = true;
  178. while (!gLandmarkList.mWaitList.empty() &&
  179. gLandmarkList.mRequestedList.size() < MAX_SIMULTANEOUS_REQUESTS)
  180. {
  181. // Start a new download from the wait list
  182. uuid_list_t::iterator iter = gLandmarkList.mWaitList.begin();
  183. LLUUID id = *iter;
  184. gLandmarkList.mWaitList.erase(iter);
  185. // Add to mRequestedList before calling getAssetData()
  186. gLandmarkList.mRequestedList[id] = gFrameTimeSeconds;
  187. // Note that getAssetData() can callback immediately and cleans
  188. // mRequestedList.
  189. gAssetStoragep->getAssetData(id, LLAssetType::AT_LANDMARK,
  190. processGetAssetReply, NULL);
  191. }
  192. scheduling = false;
  193. }
  194. }
  195. void LLLandmarkList::eraseCallbacks(const LLUUID& id)
  196. {
  197. loaded_callback_map_t::iterator it;
  198. while ((it = mLoadedCallbackMap.find(id)) != mLoadedCallbackMap.end())
  199. {
  200. mLoadedCallbackMap.erase(it);
  201. }
  202. }
  203. void LLLandmarkList::markBadAsset(const LLUUID& asset_id)
  204. {
  205. mBadList.emplace(asset_id);
  206. mRequestedList.erase(asset_id);
  207. eraseCallbacks(asset_id);
  208. }
  209. bool LLLandmarkList::assetExists(const LLUUID& asset_id)
  210. {
  211. return mList.count(asset_id) != 0 || mBadList.count(asset_id) != 0;
  212. }
  213. bool LLLandmarkList::isAssetInLoadedCallbackMap(const LLUUID& asset_id)
  214. {
  215. return mLoadedCallbackMap.count(asset_id) != 0;
  216. }
  217. void LLLandmarkList::onRegionHandle(const LLUUID& landmark_id)
  218. {
  219. // Calculate the landmark global position. This should succeed since the
  220. // region handle is available.
  221. LLLandmark* landmark = getAsset(landmark_id);
  222. if (!landmark)
  223. {
  224. llwarns << "Got region handle but landmark " << landmark_id
  225. << " is not found." << llendl;
  226. markBadAsset(landmark_id);
  227. return;
  228. }
  229. LLVector3d pos;
  230. if (!landmark->getGlobalPos(pos))
  231. {
  232. llwarns << "Got region handle but the global position for landmark "
  233. << landmark_id << " is still unknown." << llendl;
  234. eraseCallbacks(landmark_id);
  235. return;
  236. }
  237. makeCallbacks(landmark_id);
  238. }
  239. void LLLandmarkList::makeCallbacks(const LLUUID& landmark_id)
  240. {
  241. LLLandmark* landmark = getAsset(landmark_id);
  242. if (!landmark)
  243. {
  244. llwarns << "Landmark " << landmark_id << " not found." << llendl;
  245. }
  246. // Invoke all the callbacks here.
  247. loaded_callback_map_t::iterator it;
  248. while ((it = mLoadedCallbackMap.find(landmark_id)) !=
  249. mLoadedCallbackMap.end())
  250. {
  251. if (landmark)
  252. {
  253. it->second(landmark);
  254. }
  255. mLoadedCallbackMap.erase(it);
  256. }
  257. }