00001
00032 #include "linden_common.h"
00033
00034 #include "llvfile.h"
00035
00036 #include "llerror.h"
00037 #include "llthread.h"
00038 #include "llvfs.h"
00039
00040 const S32 LLVFile::READ = 0x00000001;
00041 const S32 LLVFile::WRITE = 0x00000002;
00042 const S32 LLVFile::READ_WRITE = 0x00000003;
00043 const S32 LLVFile::APPEND = 0x00000006;
00044
00045
00046 LLVFSThread* LLVFile::sVFSThread = NULL;
00047 BOOL LLVFile::sAllocdVFSThread = FALSE;
00048 BOOL LLVFile::ALLOW_ASYNC = TRUE;
00049
00050
00051
00052
00053 LLVFile::LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode)
00054 {
00055 mFileType = file_type;
00056
00057 mFileID = file_id;
00058 mPosition = 0;
00059 mMode = mode;
00060 mVFS = vfs;
00061
00062 mBytesRead = 0;
00063 mHandle = LLVFSThread::nullHandle();
00064 mPriority = 128.f;
00065
00066 mVFS->incLock(mFileID, mFileType, VFSLOCK_OPEN);
00067 }
00068
00069 LLVFile::~LLVFile()
00070 {
00071 if (!isReadComplete())
00072 {
00073 if (mHandle != LLVFSThread::nullHandle())
00074 {
00075 if (!(mMode & LLVFile::WRITE))
00076 {
00077
00078 sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_ABORT);
00079 }
00080 else
00081 {
00082 sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE);
00083 }
00084 }
00085 }
00086 mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
00087 }
00088
00089 BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority)
00090 {
00091 if (! (mMode & READ))
00092 {
00093 llwarns << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
00094 return FALSE;
00095 }
00096
00097 if (mHandle != LLVFSThread::nullHandle())
00098 {
00099 llwarns << "Attempt to read from vfile object " << mFileID << " with pending async operation" << llendl;
00100 return FALSE;
00101 }
00102 mPriority = priority;
00103
00104 BOOL success = TRUE;
00105
00106
00107 waitForLock(VFSLOCK_APPEND);
00108
00109
00110 if (async)
00111 {
00112 mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri());
00113 }
00114 else
00115 {
00116
00117 mBytesRead = sVFSThread->readImmediate(mVFS, mFileID, mFileType, buffer, mPosition, bytes);
00118 mPosition += mBytesRead;
00119 if (! mBytesRead)
00120 {
00121 success = FALSE;
00122 }
00123 }
00124
00125 return success;
00126 }
00127
00128
00129 U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read)
00130 {
00131 U8 *data;
00132 LLVFile file(vfs, uuid, type, LLVFile::READ);
00133 S32 file_size = file.getSize();
00134 if (file_size == 0)
00135 {
00136
00137 data = NULL;
00138 }
00139 else
00140 {
00141 data = new U8[file_size];
00142 file.read(data, file_size);
00143
00144 if (file.getLastBytesRead() != (S32)file_size)
00145 {
00146 delete[] data;
00147 data = NULL;
00148 file_size = 0;
00149 }
00150 }
00151 if (bytes_read)
00152 {
00153 *bytes_read = file_size;
00154 }
00155 return data;
00156 }
00157
00158 void LLVFile::setReadPriority(const F32 priority)
00159 {
00160 mPriority = priority;
00161 if (mHandle != LLVFSThread::nullHandle())
00162 {
00163 sVFSThread->setPriority(mHandle, threadPri());
00164 }
00165 }
00166
00167 BOOL LLVFile::isReadComplete()
00168 {
00169 BOOL res = TRUE;
00170 if (mHandle != LLVFSThread::nullHandle())
00171 {
00172 LLVFSThread::Request* req = (LLVFSThread::Request*)sVFSThread->getRequest(mHandle);
00173 LLVFSThread::status_t status = req->getStatus();
00174 if (status == LLVFSThread::STATUS_COMPLETE)
00175 {
00176 mBytesRead = req->getBytesRead();
00177 mPosition += mBytesRead;
00178 sVFSThread->completeRequest(mHandle);
00179 mHandle = LLVFSThread::nullHandle();
00180 }
00181 else
00182 {
00183 res = FALSE;
00184 }
00185 }
00186 return res;
00187 }
00188
00189 S32 LLVFile::getLastBytesRead()
00190 {
00191 return mBytesRead;
00192 }
00193
00194 BOOL LLVFile::eof()
00195 {
00196 return mPosition >= getSize();
00197 }
00198
00199 BOOL LLVFile::write(const U8 *buffer, S32 bytes)
00200 {
00201 if (! (mMode & WRITE))
00202 {
00203 llwarns << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
00204 }
00205 if (mHandle != LLVFSThread::nullHandle())
00206 {
00207 llerrs << "Attempt to write to vfile object " << mFileID << " with pending async operation" << llendl;
00208 return FALSE;
00209 }
00210 BOOL success = TRUE;
00211
00212
00213 if (mMode == APPEND)
00214 {
00215 U8* writebuf = new U8[bytes];
00216 memcpy(writebuf, buffer, bytes);
00217 S32 offset = -1;
00218 mHandle = sVFSThread->write(mVFS, mFileID, mFileType,
00219 writebuf, offset, bytes,
00220 LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_AUTO_DELETE);
00221 mHandle = LLVFSThread::nullHandle();
00222 }
00223 else
00224 {
00225
00226 waitForLock(VFSLOCK_READ);
00227 waitForLock(VFSLOCK_APPEND);
00228
00229 S32 pos = (mMode & APPEND) == APPEND ? -1 : mPosition;
00230
00231 S32 wrote = sVFSThread->writeImmediate(mVFS, mFileID, mFileType, (U8*)buffer, pos, bytes);
00232
00233 mPosition += wrote;
00234
00235 if (wrote < bytes)
00236 {
00237 llwarns << "Tried to write " << bytes << " bytes, actually wrote " << wrote << llendl;
00238
00239 success = FALSE;
00240 }
00241 }
00242 return success;
00243 }
00244
00245
00246 BOOL LLVFile::writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
00247 {
00248 LLVFile file(vfs, uuid, type, LLVFile::WRITE);
00249 file.setMaxSize(bytes);
00250 return file.write(buffer, bytes);
00251 }
00252
00253 BOOL LLVFile::seek(S32 offset, S32 origin)
00254 {
00255 if (mMode == APPEND)
00256 {
00257 llwarns << "Attempt to seek on append-only file" << llendl;
00258 return FALSE;
00259 }
00260
00261 if (-1 == origin)
00262 {
00263 origin = mPosition;
00264 }
00265
00266 S32 new_pos = origin + offset;
00267
00268 S32 size = getSize();
00269
00270 if (new_pos > size)
00271 {
00272 llwarns << "Attempt to seek past end of file" << llendl;
00273
00274 mPosition = size;
00275 return FALSE;
00276 }
00277 else if (new_pos < 0)
00278 {
00279 llwarns << "Attempt to seek past beginning of file" << llendl;
00280
00281 mPosition = 0;
00282 return FALSE;
00283 }
00284
00285 mPosition = new_pos;
00286 return TRUE;
00287 }
00288
00289 S32 LLVFile::tell() const
00290 {
00291 return mPosition;
00292 }
00293
00294 S32 LLVFile::getSize()
00295 {
00296 waitForLock(VFSLOCK_APPEND);
00297 S32 size = mVFS->getSize(mFileID, mFileType);
00298
00299 return size;
00300 }
00301
00302 S32 LLVFile::getMaxSize()
00303 {
00304 S32 size = mVFS->getMaxSize(mFileID, mFileType);
00305
00306 return size;
00307 }
00308
00309 BOOL LLVFile::setMaxSize(S32 size)
00310 {
00311 if (! (mMode & WRITE))
00312 {
00313 llwarns << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
00314
00315 return FALSE;
00316 }
00317
00318 if (!mVFS->checkAvailable(size))
00319 {
00320 LLFastTimer t(LLFastTimer::FTM_VFILE_WAIT);
00321 S32 count = 0;
00322 while (sVFSThread->getPending() > 1000)
00323 {
00324 if (count % 100 == 0)
00325 {
00326 llinfos << "VFS catching up... Pending: " << sVFSThread->getPending() << llendl;
00327 }
00328 if (sVFSThread->isPaused())
00329 {
00330 sVFSThread->update(0);
00331 }
00332 ms_sleep(10);
00333 }
00334 }
00335 return mVFS->setMaxSize(mFileID, mFileType, size);
00336 }
00337
00338 BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type)
00339 {
00340 if (! (mMode & WRITE))
00341 {
00342 llwarns << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
00343
00344 return FALSE;
00345 }
00346
00347 if (mHandle != LLVFSThread::nullHandle())
00348 {
00349 llwarns << "Renaming file with pending async read" << llendl;
00350 }
00351
00352 waitForLock(VFSLOCK_READ);
00353 waitForLock(VFSLOCK_APPEND);
00354
00355
00356
00357 mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
00358 mVFS->renameFile(mFileID, mFileType, new_id, new_type);
00359 mVFS->incLock(new_id, new_type, VFSLOCK_OPEN);
00360
00361 mFileID = new_id;
00362 mFileType = new_type;
00363
00364 return TRUE;
00365 }
00366
00367 BOOL LLVFile::remove()
00368 {
00369
00370
00371 if (! (mMode & WRITE))
00372 {
00373
00374
00375 llwarns << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
00376 }
00377
00378 if (mHandle != LLVFSThread::nullHandle())
00379 {
00380 llwarns << "Removing file with pending async read" << llendl;
00381 }
00382
00383
00384 mPosition = 0;
00385
00386 waitForLock(VFSLOCK_READ);
00387 waitForLock(VFSLOCK_APPEND);
00388 mVFS->removeFile(mFileID, mFileType);
00389
00390 return TRUE;
00391 }
00392
00393
00394 void LLVFile::initClass(LLVFSThread* vfsthread)
00395 {
00396 if (!vfsthread)
00397 {
00398 if (LLVFSThread::sLocal != NULL)
00399 {
00400 vfsthread = LLVFSThread::sLocal;
00401 }
00402 else
00403 {
00404 vfsthread = new LLVFSThread();
00405 sAllocdVFSThread = TRUE;
00406 }
00407 }
00408 sVFSThread = vfsthread;
00409 }
00410
00411
00412 void LLVFile::cleanupClass()
00413 {
00414 if (sAllocdVFSThread)
00415 {
00416 delete sVFSThread;
00417 }
00418 sVFSThread = NULL;
00419 }
00420
00421 bool LLVFile::isLocked(EVFSLock lock)
00422 {
00423 return mVFS->isLocked(mFileID, mFileType, lock) ? true : false;
00424 }
00425
00426 void LLVFile::waitForLock(EVFSLock lock)
00427 {
00428 LLFastTimer t(LLFastTimer::FTM_VFILE_WAIT);
00429
00430 while (isLocked(lock))
00431 {
00432 if (sVFSThread->isPaused())
00433 {
00434 sVFSThread->update(0);
00435 }
00436 ms_sleep(1);
00437 }
00438 }