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