VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/vfs/vfsreadahead.cpp@ 59756

Last change on this file since 59756 was 59756, checked in by vboxsync, 9 years ago

vfsreadahead.cpp: Initialize status code in read ahead thread.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.9 KB
Line 
1/* $Id: vfsreadahead.cpp 59756 2016-02-20 02:48:18Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Read-Ahead Thread.
4 */
5
6/*
7 * Copyright (C) 2010-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/vfs.h>
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/err.h>
37#include <iprt/file.h>
38#include <iprt/list.h>
39#include <iprt/poll.h>
40#include <iprt/string.h>
41#include <iprt/vfslowlevel.h>
42
43
44
45/*********************************************************************************************************************************
46* Header Files *
47*********************************************************************************************************************************/
48#include "internal/iprt.h"
49#include <iprt/vfs.h>
50
51#include <iprt/critsect.h>
52#include <iprt/err.h>
53#include <iprt/mem.h>
54#include <iprt/thread.h>
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60/**
61 * Buffer descriptor.
62 */
63typedef struct RTVFSREADAHEADBUFDESC
64{
65 /** List entry. */
66 RTLISTNODE ListEntry;
67 /** The offset of this extent within the file. */
68 uint64_t off;
69 /** The amount of the buffer that has been filled.
70 * (Buffer size is RTVFSREADAHEAD::cbBuffer.) */
71 uint32_t cbFilled;
72 /** */
73 uint32_t volatile fReserved;
74 /** Pointer to the buffer. */
75 uint8_t *pbBuffer;
76} RTVFSREADAHEADBUFDESC;
77/** Pointer to a memory file extent. */
78typedef RTVFSREADAHEADBUFDESC *PRTVFSREADAHEADBUFDESC;
79
80/**
81 * Read ahead file or I/O stream.
82 */
83typedef struct RTVFSREADAHEAD
84{
85 /** The I/O critical section (protects offActual).
86 * The thread doing I/O or seeking always need to own this. */
87 RTCRITSECT IoCritSect;
88
89 /** The critical section protecting the buffer lists and offConsumer.
90 *
91 * This can be taken while holding IoCritSect as that eliminates a race
92 * condition between the read ahead thread inserting into ConsumerList and
93 * a consumer thread deciding to do a direct read. */
94 RTCRITSECT BufferCritSect;
95 /** List of buffers available for consumption.
96 * The producer thread (hThread) puts buffers into this list once it's done
97 * reading into them. The consumer moves them to the FreeList once the
98 * current position has passed beyond each buffer. */
99 RTLISTANCHOR ConsumerList;
100 /** List of buffers available for the producer. */
101 RTLISTANCHOR FreeList;
102
103 /** The current file position from the consumer point of view. */
104 uint64_t offConsumer;
105
106 /** The end-of-file(/stream) offset. This is initially UINT64_MAX and later
107 * set when reading past EOF. */
108 uint64_t offEof;
109
110 /** The read ahead thread. */
111 RTTHREAD hThread;
112 /** Set when we want the thread to terminate. */
113 bool volatile fTerminateThread;
114 /** Creation flags. */
115 uint32_t fFlags;
116
117 /** The I/O stream we read from. */
118 RTVFSIOSTREAM hIos;
119 /** The file face of hIos, if we're fronting for an actual file. */
120 RTVFSFILE hFile;
121 /** The buffer size. */
122 uint32_t cbBuffer;
123 /** The number of buffers. */
124 uint32_t cBuffers;
125 /** Single big buffer allocation, cBuffers * cbBuffer in size. */
126 uint8_t *pbAllBuffers;
127 /** Array of buffer descriptors (cBuffers in size). */
128 RTVFSREADAHEADBUFDESC aBufDescs[1];
129} RTVFSREADAHEAD;
130/** Pointer to a memory file. */
131typedef RTVFSREADAHEAD *PRTVFSREADAHEAD;
132
133
134
135/**
136 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
137 */
138static DECLCALLBACK(int) rtVfsReadAhead_Close(void *pvThis)
139{
140 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
141 int rc;
142
143 /*
144 * Stop the read-ahead thread.
145 */
146 if (pThis->hThread != NIL_RTTHREAD)
147 {
148 ASMAtomicWriteBool(&pThis->fTerminateThread, true);
149 rc = RTThreadUserSignal(pThis->hThread);
150 AssertRC(rc);
151 rc = RTThreadWait(pThis->hThread, RT_INDEFINITE_WAIT, NULL);
152 AssertRCReturn(rc, rc);
153 pThis->hThread = NIL_RTTHREAD;
154 }
155
156 /*
157 * Release the upstream objects.
158 */
159 RTCritSectEnter(&pThis->IoCritSect);
160
161 RTVfsIoStrmRelease(pThis->hIos);
162 pThis->hIos = NIL_RTVFSIOSTREAM;
163 RTVfsFileRelease(pThis->hFile);
164 pThis->hFile = NIL_RTVFSFILE;
165
166 RTCritSectLeave(&pThis->IoCritSect);
167
168 /*
169 * Free the buffers.
170 */
171 RTCritSectEnter(&pThis->BufferCritSect);
172 if (pThis->pbAllBuffers)
173 {
174 RTMemPageFree(pThis->pbAllBuffers, pThis->cBuffers * pThis->cbBuffer);
175 pThis->pbAllBuffers = NULL;
176 }
177 RTCritSectLeave(&pThis->BufferCritSect);
178
179 /*
180 * Destroy the critical sections.
181 */
182 RTCritSectDelete(&pThis->BufferCritSect);
183 RTCritSectDelete(&pThis->IoCritSect);
184
185 return VINF_SUCCESS;
186}
187
188
189/**
190 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
191 */
192static DECLCALLBACK(int) rtVfsReadAhead_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
193{
194 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
195 return RTVfsIoStrmQueryInfo(pThis->hIos, pObjInfo, enmAddAttr);
196}
197
198
199/**
200 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
201 */
202static DECLCALLBACK(int) rtVfsReadAhead_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
203{
204 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
205
206 Assert(pSgBuf->cSegs == 1); /* Caller deals with multiple SGs. */
207 Assert(off < 0);
208 NOREF(fBlocking);
209
210
211 /*
212 * We loop here to repeat the buffer search after entering the I/O critical
213 * section, just in case a buffer got inserted while we were waiting for it.
214 */
215 int rc = VINF_SUCCESS;
216 uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
217 size_t cbDst = pSgBuf->paSegs[0].cbSeg;
218 size_t cbTotalRead = 0;
219 bool fPokeReader = false;
220 bool fOwnsIoCritSect = false;
221 RTCritSectEnter(&pThis->BufferCritSect);
222 for (;;)
223 {
224 /*
225 * Try satisfy the read from the buffers.
226 */
227 uint64_t offCur = pThis->offConsumer;
228 if (off != -1)
229 {
230 offCur = (uint64_t)off;
231 if (pThis->offConsumer != offCur)
232 fPokeReader = true; /* If the current position changed, poke it in case it stopped at EOF. */
233 pThis->offConsumer = offCur;
234 }
235
236 PRTVFSREADAHEADBUFDESC pBufDesc, pNextBufDesc;
237 RTListForEachSafe(&pThis->ConsumerList, pBufDesc, pNextBufDesc, RTVFSREADAHEADBUFDESC, ListEntry)
238 {
239 /* The buffers are sorted and reads must start in a buffer if
240 anything should be taken from the buffer (at least for now). */
241 if (offCur < pBufDesc->off)
242 break;
243
244 /* Anything we can read from this buffer? */
245 uint64_t offCurBuf = offCur - pBufDesc->off;
246 if (offCurBuf < pBufDesc->cbFilled)
247 {
248 size_t const cbFromCurBuf = RT_MIN(pBufDesc->cbFilled - offCurBuf, cbDst);
249 memcpy(pbDst, pBufDesc->pbBuffer + offCurBuf, cbFromCurBuf);
250 pbDst += cbFromCurBuf;
251 cbDst -= cbFromCurBuf;
252 cbTotalRead += cbFromCurBuf;
253 offCur += cbFromCurBuf;
254 }
255
256 /* Discard buffers we've read past. */
257 if (pBufDesc->off + pBufDesc->cbFilled <= offCur)
258 {
259 RTListNodeRemove(&pBufDesc->ListEntry);
260 RTListAppend(&pThis->FreeList, &pBufDesc->ListEntry);
261 fPokeReader = true; /* Poke it as there are now more buffers available. */
262 }
263
264 /* Stop if we're done. */
265 if (!cbDst)
266 break;
267 }
268
269 pThis->offConsumer = offCur;
270 if (off != -1)
271 off = offCur;
272
273 if (!cbDst)
274 break;
275
276 /*
277 * Check if we've reached the end of the file/stream.
278 */
279 if (offCur >= pThis->offEof)
280 {
281 rc = pcbRead ? VINF_EOF : VERR_EOF;
282 break;
283 }
284
285
286 /*
287 * First time around we don't own the I/O critsect and need to take it
288 * and repeat the above buffer reading code.
289 */
290 if (!fOwnsIoCritSect)
291 {
292 RTCritSectLeave(&pThis->BufferCritSect);
293 RTCritSectEnter(&pThis->IoCritSect);
294 RTCritSectEnter(&pThis->BufferCritSect);
295 fOwnsIoCritSect = true;
296 continue;
297 }
298
299
300 /*
301 * Do a direct read of the remaining data.
302 */
303 if (off == -1)
304 {
305 RTFOFF offActual = RTVfsIoStrmTell(pThis->hIos);
306 if (offActual >= 0 && (uint64_t)offActual != offCur)
307 off = offCur;
308 }
309 RTSGSEG TmpSeg = { pbDst, cbDst };
310 RTSGBUF TmpSgBuf;
311 RTSgBufInit(&TmpSgBuf, &TmpSeg, 1);
312 size_t cbThisRead = cbDst;
313 rc = RTVfsIoStrmSgRead(pThis->hIos, off, pSgBuf, fBlocking, pcbRead ? &cbThisRead : NULL);
314 if (RT_SUCCESS(rc))
315 {
316 cbTotalRead = cbThisRead;
317 offCur += cbThisRead;
318 pThis->offConsumer = offCur;
319 if (rc != VINF_EOF)
320 fPokeReader = true;
321 else
322 pThis->offEof = offCur;
323 }
324 /* else if (rc == VERR_EOF): hard to say where exactly the current position
325 is here as cannot have had a non-NULL pcbRead. Set offEof later. */
326 break;
327 }
328 RTCritSectLeave(&pThis->BufferCritSect);
329 if (fOwnsIoCritSect)
330 RTCritSectLeave(&pThis->IoCritSect);
331 if (fPokeReader && rc != VINF_EOF && rc != VERR_EOF)
332 RTThreadUserSignal(pThis->hThread);
333
334 if (pcbRead)
335 *pcbRead = cbTotalRead;
336
337 return rc;
338}
339
340
341/**
342 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
343 */
344static DECLCALLBACK(int) rtVfsReadAhead_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
345{
346 AssertFailed();
347 return VERR_ACCESS_DENIED;
348}
349
350
351/**
352 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
353 */
354static DECLCALLBACK(int) rtVfsReadAhead_Flush(void *pvThis)
355{
356 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
357 return RTVfsIoStrmFlush(pThis->hIos);
358}
359
360
361/**
362 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
363 */
364static DECLCALLBACK(int) rtVfsReadAhead_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
365 uint32_t *pfRetEvents)
366{
367 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
368 if (pThis->hThread != NIL_RTTHREAD)
369 {
370 /** @todo poll one with read-ahead thread. */
371 return VERR_NOT_IMPLEMENTED;
372 }
373 return RTVfsIoStrmPoll(pThis->hIos, fEvents, cMillies, fIntr, pfRetEvents);
374}
375
376
377/**
378 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
379 */
380static DECLCALLBACK(int) rtVfsReadAhead_Tell(void *pvThis, PRTFOFF poffActual)
381{
382 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
383
384 RTCritSectEnter(&pThis->BufferCritSect);
385 *poffActual = pThis->offConsumer;
386 RTCritSectLeave(&pThis->BufferCritSect);
387
388 return VINF_SUCCESS;
389}
390
391
392/**
393 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
394 */
395static DECLCALLBACK(int) rtVfsReadAhead_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
396{
397 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
398 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
399
400 RTCritSectEnter(&pThis->IoCritSect);
401 /// @todo int rc = RTVfsFileSetMode(pThis->hFile, fMode, fMask);
402 int rc = VERR_NOT_SUPPORTED;
403 RTCritSectLeave(&pThis->IoCritSect);
404
405 return rc;
406}
407
408
409/**
410 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
411 */
412static DECLCALLBACK(int) rtVfsReadAhead_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
413 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
414{
415 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
416 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
417
418 RTCritSectEnter(&pThis->IoCritSect);
419 /// @todo int rc = RTVfsFileSetTimes(pThis->hFile, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
420 int rc = VERR_NOT_SUPPORTED;
421 RTCritSectLeave(&pThis->IoCritSect);
422
423 return rc;
424}
425
426
427/**
428 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
429 */
430static DECLCALLBACK(int) rtVfsReadAhead_SetOwner(void *pvThis, RTUID uid, RTGID gid)
431{
432 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
433 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
434
435 RTCritSectEnter(&pThis->IoCritSect);
436 /// @todo int rc = RTVfsFileSetOwner(pThis->hFile, uid, gid);
437 int rc = VERR_NOT_SUPPORTED;
438 RTCritSectLeave(&pThis->IoCritSect);
439
440 return rc;
441}
442
443
444/**
445 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
446 */
447static DECLCALLBACK(int) rtVfsReadAhead_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
448{
449 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
450 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
451
452 RTCritSectEnter(&pThis->IoCritSect); /* protects against concurrent I/O using the offset. */
453 RTCritSectEnter(&pThis->BufferCritSect); /* protects offConsumer */
454
455 uint64_t offActual = UINT64_MAX;
456 int rc = RTVfsFileSeek(pThis->hFile, offSeek, uMethod, &offActual);
457 if (RT_SUCCESS(rc))
458 pThis->offConsumer = offActual;
459
460 RTCritSectLeave(&pThis->BufferCritSect);
461 RTCritSectLeave(&pThis->IoCritSect);
462
463 return rc;
464}
465
466
467/**
468 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
469 */
470static DECLCALLBACK(int) rtVfsReadAhead_QuerySize(void *pvThis, uint64_t *pcbFile)
471{
472 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
473 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
474
475 RTCritSectEnter(&pThis->IoCritSect); /* paranoia */
476 int rc = RTVfsFileGetSize(pThis->hFile, pcbFile);
477 RTCritSectLeave(&pThis->IoCritSect);
478
479 return rc;
480}
481
482
483/**
484 * Read ahead I/O stream operations.
485 */
486DECL_HIDDEN_CONST(const RTVFSIOSTREAMOPS) g_VfsReadAheadIosOps =
487{ /* Stream */
488 { /* Obj */
489 RTVFSOBJOPS_VERSION,
490 RTVFSOBJTYPE_IO_STREAM,
491 "Read ahead I/O stream",
492 rtVfsReadAhead_Close,
493 rtVfsReadAhead_QueryInfo,
494 RTVFSOBJOPS_VERSION
495 },
496 RTVFSIOSTREAMOPS_VERSION,
497 RTVFSIOSTREAMOPS_FEAT_NO_SG,
498 rtVfsReadAhead_Read,
499 rtVfsReadAhead_Write,
500 rtVfsReadAhead_Flush,
501 rtVfsReadAhead_PollOne,
502 rtVfsReadAhead_Tell,
503 NULL /*Skip*/,
504 NULL /*ZeroFill*/,
505 RTVFSIOSTREAMOPS_VERSION,
506};
507
508
509/**
510 * Read ahead file operations.
511 */
512DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_VfsReadAheadFileOps =
513{
514 { /* Stream */
515 { /* Obj */
516 RTVFSOBJOPS_VERSION,
517 RTVFSOBJTYPE_FILE,
518 "Read ahead file",
519 rtVfsReadAhead_Close,
520 rtVfsReadAhead_QueryInfo,
521 RTVFSOBJOPS_VERSION
522 },
523 RTVFSIOSTREAMOPS_VERSION,
524 RTVFSIOSTREAMOPS_FEAT_NO_SG,
525 rtVfsReadAhead_Read,
526 rtVfsReadAhead_Write,
527 rtVfsReadAhead_Flush,
528 rtVfsReadAhead_PollOne,
529 rtVfsReadAhead_Tell,
530 NULL /*Skip*/,
531 NULL /*ZeroFill*/,
532 RTVFSIOSTREAMOPS_VERSION,
533 },
534 RTVFSFILEOPS_VERSION,
535 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
536 { /* ObjSet */
537 RTVFSOBJSETOPS_VERSION,
538 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
539 rtVfsReadAhead_SetMode,
540 rtVfsReadAhead_SetTimes,
541 rtVfsReadAhead_SetOwner,
542 RTVFSOBJSETOPS_VERSION
543 },
544 rtVfsReadAhead_Seek,
545 rtVfsReadAhead_QuerySize,
546 RTVFSFILEOPS_VERSION
547};
548
549
550/**
551 * @callback_method_impl{PFNRTTHREAD, Read ahead thread procedure}
552 */
553static DECLCALLBACK(int) rtVfsReadAheadThreadProc(RTTHREAD hThreadSelf, void *pvUser)
554{
555 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvUser;
556 Assert(pThis);
557
558 while (!pThis->fTerminateThread)
559 {
560 int rc;
561
562 /*
563 * Is there a buffer handy for reading ahead.
564 */
565 PRTVFSREADAHEADBUFDESC pBufDesc = NULL;
566 RTCritSectEnter(&pThis->BufferCritSect);
567 if (!pThis->fTerminateThread)
568 pBufDesc = RTListRemoveFirst(&pThis->FreeList, RTVFSREADAHEADBUFDESC, ListEntry);
569 RTCritSectLeave(&pThis->BufferCritSect);
570
571 if (pBufDesc)
572 {
573 /*
574 * Got a buffer, take the I/O lock and read into it.
575 */
576 rc = VERR_CALLBACK_RETURN;
577 RTCritSectEnter(&pThis->IoCritSect);
578 if (!pThis->fTerminateThread)
579 {
580
581 pBufDesc->off = RTVfsIoStrmTell(pThis->hIos);
582 size_t cbRead = 0;
583 size_t cbToRead = pThis->cbBuffer;
584 rc = RTVfsIoStrmRead(pThis->hIos, pBufDesc->pbBuffer, pThis->cbBuffer, true /*fBlocking*/, &cbRead);
585 if (RT_SUCCESS(rc))
586 {
587 if (rc == VINF_EOF)
588 pThis->offEof = pBufDesc->off + cbRead;
589 pBufDesc->cbFilled = (uint32_t)cbRead;
590
591 /*
592 * Put back the buffer. The consumer list is sorted by offset, but
593 * we should usually end up appending the buffer.
594 */
595 RTCritSectEnter(&pThis->BufferCritSect);
596 PRTVFSREADAHEADBUFDESC pAfter = RTListGetLast(&pThis->ConsumerList, RTVFSREADAHEADBUFDESC, ListEntry);
597 if (!pAfter || pAfter->off <= pBufDesc->off)
598 RTListAppend(&pThis->ConsumerList, &pBufDesc->ListEntry);
599 else
600 {
601 do
602 pAfter = RTListGetPrev(&pThis->ConsumerList, pAfter, RTVFSREADAHEADBUFDESC, ListEntry);
603 while (pAfter && pAfter->off > pBufDesc->off);
604 if (!pAfter)
605 RTListPrepend(&pThis->ConsumerList, &pBufDesc->ListEntry);
606 else
607 {
608 Assert(pAfter->off <= pBufDesc->off);
609 RTListNodeInsertAfter(&pAfter->ListEntry, &pBufDesc->ListEntry);
610 }
611 }
612 RTCritSectLeave(&pThis->BufferCritSect);
613 pBufDesc = NULL;
614
615#ifdef RT_STRICT
616 /* Verify the list ordering. */
617 unsigned cAsserted = 0;
618 uint64_t offAssert = 0;
619 PRTVFSREADAHEADBUFDESC pAssertCur;
620 RTListForEach(&pThis->ConsumerList, pAssertCur, RTVFSREADAHEADBUFDESC, ListEntry)
621 {
622 Assert(offAssert <= pAssertCur->off);
623 offAssert = pAssertCur->off;
624 Assert(cAsserted < pThis->cBuffers);
625 cAsserted++;
626 }
627#endif
628 }
629 else
630 Assert(rc != VERR_EOF);
631 }
632 RTCritSectLeave(&pThis->IoCritSect);
633
634 /*
635 * If we succeeded and we didn't yet reach the end of the stream,
636 * loop without delay to start processing the next buffer.
637 */
638 if (RT_LIKELY(!pBufDesc && rc != VINF_EOF))
639 continue;
640
641 /* Put any unused buffer back in the free list (termination/failure, not EOF). */
642 if (pBufDesc)
643 {
644 RTCritSectEnter(&pThis->BufferCritSect);
645 RTListPrepend(&pThis->FreeList, &pBufDesc->ListEntry);
646 RTCritSectLeave(&pThis->BufferCritSect);
647 }
648 if (pThis->fTerminateThread)
649 break;
650 }
651
652 /*
653 * Wait for more to do.
654 */
655 rc = RTThreadUserWait(hThreadSelf, RT_MS_1MIN);
656 if (RT_SUCCESS(rc))
657 rc = RTThreadUserReset(hThreadSelf);
658 }
659
660 return VINF_SUCCESS;
661}
662
663
664static int rtVfsCreateReadAheadInstance(RTVFSIOSTREAM hVfsIosSrc, RTVFSFILE hVfsFileSrc, uint32_t fFlags,
665 uint32_t cBuffers, uint32_t cbBuffer, PRTVFSIOSTREAM phVfsIos, PRTVFSFILE phVfsFile)
666{
667 /*
668 * Validate input a little.
669 */
670 int rc = VINF_SUCCESS;
671 AssertStmt(cBuffers < _4K, rc = VERR_OUT_OF_RANGE);
672 if (cBuffers == 0)
673 cBuffers = 4;
674 AssertStmt(cbBuffer <= _512K, rc = VERR_OUT_OF_RANGE);
675 if (cbBuffer == 0)
676 cbBuffer = _256K / cBuffers;
677 AssertStmt(!fFlags, rc = VERR_INVALID_FLAGS);
678
679 if (RT_SUCCESS(rc))
680 {
681 /*
682 * Create a file or I/O stream instance.
683 */
684 RTVFSFILE hVfsFileReadAhead = NIL_RTVFSFILE;
685 RTVFSIOSTREAM hVfsIosReadAhead = NIL_RTVFSIOSTREAM;
686 PRTVFSREADAHEAD pThis;
687 size_t cbThis = RT_OFFSETOF(RTVFSREADAHEAD, aBufDescs[cBuffers]);
688 if (hVfsFileSrc != NIL_RTVFSFILE)
689 rc = RTVfsNewFile(&g_VfsReadAheadFileOps, cbThis, RTFILE_O_READ, NIL_RTVFS, NIL_RTVFSLOCK,
690 &hVfsFileReadAhead, (void **)&pThis);
691 else
692 rc = RTVfsNewIoStream(&g_VfsReadAheadIosOps, cbThis, RTFILE_O_READ, NIL_RTVFS, NIL_RTVFSLOCK,
693 &hVfsIosReadAhead, (void **)&pThis);
694 if (RT_SUCCESS(rc))
695 {
696 RTListInit(&pThis->ConsumerList);
697 RTListInit(&pThis->FreeList);
698 pThis->hThread = NIL_RTTHREAD;
699 pThis->fTerminateThread = false;
700 pThis->fFlags = fFlags;
701 pThis->hFile = hVfsFileSrc;
702 pThis->hIos = hVfsIosSrc;
703 pThis->cBuffers = cBuffers;
704 pThis->cbBuffer = cbBuffer;
705 pThis->offEof = UINT64_MAX;
706 pThis->offConsumer = RTVfsIoStrmTell(hVfsIosSrc);
707 if ((RTFOFF)pThis->offConsumer >= 0)
708 {
709 rc = RTCritSectInit(&pThis->IoCritSect);
710 if (RT_SUCCESS(rc))
711 rc = RTCritSectInit(&pThis->BufferCritSect);
712 if (RT_SUCCESS(rc))
713 {
714 pThis->pbAllBuffers = (uint8_t *)RTMemPageAlloc(pThis->cbBuffer * pThis->cBuffers);
715 if (pThis->pbAllBuffers)
716 {
717 for (uint32_t i = 0; i < cBuffers; i++)
718 {
719 pThis->aBufDescs[i].cbFilled = 0;
720 pThis->aBufDescs[i].off = UINT64_MAX / 2;
721 pThis->aBufDescs[i].pbBuffer = &pThis->pbAllBuffers[cbBuffer * i];
722 RTListAppend(&pThis->FreeList, &pThis->aBufDescs[i].ListEntry);
723 }
724
725 /*
726 * Create thread.
727 */
728 rc = RTThreadCreate(&pThis->hThread, rtVfsReadAheadThreadProc, pThis, 0, RTTHREADTYPE_DEFAULT,
729 RTTHREADFLAGS_WAITABLE, "vfsreadahead");
730 if (RT_SUCCESS(rc))
731 {
732 /*
733 * We're good.
734 */
735 if (phVfsFile)
736 *phVfsFile = hVfsFileReadAhead;
737 else if (hVfsFileReadAhead == NIL_RTVFSFILE)
738 *phVfsIos = hVfsIosReadAhead;
739 else
740 {
741 *phVfsIos = RTVfsFileToIoStream(hVfsFileReadAhead);
742 RTVfsFileRelease(hVfsFileReadAhead);
743 AssertReturn(*phVfsIos != NIL_RTVFSIOSTREAM, VERR_INTERNAL_ERROR_5);
744 }
745 return VINF_SUCCESS;
746 }
747 }
748 }
749 }
750 else
751 rc = (int)pThis->offConsumer;
752 }
753 }
754
755 RTVfsFileRelease(hVfsFileSrc);
756 RTVfsIoStrmRelease(hVfsIosSrc);
757 return rc;
758}
759
760
761RTDECL(int) RTVfsCreateReadAheadForIoStream(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, uint32_t cBuffers, uint32_t cbBuffer,
762 PRTVFSIOSTREAM phVfsIos)
763{
764 AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
765 *phVfsIos = NIL_RTVFSIOSTREAM;
766
767 /*
768 * Retain the input stream, trying to obtain a file handle too so we can
769 * fully mirror it.
770 */
771 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIos);
772 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
773 RTVFSFILE hVfsFile = RTVfsIoStrmToFile(hVfsIos);
774
775 /*
776 * Do the job. (This always consumes the above retained references.)
777 */
778 return rtVfsCreateReadAheadInstance(hVfsIos, hVfsFile, fFlags, cBuffers, cbBuffer, phVfsIos, NULL);
779}
780
781
782RTDECL(int) RTVfsCreateReadAheadForFile(RTVFSFILE hVfsFile, uint32_t fFlags, uint32_t cBuffers, uint32_t cbBuffer,
783 PRTVFSFILE phVfsFile)
784{
785 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
786 *phVfsFile = NIL_RTVFSFILE;
787
788 /*
789 * Retain the input file and cast it o an I/O stream.
790 */
791 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
792 AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_INVALID_HANDLE);
793 uint32_t cRefs = RTVfsFileRetain(hVfsFile);
794 AssertReturnStmt(cRefs != UINT32_MAX, RTVfsIoStrmRelease(hVfsIos), VERR_INVALID_HANDLE);
795
796 /*
797 * Do the job. (This always consumes the above retained references.)
798 */
799 return rtVfsCreateReadAheadInstance(hVfsIos, hVfsFile, fFlags, cBuffers, cbBuffer, NULL, phVfsFile);
800}
801
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette