PageRenderTime 52ms CodeModel.GetById 20ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llvfs/llvfile.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 435 lines | 322 code | 65 blank | 48 comment | 53 complexity | 442c9b0e337034f6b2059824e9284f75 MD5 | raw file
  1/** 
  2 * @file llvfile.cpp
  3 * @brief Implementation of virtual file
  4 *
  5 * $LicenseInfo:firstyear=2002&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 "linden_common.h"
 28
 29#include "llvfile.h"
 30
 31#include "llerror.h"
 32#include "llthread.h"
 33#include "llstat.h"
 34#include "llvfs.h"
 35
 36const S32 LLVFile::READ			= 0x00000001;
 37const S32 LLVFile::WRITE		= 0x00000002;
 38const S32 LLVFile::READ_WRITE	= 0x00000003;  // LLVFile::READ & LLVFile::WRITE
 39const S32 LLVFile::APPEND		= 0x00000006;  // 0x00000004 & LLVFile::WRITE
 40
 41static LLFastTimer::DeclareTimer FTM_VFILE_WAIT("VFile Wait");
 42
 43//----------------------------------------------------------------------------
 44LLVFSThread* LLVFile::sVFSThread = NULL;
 45BOOL LLVFile::sAllocdVFSThread = FALSE;
 46//----------------------------------------------------------------------------
 47
 48//============================================================================
 49
 50LLVFile::LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode)
 51{
 52	mFileType =	file_type;
 53
 54	mFileID =	file_id;
 55	mPosition = 0;
 56	mMode =		mode;
 57	mVFS =		vfs;
 58
 59	mBytesRead = 0;
 60	mHandle = LLVFSThread::nullHandle();
 61	mPriority = 128.f;
 62
 63	mVFS->incLock(mFileID, mFileType, VFSLOCK_OPEN);
 64}
 65
 66LLVFile::~LLVFile()
 67{
 68	if (!isReadComplete())
 69	{
 70		if (mHandle != LLVFSThread::nullHandle())
 71		{
 72			if (!(mMode & LLVFile::WRITE))
 73			{
 74				//llwarns << "Destroying LLVFile with pending async read/write, aborting..." << llendl;
 75				sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_ABORT);
 76			}
 77			else // WRITE
 78			{
 79				sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE);
 80			}
 81		}
 82	}
 83	mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
 84}
 85
 86BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority)
 87{
 88	if (! (mMode & READ))
 89	{
 90		llwarns << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
 91		return FALSE;
 92	}
 93
 94	if (mHandle != LLVFSThread::nullHandle())
 95	{
 96		llwarns << "Attempt to read from vfile object " << mFileID << " with pending async operation" << llendl;
 97		return FALSE;
 98	}
 99	mPriority = priority;
100	
101	BOOL success = TRUE;
102
103	// We can't do a read while there are pending async writes
104	waitForLock(VFSLOCK_APPEND);
105	
106	// *FIX: (???)
107	if (async)
108	{
109		mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri());
110	}
111	else
112	{
113		// We can't do a read while there are pending async writes on this file
114		mBytesRead = sVFSThread->readImmediate(mVFS, mFileID, mFileType, buffer, mPosition, bytes);
115		mPosition += mBytesRead;
116		if (! mBytesRead)
117		{
118			success = FALSE;
119		}
120	}
121
122	return success;
123}
124
125//static
126U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read)
127{
128	U8 *data;
129	LLVFile file(vfs, uuid, type, LLVFile::READ);
130	S32 file_size = file.getSize();
131	if (file_size == 0)
132	{
133		// File is empty.
134		data = NULL;
135	}
136	else
137	{
138		data = new U8[file_size];
139		file.read(data, file_size);	/* Flawfinder: ignore */ 
140		
141		if (file.getLastBytesRead() != (S32)file_size)
142		{
143			delete[] data;
144			data = NULL;
145			file_size = 0;
146		}
147	}
148	if (bytes_read)
149	{
150		*bytes_read = file_size;
151	}
152	return data;
153}
154	
155void LLVFile::setReadPriority(const F32 priority)
156{
157	mPriority = priority;
158	if (mHandle != LLVFSThread::nullHandle())
159	{
160		sVFSThread->setPriority(mHandle, threadPri());
161	}
162}
163
164BOOL LLVFile::isReadComplete()
165{
166	BOOL res = TRUE;
167	if (mHandle != LLVFSThread::nullHandle())
168	{
169		LLVFSThread::Request* req = (LLVFSThread::Request*)sVFSThread->getRequest(mHandle);
170		LLVFSThread::status_t status = req->getStatus();
171		if (status == LLVFSThread::STATUS_COMPLETE)
172		{
173			mBytesRead = req->getBytesRead();
174			mPosition += mBytesRead;
175			sVFSThread->completeRequest(mHandle);
176			mHandle = LLVFSThread::nullHandle();
177		}
178		else
179		{
180			res = FALSE;
181		}
182	}
183	return res;
184}
185
186S32 LLVFile::getLastBytesRead()
187{
188	return mBytesRead;
189}
190
191BOOL LLVFile::eof()
192{
193	return mPosition >= getSize();
194}
195
196BOOL LLVFile::write(const U8 *buffer, S32 bytes)
197{
198	if (! (mMode & WRITE))
199	{
200		llwarns << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
201	}
202	if (mHandle != LLVFSThread::nullHandle())
203	{
204		llerrs << "Attempt to write to vfile object " << mFileID << " with pending async operation" << llendl;
205		return FALSE;
206	}
207	BOOL success = TRUE;
208	
209	// *FIX: allow async writes? potential problem wit mPosition...
210	if (mMode == APPEND) // all appends are async (but WRITEs are not)
211	{	
212		U8* writebuf = new U8[bytes];
213		memcpy(writebuf, buffer, bytes);
214		S32 offset = -1;
215		mHandle = sVFSThread->write(mVFS, mFileID, mFileType,
216									writebuf, offset, bytes,
217									LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_AUTO_DELETE);
218		mHandle = LLVFSThread::nullHandle(); // FLAG_AUTO_COMPLETE means we don't track this
219	}
220	else
221	{
222		// We can't do a write while there are pending reads or writes on this file
223		waitForLock(VFSLOCK_READ);
224		waitForLock(VFSLOCK_APPEND);
225
226		S32 pos = (mMode & APPEND) == APPEND ? -1 : mPosition;
227
228		S32 wrote = sVFSThread->writeImmediate(mVFS, mFileID, mFileType, (U8*)buffer, pos, bytes);
229
230		mPosition += wrote;
231		
232		if (wrote < bytes)
233		{	
234			llwarns << "Tried to write " << bytes << " bytes, actually wrote " << wrote << llendl;
235
236			success = FALSE;
237		}
238	}
239	return success;
240}
241
242//static
243BOOL LLVFile::writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
244{
245	LLVFile file(vfs, uuid, type, LLVFile::WRITE);
246	file.setMaxSize(bytes);
247	return file.write(buffer, bytes);
248}
249
250BOOL LLVFile::seek(S32 offset, S32 origin)
251{
252	if (mMode == APPEND)
253	{
254		llwarns << "Attempt to seek on append-only file" << llendl;
255		return FALSE;
256	}
257
258	if (-1 == origin)
259	{
260		origin = mPosition;
261	}
262
263	S32 new_pos = origin + offset;
264
265	S32 size = getSize(); // Calls waitForLock(VFSLOCK_APPEND)
266
267	if (new_pos > size)
268	{
269		llwarns << "Attempt to seek past end of file" << llendl;
270
271		mPosition = size;
272		return FALSE;
273	}
274	else if (new_pos < 0)
275	{
276		llwarns << "Attempt to seek past beginning of file" << llendl;
277
278		mPosition = 0;
279		return FALSE;
280	}
281
282	mPosition = new_pos;
283	return TRUE;
284}
285
286S32 LLVFile::tell() const
287{
288	return mPosition;
289}
290
291S32 LLVFile::getSize()
292{
293	waitForLock(VFSLOCK_APPEND);
294	S32 size = mVFS->getSize(mFileID, mFileType);
295
296	return size;
297}
298
299S32 LLVFile::getMaxSize()
300{
301	S32 size = mVFS->getMaxSize(mFileID, mFileType);
302
303	return size;
304}
305
306BOOL LLVFile::setMaxSize(S32 size)
307{
308	if (! (mMode & WRITE))
309	{
310		llwarns << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
311
312		return FALSE;
313	}
314
315	if (!mVFS->checkAvailable(size))
316	{
317		//LLFastTimer t(FTM_VFILE_WAIT);
318		S32 count = 0;
319		while (sVFSThread->getPending() > 1000)
320		{
321			if (count % 100 == 0)
322			{
323				llinfos << "VFS catching up... Pending: " << sVFSThread->getPending() << llendl;
324			}
325			if (sVFSThread->isPaused())
326			{
327				sVFSThread->update(0);
328			}
329			ms_sleep(10);
330		}
331	}
332	return mVFS->setMaxSize(mFileID, mFileType, size);
333}
334
335BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type)
336{
337	if (! (mMode & WRITE))
338	{
339		llwarns << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
340
341		return FALSE;
342	}
343
344	if (mHandle != LLVFSThread::nullHandle())
345	{
346		llwarns << "Renaming file with pending async read" << llendl;
347	}
348
349	waitForLock(VFSLOCK_READ);
350	waitForLock(VFSLOCK_APPEND);
351
352	// we need to release / replace our own lock
353	// since the renamed file will inherit locks from the new name
354	mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
355	mVFS->renameFile(mFileID, mFileType, new_id, new_type);
356	mVFS->incLock(new_id, new_type, VFSLOCK_OPEN);
357	
358	mFileID = new_id;
359	mFileType = new_type;
360
361	return TRUE;
362}
363
364BOOL LLVFile::remove()
365{
366// 	llinfos << "Removing file " << mFileID << llendl;
367	
368	if (! (mMode & WRITE))
369	{
370		// Leaving paranoia warning just because this should be a very infrequent
371		// operation.
372		llwarns << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
373	}
374
375	if (mHandle != LLVFSThread::nullHandle())
376	{
377		llwarns << "Removing file with pending async read" << llendl;
378	}
379	
380	// why not seek back to the beginning of the file too?
381	mPosition = 0;
382
383	waitForLock(VFSLOCK_READ);
384	waitForLock(VFSLOCK_APPEND);
385	mVFS->removeFile(mFileID, mFileType);
386
387	return TRUE;
388}
389
390// static
391void LLVFile::initClass(LLVFSThread* vfsthread)
392{
393	if (!vfsthread)
394	{
395		if (LLVFSThread::sLocal != NULL)
396		{
397			vfsthread = LLVFSThread::sLocal;
398		}
399		else
400		{
401			vfsthread = new LLVFSThread();
402			sAllocdVFSThread = TRUE;
403		}
404	}
405	sVFSThread = vfsthread;
406}
407
408// static
409void LLVFile::cleanupClass()
410{
411	if (sAllocdVFSThread)
412	{
413		delete sVFSThread;
414	}
415	sVFSThread = NULL;
416}
417
418bool LLVFile::isLocked(EVFSLock lock)
419{
420	return mVFS->isLocked(mFileID, mFileType, lock) ? true : false;
421}
422
423void LLVFile::waitForLock(EVFSLock lock)
424{
425	//LLFastTimer t(FTM_VFILE_WAIT);
426	// spin until the lock clears
427	while (isLocked(lock))
428	{
429		if (sVFSThread->isPaused())
430		{
431			sVFSThread->update(0);
432		}
433		ms_sleep(1);
434	}
435}