llvfile.cpp

Go to the documentation of this file.
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;  // LLVFile::READ & LLVFile::WRITE
00043 const S32 LLVFile::APPEND               = 0x00000006;  // 0x00000004 & LLVFile::WRITE
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                                 //llwarns << "Destroying LLVFile with pending async read/write, aborting..." << llendl;
00078                                 sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_ABORT);
00079                         }
00080                         else // WRITE
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         // We can't do a read while there are pending async writes
00107         waitForLock(VFSLOCK_APPEND);
00108         
00109         // *FIX: (???)
00110         if (async)
00111         {
00112                 mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri());
00113         }
00114         else
00115         {
00116                 // We can't do a read while there are pending async writes on this file
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 //static
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                 // File is empty.
00137                 data = NULL;
00138         }
00139         else
00140         {
00141                 data = new U8[file_size];
00142                 file.read(data, file_size);     /* Flawfinder: ignore */ 
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         // *FIX: allow async writes? potential problem wit mPosition...
00213         if (mMode == APPEND) // all appends are async (but WRITEs are not)
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(); // FLAG_AUTO_COMPLETE means we don't track this
00222         }
00223         else
00224         {
00225                 // We can't do a write while there are pending reads or writes on this file
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 //static
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(); // Calls waitForLock(VFSLOCK_APPEND)
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         // we need to release / replace our own lock
00356         // since the renamed file will inherit locks from the new name
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 //      llinfos << "Removing file " << mFileID << llendl;
00370         
00371         if (! (mMode & WRITE))
00372         {
00373                 // Leaving paranoia warning just because this should be a very infrequent
00374                 // operation.
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         // why not seek back to the beginning of the file too?
00384         mPosition = 0;
00385 
00386         waitForLock(VFSLOCK_READ);
00387         waitForLock(VFSLOCK_APPEND);
00388         mVFS->removeFile(mFileID, mFileType);
00389 
00390         return TRUE;
00391 }
00392 
00393 // static
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 // static
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         // spin until the lock clears
00430         while (isLocked(lock))
00431         {
00432                 if (sVFSThread->isPaused())
00433                 {
00434                         sVFSThread->update(0);
00435                 }
00436                 ms_sleep(1);
00437         }
00438 }

Generated on Thu Jul 1 06:09:25 2010 for Second Life Viewer by  doxygen 1.4.7