PageRenderTime 108ms CodeModel.GetById 27ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/newview/llviewerassetstorage.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 389 lines | 262 code | 45 blank | 82 comment | 24 complexity | dd42ff814c46f7bccc1601cb476cb8bb MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llviewerassetstorage.cpp
  3. * @brief Subclass capable of loading asset data to/from an external source.
  4. *
  5. * $LicenseInfo:firstyear=2003&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 "llviewerassetstorage.h"
  28. #include "llvfile.h"
  29. #include "llvfs.h"
  30. #include "message.h"
  31. #include "llagent.h"
  32. #include "lltransfersourceasset.h"
  33. #include "lltransfertargetvfile.h"
  34. #include "llviewerassetstats.h"
  35. ///----------------------------------------------------------------------------
  36. /// LLViewerAssetRequest
  37. ///----------------------------------------------------------------------------
  38. /**
  39. * @brief Local class to encapsulate asset fetch requests with a timestamp.
  40. *
  41. * Derived from the common LLAssetRequest class, this is currently used
  42. * only for fetch/get operations and its only function is to wrap remote
  43. * asset fetch requests so that they can be timed.
  44. */
  45. class LLViewerAssetRequest : public LLAssetRequest
  46. {
  47. public:
  48. LLViewerAssetRequest(const LLUUID &uuid, const LLAssetType::EType type)
  49. : LLAssetRequest(uuid, type),
  50. mMetricsStartTime(0)
  51. {
  52. }
  53. LLViewerAssetRequest & operator=(const LLViewerAssetRequest &); // Not defined
  54. // Default assignment operator valid
  55. // virtual
  56. ~LLViewerAssetRequest()
  57. {
  58. recordMetrics();
  59. }
  60. protected:
  61. void recordMetrics()
  62. {
  63. if (mMetricsStartTime)
  64. {
  65. // Okay, it appears this request was used for useful things. Record
  66. // the expected dequeue and duration of request processing.
  67. LLViewerAssetStatsFF::record_dequeue_main(mType, false, false);
  68. LLViewerAssetStatsFF::record_response_main(mType, false, false,
  69. (LLViewerAssetStatsFF::get_timestamp()
  70. - mMetricsStartTime));
  71. mMetricsStartTime = 0;
  72. }
  73. }
  74. public:
  75. LLViewerAssetStats::duration_t mMetricsStartTime;
  76. };
  77. ///----------------------------------------------------------------------------
  78. /// LLViewerAssetStorage
  79. ///----------------------------------------------------------------------------
  80. LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
  81. LLVFS *vfs, LLVFS *static_vfs,
  82. const LLHost &upstream_host)
  83. : LLAssetStorage(msg, xfer, vfs, static_vfs, upstream_host)
  84. {
  85. }
  86. LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
  87. LLVFS *vfs, LLVFS *static_vfs)
  88. : LLAssetStorage(msg, xfer, vfs, static_vfs)
  89. {
  90. }
  91. // virtual
  92. void LLViewerAssetStorage::storeAssetData(
  93. const LLTransactionID& tid,
  94. LLAssetType::EType asset_type,
  95. LLStoreAssetCallback callback,
  96. void* user_data,
  97. bool temp_file,
  98. bool is_priority,
  99. bool store_local,
  100. bool user_waiting,
  101. F64 timeout)
  102. {
  103. LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
  104. LL_DEBUGS("AssetStorage") << "LLViewerAssetStorage::storeAssetData (legacy) " << tid << ":" << LLAssetType::lookup(asset_type)
  105. << " ASSET_ID: " << asset_id << llendl;
  106. if (mUpstreamHost.isOk())
  107. {
  108. if (mVFS->getExists(asset_id, asset_type))
  109. {
  110. // Pack data into this packet if we can fit it.
  111. U8 buffer[MTUBYTES];
  112. buffer[0] = 0;
  113. LLVFile vfile(mVFS, asset_id, asset_type, LLVFile::READ);
  114. S32 asset_size = vfile.getSize();
  115. LLAssetRequest *req = new LLAssetRequest(asset_id, asset_type);
  116. req->mUpCallback = callback;
  117. req->mUserData = user_data;
  118. if (asset_size < 1)
  119. {
  120. // This can happen if there's a bug in our code or if the VFS has been corrupted.
  121. llwarns << "LLViewerAssetStorage::storeAssetData() Data _should_ already be in the VFS, but it's not! " << asset_id << llendl;
  122. // LLAssetStorage metric: Zero size VFS
  123. reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" );
  124. delete req;
  125. if (callback)
  126. {
  127. callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_FAILED, LL_EXSTAT_VFS_CORRUPT);
  128. }
  129. return;
  130. }
  131. else
  132. {
  133. // LLAssetStorage metric: Successful Request
  134. S32 size = mVFS->getSize(asset_id, asset_type);
  135. const char *message = "Added to upload queue";
  136. reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, size, MR_OKAY, __FILE__, __LINE__, message );
  137. if(is_priority)
  138. {
  139. mPendingUploads.push_front(req);
  140. }
  141. else
  142. {
  143. mPendingUploads.push_back(req);
  144. }
  145. }
  146. // Read the data from the VFS if it'll fit in this packet.
  147. if (asset_size + 100 < MTUBYTES)
  148. {
  149. BOOL res = vfile.read(buffer, asset_size); /* Flawfinder: ignore */
  150. S32 bytes_read = res ? vfile.getLastBytesRead() : 0;
  151. if( bytes_read == asset_size )
  152. {
  153. req->mDataSentInFirstPacket = TRUE;
  154. //llinfos << "LLViewerAssetStorage::createAsset sending data in first packet" << llendl;
  155. }
  156. else
  157. {
  158. llwarns << "Probable corruption in VFS file, aborting store asset data" << llendl;
  159. // LLAssetStorage metric: VFS corrupt - bogus size
  160. reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, asset_size, MR_VFS_CORRUPTION, __FILE__, __LINE__, "VFS corruption" );
  161. if (callback)
  162. {
  163. callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LL_EXSTAT_VFS_CORRUPT);
  164. }
  165. return;
  166. }
  167. }
  168. else
  169. {
  170. // Too big, do an xfer
  171. buffer[0] = 0;
  172. asset_size = 0;
  173. }
  174. mMessageSys->newMessageFast(_PREHASH_AssetUploadRequest);
  175. mMessageSys->nextBlockFast(_PREHASH_AssetBlock);
  176. mMessageSys->addUUIDFast(_PREHASH_TransactionID, tid);
  177. mMessageSys->addS8Fast(_PREHASH_Type, (S8)asset_type);
  178. mMessageSys->addBOOLFast(_PREHASH_Tempfile, temp_file);
  179. mMessageSys->addBOOLFast(_PREHASH_StoreLocal, store_local);
  180. mMessageSys->addBinaryDataFast( _PREHASH_AssetData, buffer, asset_size );
  181. mMessageSys->sendReliable(mUpstreamHost);
  182. }
  183. else
  184. {
  185. llwarns << "AssetStorage: attempt to upload non-existent vfile " << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
  186. // LLAssetStorage metric: Zero size VFS
  187. reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" );
  188. if (callback)
  189. {
  190. callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LL_EXSTAT_NONEXISTENT_FILE);
  191. }
  192. }
  193. }
  194. else
  195. {
  196. llwarns << "Attempt to move asset store request upstream w/o valid upstream provider" << llendl;
  197. // LLAssetStorage metric: Upstream provider dead
  198. reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_NO_UPSTREAM, __FILE__, __LINE__, "No upstream provider" );
  199. if (callback)
  200. {
  201. callback(asset_id, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM);
  202. }
  203. }
  204. }
  205. void LLViewerAssetStorage::storeAssetData(
  206. const std::string& filename,
  207. const LLTransactionID& tid,
  208. LLAssetType::EType asset_type,
  209. LLStoreAssetCallback callback,
  210. void* user_data,
  211. bool temp_file,
  212. bool is_priority,
  213. bool user_waiting,
  214. F64 timeout)
  215. {
  216. if(filename.empty())
  217. {
  218. // LLAssetStorage metric: no filename
  219. reportMetric( LLUUID::null, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_VFS_CORRUPTION, __FILE__, __LINE__, "Filename missing" );
  220. llerrs << "No filename specified" << llendl;
  221. return;
  222. }
  223. LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
  224. LL_DEBUGS("AssetStorage") << "LLViewerAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
  225. LL_DEBUGS("AssetStorage") << "ASSET_ID: " << asset_id << llendl;
  226. S32 size = 0;
  227. LLFILE* fp = LLFile::fopen(filename, "rb");
  228. if (fp)
  229. {
  230. fseek(fp, 0, SEEK_END);
  231. size = ftell(fp);
  232. fseek(fp, 0, SEEK_SET);
  233. }
  234. if( size )
  235. {
  236. LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
  237. legacy->mUpCallback = callback;
  238. legacy->mUserData = user_data;
  239. LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE);
  240. file.setMaxSize(size);
  241. const S32 buf_size = 65536;
  242. U8 copy_buf[buf_size];
  243. while ((size = (S32)fread(copy_buf, 1, buf_size, fp)))
  244. {
  245. file.write(copy_buf, size);
  246. }
  247. fclose(fp);
  248. // if this upload fails, the caller needs to setup a new tempfile for us
  249. if (temp_file)
  250. {
  251. LLFile::remove(filename);
  252. }
  253. // LLAssetStorage metric: Success not needed; handled in the overloaded method here:
  254. LLViewerAssetStorage::storeAssetData(
  255. tid,
  256. asset_type,
  257. legacyStoreDataCallback,
  258. (void**)legacy,
  259. temp_file,
  260. is_priority);
  261. }
  262. else // size == 0 (but previous block changes size)
  263. {
  264. if( fp )
  265. {
  266. // LLAssetStorage metric: Zero size
  267. reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file was zero length" );
  268. }
  269. else
  270. {
  271. // LLAssetStorage metric: Missing File
  272. reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_FILE_NONEXIST, __FILE__, __LINE__, "The file didn't exist" );
  273. }
  274. if (callback)
  275. {
  276. callback(asset_id, user_data, LL_ERR_CANNOT_OPEN_FILE, LL_EXSTAT_BLOCKED_FILE);
  277. }
  278. }
  279. }
  280. /**
  281. * @brief Allocate and queue an asset fetch request for the viewer
  282. *
  283. * This is a nearly-verbatim copy of the base class's implementation
  284. * with the following changes:
  285. * - Use a locally-derived request class
  286. * - Start timing for metrics when request is queued
  287. *
  288. * This is an unfortunate implementation choice but it's forced by
  289. * current conditions. A refactoring that might clean up the layers
  290. * of responsibility or introduce factories or more virtualization
  291. * of methods would enable a more attractive solution.
  292. *
  293. * If LLAssetStorage::_queueDataRequest changes, this must change
  294. * as well.
  295. */
  296. // virtual
  297. void LLViewerAssetStorage::_queueDataRequest(
  298. const LLUUID& uuid,
  299. LLAssetType::EType atype,
  300. LLGetAssetCallback callback,
  301. void *user_data,
  302. BOOL duplicate,
  303. BOOL is_priority)
  304. {
  305. if (mUpstreamHost.isOk())
  306. {
  307. // stash the callback info so we can find it after we get the response message
  308. LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype);
  309. req->mDownCallback = callback;
  310. req->mUserData = user_data;
  311. req->mIsPriority = is_priority;
  312. if (!duplicate)
  313. {
  314. // Only collect metrics for non-duplicate requests. Others
  315. // are piggy-backing and will artificially lower averages.
  316. req->mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
  317. }
  318. mPendingDownloads.push_back(req);
  319. if (!duplicate)
  320. {
  321. // send request message to our upstream data provider
  322. // Create a new asset transfer.
  323. LLTransferSourceParamsAsset spa;
  324. spa.setAsset(uuid, atype);
  325. // Set our destination file, and the completion callback.
  326. LLTransferTargetParamsVFile tpvf;
  327. tpvf.setAsset(uuid, atype);
  328. tpvf.setCallback(downloadCompleteCallback, req);
  329. LL_DEBUGS("AssetStorage") << "Starting transfer for " << uuid << llendl;
  330. LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET);
  331. ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f));
  332. LLViewerAssetStatsFF::record_enqueue_main(atype, false, false);
  333. }
  334. }
  335. else
  336. {
  337. // uh-oh, we shouldn't have gotten here
  338. llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
  339. if (callback)
  340. {
  341. callback(mVFS, uuid, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM);
  342. }
  343. }
  344. }