aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llvfs/llvfile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llvfs/llvfile.cpp')
-rw-r--r--linden/indra/llvfs/llvfile.cpp434
1 files changed, 434 insertions, 0 deletions
diff --git a/linden/indra/llvfs/llvfile.cpp b/linden/indra/llvfs/llvfile.cpp
new file mode 100644
index 0000000..2faa5c3
--- /dev/null
+++ b/linden/indra/llvfs/llvfile.cpp
@@ -0,0 +1,434 @@
1/**
2 * @file llvfile.cpp
3 * @brief Implementation of virtual file
4 *
5 * Copyright (c) 2002-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#include "llvfile.h"
31
32#include "llerror.h"
33#include "llthread.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
41//----------------------------------------------------------------------------
42LLVFSThread* LLVFile::sVFSThread = NULL;
43BOOL LLVFile::sAllocdVFSThread = FALSE;
44BOOL LLVFile::ALLOW_ASYNC = TRUE;
45//----------------------------------------------------------------------------
46
47//============================================================================
48
49LLVFile::LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode)
50{
51 mFileType = file_type;
52
53 mFileID = file_id;
54 mPosition = 0;
55 mMode = mode;
56 mVFS = vfs;
57
58 mBytesRead = 0;
59 mHandle = LLVFSThread::nullHandle();
60 mPriority = 128.f;
61
62 mVFS->incLock(mFileID, mFileType, VFSLOCK_OPEN);
63}
64
65LLVFile::~LLVFile()
66{
67 if (!isReadComplete())
68 {
69 if (mHandle != LLVFSThread::nullHandle())
70 {
71 if (!(mMode & LLVFile::WRITE))
72 {
73 // llwarns << "Destroying LLVFile with pending async read/write, aborting..." << llendl;
74 sVFSThread->abortRequest(mHandle, LLVFSThread::AUTO_COMPLETE);
75 }
76 else // WRITE
77 {
78 sVFSThread->setFlags(mHandle, LLVFSThread::AUTO_COMPLETE);
79 }
80 }
81 }
82 mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
83}
84
85BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority)
86{
87 if (! (mMode & READ))
88 {
89 llwarns << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
90 return FALSE;
91 }
92
93 if (mHandle != LLVFSThread::nullHandle())
94 {
95 llwarns << "Attempt to read from vfile object " << mFileID << " with pending async operation" << llendl;
96 return FALSE;
97 }
98 mPriority = priority;
99
100 BOOL success = TRUE;
101
102 // We can't do a read while there are pending async writes
103 waitForLock(VFSLOCK_APPEND);
104
105 // *FIX: (???)
106 if (async)
107 {
108 mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri());
109 }
110 else
111 {
112 // We can't do a read while there are pending async writes on this file
113 mBytesRead = sVFSThread->readImmediate(mVFS, mFileID, mFileType, buffer, mPosition, bytes);
114 mPosition += mBytesRead;
115 if (! mBytesRead)
116 {
117 success = FALSE;
118 }
119 }
120
121 return success;
122}
123
124//static
125U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read)
126{
127 U8 *data;
128 LLVFile file(vfs, uuid, type, LLVFile::READ);
129 S32 file_size = file.getSize();
130 if (file_size == 0)
131 {
132 // File is empty.
133 data = NULL;
134 }
135 else
136 {
137 data = new U8[file_size];
138 file.read(data, file_size);
139
140 if (file.getLastBytesRead() != (S32)file_size)
141 {
142 delete[] data;
143 data = NULL;
144 file_size = 0;
145 }
146 }
147 if (bytes_read)
148 {
149 *bytes_read = file_size;
150 }
151 return data;
152}
153
154void LLVFile::setReadPriority(const F32 priority)
155{
156 mPriority = priority;
157 if (mHandle != LLVFSThread::nullHandle())
158 {
159 sVFSThread->setPriority(mHandle, threadPri());
160 }
161}
162
163BOOL LLVFile::isReadComplete()
164{
165 BOOL res = TRUE;
166 if (mHandle != LLVFSThread::nullHandle())
167 {
168 LLVFSThread::Request* req = (LLVFSThread::Request*)sVFSThread->getRequest(mHandle);
169 LLVFSThread::status_t status = req->getStatus();
170 if (status == LLVFSThread::STATUS_COMPLETE)
171 {
172 mBytesRead = req->getBytesRead();
173 mPosition += mBytesRead;
174 sVFSThread->completeRequest(mHandle);
175 mHandle = LLVFSThread::nullHandle();
176 }
177 else
178 {
179 res = FALSE;
180 }
181 }
182 return res;
183}
184
185S32 LLVFile::getLastBytesRead()
186{
187 return mBytesRead;
188}
189
190BOOL LLVFile::eof()
191{
192 return mPosition >= getSize();
193}
194
195BOOL LLVFile::write(const U8 *buffer, S32 bytes)
196{
197 if (! (mMode & WRITE))
198 {
199 llwarns << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
200 }
201 if (mHandle != LLVFSThread::nullHandle())
202 {
203 llerrs << "Attempt to write to vfile object " << mFileID << " with pending async operation" << llendl;
204 return FALSE;
205 }
206 BOOL success = TRUE;
207
208 // *FIX: allow async writes? potential problem wit mPosition...
209 if (mMode == APPEND) // all appends are async (but WRITEs are not)
210 {
211 U8* writebuf = new U8[bytes];
212 memcpy(writebuf, buffer, bytes);
213 S32 offset = -1;
214 mHandle = sVFSThread->write(mVFS, mFileID, mFileType,
215 writebuf, offset, bytes,
216 LLVFSThread::AUTO_COMPLETE | LLVFSThread::AUTO_DELETE);
217 mHandle = LLVFSThread::nullHandle(); // AUTO_COMPLETE means we don't track this
218 }
219 else
220 {
221 // We can't do a write while there are pending reads or writes on this file
222 waitForLock(VFSLOCK_READ);
223 waitForLock(VFSLOCK_APPEND);
224
225 S32 pos = (mMode & APPEND) == APPEND ? -1 : mPosition;
226
227 S32 wrote = sVFSThread->writeImmediate(mVFS, mFileID, mFileType, (U8*)buffer, pos, bytes);
228
229 mPosition += wrote;
230
231 if (wrote < bytes)
232 {
233 llwarns << "Tried to write " << bytes << " bytes, actually wrote " << wrote << llendl;
234
235 success = FALSE;
236 }
237 }
238 return success;
239}
240
241//static
242BOOL LLVFile::writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
243{
244 LLVFile file(vfs, uuid, type, LLVFile::WRITE);
245 file.setMaxSize(bytes);
246 return file.write(buffer, bytes);
247}
248
249BOOL LLVFile::seek(S32 offset, S32 origin)
250{
251 if (mMode == APPEND)
252 {
253 llwarns << "Attempt to seek on append-only file" << llendl;
254 return FALSE;
255 }
256
257 if (-1 == origin)
258 {
259 origin = mPosition;
260 }
261
262 S32 new_pos = origin + offset;
263
264 S32 size = getSize(); // Calls waitForLock(VFSLOCK_APPEND)
265
266 if (new_pos > size)
267 {
268 llwarns << "Attempt to seek past end of file" << llendl;
269
270 mPosition = size;
271 return FALSE;
272 }
273 else if (new_pos < 0)
274 {
275 llwarns << "Attempt to seek past beginning of file" << llendl;
276
277 mPosition = 0;
278 return FALSE;
279 }
280
281 mPosition = new_pos;
282 return TRUE;
283}
284
285S32 LLVFile::tell() const
286{
287 return mPosition;
288}
289
290S32 LLVFile::getSize()
291{
292 waitForLock(VFSLOCK_APPEND);
293 S32 size = mVFS->getSize(mFileID, mFileType);
294
295 return size;
296}
297
298S32 LLVFile::getMaxSize()
299{
300 S32 size = mVFS->getMaxSize(mFileID, mFileType);
301
302 return size;
303}
304
305BOOL LLVFile::setMaxSize(S32 size)
306{
307 if (! (mMode & WRITE))
308 {
309 llwarns << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
310
311 return FALSE;
312 }
313
314 if (!mVFS->checkAvailable(size))
315 {
316 LLFastTimer t(LLFastTimer::FTM_VFILE_WAIT);
317 S32 count = 0;
318 while (sVFSThread->getPending() > 1000)
319 {
320 if (count % 100 == 0)
321 {
322 llinfos << "VFS catching up... Pending: " << sVFSThread->getPending() << llendl;
323 }
324 if (sVFSThread->isPaused())
325 {
326 sVFSThread->updateQueue(0);
327 }
328 ms_sleep(10);
329 }
330 }
331 return mVFS->setMaxSize(mFileID, mFileType, size);
332}
333
334BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type)
335{
336 if (! (mMode & WRITE))
337 {
338 llwarns << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
339
340 return FALSE;
341 }
342
343 if (mHandle != LLVFSThread::nullHandle())
344 {
345 llwarns << "Renaming file with pending async read" << llendl;
346 }
347
348 waitForLock(VFSLOCK_READ);
349 waitForLock(VFSLOCK_APPEND);
350
351 // we need to release / replace our own lock
352 // since the renamed file will inherit locks from the new name
353 mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
354 mVFS->renameFile(mFileID, mFileType, new_id, new_type);
355 mVFS->incLock(new_id, new_type, VFSLOCK_OPEN);
356
357 mFileID = new_id;
358 mFileType = new_type;
359
360 return TRUE;
361}
362
363BOOL LLVFile::remove()
364{
365// llinfos << "Removing file " << mFileID << llendl;
366
367 if (! (mMode & WRITE))
368 {
369 // Leaving paranoia warning just because this should be a very infrequent
370 // operation.
371 llwarns << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
372 }
373
374 if (mHandle != LLVFSThread::nullHandle())
375 {
376 llwarns << "Removing file with pending async read" << llendl;
377 }
378
379 // why not seek back to the beginning of the file too?
380 mPosition = 0;
381
382 waitForLock(VFSLOCK_READ);
383 waitForLock(VFSLOCK_APPEND);
384 mVFS->removeFile(mFileID, mFileType);
385
386 return TRUE;
387}
388
389// static
390void LLVFile::initClass(LLVFSThread* vfsthread)
391{
392 if (!vfsthread)
393 {
394 if (LLVFSThread::sLocal != NULL)
395 {
396 vfsthread = LLVFSThread::sLocal;
397 }
398 else
399 {
400 vfsthread = new LLVFSThread();
401 sAllocdVFSThread = TRUE;
402 }
403 }
404 sVFSThread = vfsthread;
405}
406
407// static
408void LLVFile::cleanupClass()
409{
410 if (sAllocdVFSThread)
411 {
412 delete sVFSThread;
413 }
414 sVFSThread = NULL;
415}
416
417bool LLVFile::isLocked(EVFSLock lock)
418{
419 return mVFS->isLocked(mFileID, mFileType, lock) ? true : false;
420}
421
422void LLVFile::waitForLock(EVFSLock lock)
423{
424 LLFastTimer t(LLFastTimer::FTM_VFILE_WAIT);
425 // spin until the lock clears
426 while (isLocked(lock))
427 {
428 if (sVFSThread->isPaused())
429 {
430 sVFSThread->updateQueue(0);
431 }
432 ms_sleep(1);
433 }
434}