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