summaryrefslogtreecommitdiff
path: root/indra/llvfs/llvfile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llvfs/llvfile.cpp')
-rw-r--r--indra/llvfs/llvfile.cpp437
1 files changed, 437 insertions, 0 deletions
diff --git a/indra/llvfs/llvfile.cpp b/indra/llvfs/llvfile.cpp
new file mode 100644
index 0000000000..b8588e99f4
--- /dev/null
+++ b/indra/llvfs/llvfile.cpp
@@ -0,0 +1,437 @@
+/**
+ * @file llvfile.cpp
+ * @brief Implementation of virtual file
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llvfile.h"
+
+#include "llerror.h"
+#include "llthread.h"
+#include "lltimer.h"
+#include "llfasttimer.h"
+#include "llmemory.h"
+#include "llvfs.h"
+
+const S32 LLVFile::READ = 0x00000001;
+const S32 LLVFile::WRITE = 0x00000002;
+const S32 LLVFile::READ_WRITE = 0x00000003; // LLVFile::READ & LLVFile::WRITE
+const S32 LLVFile::APPEND = 0x00000006; // 0x00000004 & LLVFile::WRITE
+
+static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait");
+
+//----------------------------------------------------------------------------
+LLVFSThread* LLVFile::sVFSThread = NULL;
+BOOL LLVFile::sAllocdVFSThread = FALSE;
+//----------------------------------------------------------------------------
+
+//============================================================================
+
+LLVFile::LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode)
+{
+ mFileType = file_type;
+
+ mFileID = file_id;
+ mPosition = 0;
+ mMode = mode;
+ mVFS = vfs;
+
+ mBytesRead = 0;
+ mHandle = LLVFSThread::nullHandle();
+ mPriority = 128.f;
+
+ mVFS->incLock(mFileID, mFileType, VFSLOCK_OPEN);
+}
+
+LLVFile::~LLVFile()
+{
+ if (!isReadComplete())
+ {
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ if (!(mMode & LLVFile::WRITE))
+ {
+ //LL_WARNS() << "Destroying LLVFile with pending async read/write, aborting..." << LL_ENDL;
+ sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_ABORT);
+ }
+ else // WRITE
+ {
+ sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE);
+ }
+ }
+ }
+ mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
+}
+
+BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority)
+{
+ if (! (mMode & READ))
+ {
+ LL_WARNS() << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL;
+ return FALSE;
+ }
+
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ LL_WARNS() << "Attempt to read from vfile object " << mFileID << " with pending async operation" << LL_ENDL;
+ return FALSE;
+ }
+ mPriority = priority;
+
+ BOOL success = TRUE;
+
+ // We can't do a read while there are pending async writes
+ waitForLock(VFSLOCK_APPEND);
+
+ // *FIX: (?)
+ if (async)
+ {
+ mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri());
+ }
+ else
+ {
+ // We can't do a read while there are pending async writes on this file
+ mBytesRead = sVFSThread->readImmediate(mVFS, mFileID, mFileType, buffer, mPosition, bytes);
+ mPosition += mBytesRead;
+ if (! mBytesRead)
+ {
+ success = FALSE;
+ }
+ }
+
+ return success;
+}
+
+//static
+U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read)
+{
+ U8 *data;
+ LLVFile file(vfs, uuid, type, LLVFile::READ);
+ S32 file_size = file.getSize();
+ if (file_size == 0)
+ {
+ // File is empty.
+ data = NULL;
+ }
+ else
+ {
+ data = (U8*) ll_aligned_malloc<16>(file_size);
+ file.read(data, file_size); /* Flawfinder: ignore */
+
+ if (file.getLastBytesRead() != (S32)file_size)
+ {
+ ll_aligned_free<16>(data);
+ data = NULL;
+ file_size = 0;
+ }
+ }
+ if (bytes_read)
+ {
+ *bytes_read = file_size;
+ }
+ return data;
+}
+
+void LLVFile::setReadPriority(const F32 priority)
+{
+ mPriority = priority;
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ sVFSThread->setPriority(mHandle, threadPri());
+ }
+}
+
+BOOL LLVFile::isReadComplete()
+{
+ BOOL res = TRUE;
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ LLVFSThread::Request* req = (LLVFSThread::Request*)sVFSThread->getRequest(mHandle);
+ LLVFSThread::status_t status = req->getStatus();
+ if (status == LLVFSThread::STATUS_COMPLETE)
+ {
+ mBytesRead = req->getBytesRead();
+ mPosition += mBytesRead;
+ sVFSThread->completeRequest(mHandle);
+ mHandle = LLVFSThread::nullHandle();
+ }
+ else
+ {
+ res = FALSE;
+ }
+ }
+ return res;
+}
+
+S32 LLVFile::getLastBytesRead()
+{
+ return mBytesRead;
+}
+
+BOOL LLVFile::eof()
+{
+ return mPosition >= getSize();
+}
+
+BOOL LLVFile::write(const U8 *buffer, S32 bytes)
+{
+ if (! (mMode & WRITE))
+ {
+ LL_WARNS() << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL;
+ }
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ LL_ERRS() << "Attempt to write to vfile object " << mFileID << " with pending async operation" << LL_ENDL;
+ return FALSE;
+ }
+ BOOL success = TRUE;
+
+ // *FIX: allow async writes? potential problem wit mPosition...
+ if (mMode == APPEND) // all appends are async (but WRITEs are not)
+ {
+ U8* writebuf = new U8[bytes];
+ memcpy(writebuf, buffer, bytes);
+ S32 offset = -1;
+ mHandle = sVFSThread->write(mVFS, mFileID, mFileType,
+ writebuf, offset, bytes,
+ LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_AUTO_DELETE);
+ mHandle = LLVFSThread::nullHandle(); // FLAG_AUTO_COMPLETE means we don't track this
+ }
+ else
+ {
+ // We can't do a write while there are pending reads or writes on this file
+ waitForLock(VFSLOCK_READ);
+ waitForLock(VFSLOCK_APPEND);
+
+ S32 pos = (mMode & APPEND) == APPEND ? -1 : mPosition;
+
+ S32 wrote = sVFSThread->writeImmediate(mVFS, mFileID, mFileType, (U8*)buffer, pos, bytes);
+
+ mPosition += wrote;
+
+ if (wrote < bytes)
+ {
+ LL_WARNS() << "Tried to write " << bytes << " bytes, actually wrote " << wrote << LL_ENDL;
+
+ success = FALSE;
+ }
+ }
+ return success;
+}
+
+//static
+BOOL LLVFile::writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
+{
+ LLVFile file(vfs, uuid, type, LLVFile::WRITE);
+ file.setMaxSize(bytes);
+ return file.write(buffer, bytes);
+}
+
+BOOL LLVFile::seek(S32 offset, S32 origin)
+{
+ if (mMode == APPEND)
+ {
+ LL_WARNS() << "Attempt to seek on append-only file" << LL_ENDL;
+ return FALSE;
+ }
+
+ if (-1 == origin)
+ {
+ origin = mPosition;
+ }
+
+ S32 new_pos = origin + offset;
+
+ S32 size = getSize(); // Calls waitForLock(VFSLOCK_APPEND)
+
+ if (new_pos > size)
+ {
+ LL_WARNS() << "Attempt to seek past end of file" << LL_ENDL;
+
+ mPosition = size;
+ return FALSE;
+ }
+ else if (new_pos < 0)
+ {
+ LL_WARNS() << "Attempt to seek past beginning of file" << LL_ENDL;
+
+ mPosition = 0;
+ return FALSE;
+ }
+
+ mPosition = new_pos;
+ return TRUE;
+}
+
+S32 LLVFile::tell() const
+{
+ return mPosition;
+}
+
+S32 LLVFile::getSize()
+{
+ waitForLock(VFSLOCK_APPEND);
+ S32 size = mVFS->getSize(mFileID, mFileType);
+
+ return size;
+}
+
+S32 LLVFile::getMaxSize()
+{
+ S32 size = mVFS->getMaxSize(mFileID, mFileType);
+
+ return size;
+}
+
+BOOL LLVFile::setMaxSize(S32 size)
+{
+ if (! (mMode & WRITE))
+ {
+ LL_WARNS() << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL;
+
+ return FALSE;
+ }
+
+ if (!mVFS->checkAvailable(size))
+ {
+ //LL_RECORD_BLOCK_TIME(FTM_VFILE_WAIT);
+ S32 count = 0;
+ while (sVFSThread->getPending() > 1000)
+ {
+ if (count % 100 == 0)
+ {
+ LL_INFOS() << "VFS catching up... Pending: " << sVFSThread->getPending() << LL_ENDL;
+ }
+ if (sVFSThread->isPaused())
+ {
+ sVFSThread->update(0);
+ }
+ ms_sleep(10);
+ }
+ }
+ return mVFS->setMaxSize(mFileID, mFileType, size);
+}
+
+BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type)
+{
+ if (! (mMode & WRITE))
+ {
+ LL_WARNS() << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL;
+
+ return FALSE;
+ }
+
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ LL_WARNS() << "Renaming file with pending async read" << LL_ENDL;
+ }
+
+ waitForLock(VFSLOCK_READ);
+ waitForLock(VFSLOCK_APPEND);
+
+ // we need to release / replace our own lock
+ // since the renamed file will inherit locks from the new name
+ mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
+ mVFS->renameFile(mFileID, mFileType, new_id, new_type);
+ mVFS->incLock(new_id, new_type, VFSLOCK_OPEN);
+
+ mFileID = new_id;
+ mFileType = new_type;
+
+ return TRUE;
+}
+
+BOOL LLVFile::remove()
+{
+// LL_INFOS() << "Removing file " << mFileID << LL_ENDL;
+
+ if (! (mMode & WRITE))
+ {
+ // Leaving paranoia warning just because this should be a very infrequent
+ // operation.
+ LL_WARNS() << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL;
+ }
+
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ LL_WARNS() << "Removing file with pending async read" << LL_ENDL;
+ }
+
+ // why not seek back to the beginning of the file too?
+ mPosition = 0;
+
+ waitForLock(VFSLOCK_READ);
+ waitForLock(VFSLOCK_APPEND);
+ mVFS->removeFile(mFileID, mFileType);
+
+ return TRUE;
+}
+
+// static
+void LLVFile::initClass(LLVFSThread* vfsthread)
+{
+ if (!vfsthread)
+ {
+ if (LLVFSThread::sLocal != NULL)
+ {
+ vfsthread = LLVFSThread::sLocal;
+ }
+ else
+ {
+ vfsthread = new LLVFSThread();
+ sAllocdVFSThread = TRUE;
+ }
+ }
+ sVFSThread = vfsthread;
+}
+
+// static
+void LLVFile::cleanupClass()
+{
+ if (sAllocdVFSThread)
+ {
+ delete sVFSThread;
+ }
+ sVFSThread = NULL;
+}
+
+bool LLVFile::isLocked(EVFSLock lock)
+{
+ return mVFS->isLocked(mFileID, mFileType, lock) ? true : false;
+}
+
+void LLVFile::waitForLock(EVFSLock lock)
+{
+ //LL_RECORD_BLOCK_TIME(FTM_VFILE_WAIT);
+ // spin until the lock clears
+ while (isLocked(lock))
+ {
+ if (sVFSThread->isPaused())
+ {
+ sVFSThread->update(0);
+ }
+ ms_sleep(1);
+ }
+}