VirtualBox

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

Last change on this file since 96563 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.6 KB
Line 
1/* $Id: vfsreadahead.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Read-Ahead Thread.
4 */
5
6/*
7 * Copyright (C) 2010-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_VFS
42#include "internal/iprt.h"
43#include <iprt/vfs.h>
44
45#include <iprt/asm.h>
46#include <iprt/assert.h>
47#include <iprt/err.h>
48#include <iprt/file.h>
49#include <iprt/list.h>
50#include <iprt/log.h>
51#include <iprt/poll.h>
52#include <iprt/string.h>
53#include <iprt/vfslowlevel.h>
54
55
56
57/*********************************************************************************************************************************
58* Header Files *
59*********************************************************************************************************************************/
60#include "internal/iprt.h"
61#include <iprt/vfs.h>
62
63#include <iprt/critsect.h>
64#include <iprt/err.h>
65#include <iprt/mem.h>
66#include <iprt/thread.h>
67
68
69/*********************************************************************************************************************************
70* Structures and Typedefs *
71*********************************************************************************************************************************/
72/**
73 * Buffer descriptor.
74 */
75typedef struct RTVFSREADAHEADBUFDESC
76{
77 /** List entry. */
78 RTLISTNODE ListEntry;
79 /** The offset of this extent within the file. */
80 uint64_t off;
81 /** The amount of the buffer that has been filled.
82 * (Buffer size is RTVFSREADAHEAD::cbBuffer.) */
83 uint32_t cbFilled;
84 /** */
85 uint32_t volatile fReserved;
86 /** Pointer to the buffer. */
87 uint8_t *pbBuffer;
88} RTVFSREADAHEADBUFDESC;
89/** Pointer to a memory file extent. */
90typedef RTVFSREADAHEADBUFDESC *PRTVFSREADAHEADBUFDESC;
91
92/**
93 * Read ahead file or I/O stream.
94 */
95typedef struct RTVFSREADAHEAD
96{
97 /** The I/O critical section (protects offActual).
98 * The thread doing I/O or seeking always need to own this. */
99 RTCRITSECT IoCritSect;
100
101 /** The critical section protecting the buffer lists and offConsumer.
102 *
103 * This can be taken while holding IoCritSect as that eliminates a race
104 * condition between the read ahead thread inserting into ConsumerList and
105 * a consumer thread deciding to do a direct read. */
106 RTCRITSECT BufferCritSect;
107 /** List of buffers available for consumption.
108 * The producer thread (hThread) puts buffers into this list once it's done
109 * reading into them. The consumer moves them to the FreeList once the
110 * current position has passed beyond each buffer. */
111 RTLISTANCHOR ConsumerList;
112 /** List of buffers available for the producer. */
113 RTLISTANCHOR FreeList;
114
115 /** The current file position from the consumer point of view. */
116 uint64_t offConsumer;
117
118 /** The end-of-file(/stream) offset. This is initially UINT64_MAX and later
119 * set when reading past EOF. */
120 uint64_t offEof;
121
122 /** The read ahead thread. */
123 RTTHREAD hThread;
124 /** Set when we want the thread to terminate. */
125 bool volatile fTerminateThread;
126 /** Creation flags. */
127 uint32_t fFlags;
128
129 /** The I/O stream we read from. */
130 RTVFSIOSTREAM hIos;
131 /** The file face of hIos, if we're fronting for an actual file. */
132 RTVFSFILE hFile;
133 /** The buffer size. */
134 uint32_t cbBuffer;
135 /** The number of buffers. */
136 uint32_t cBuffers;
137 /** Single big buffer allocation, cBuffers * cbBuffer in size. */
138 uint8_t *pbAllBuffers;
139 /** Array of buffer descriptors (cBuffers in size). */
140 RTVFSREADAHEADBUFDESC aBufDescs[1];
141} RTVFSREADAHEAD;
142/** Pointer to a memory file. */
143typedef RTVFSREADAHEAD *PRTVFSREADAHEAD;
144
145
146
147/**
148 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
149 */
150static DECLCALLBACK(int) rtVfsReadAhead_Close(void *pvThis)
151{
152 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
153 int rc;
154
155 /*
156 * Stop the read-ahead thread.
157 */
158 if (pThis->hThread != NIL_RTTHREAD)
159 {
160 ASMAtomicWriteBool(&pThis->fTerminateThread, true);
161 rc = RTThreadUserSignal(pThis->hThread);
162 AssertRC(rc);
163 rc = RTThreadWait(pThis->hThread, RT_INDEFINITE_WAIT, NULL);
164 AssertRCReturn(rc, rc);
165 pThis->hThread = NIL_RTTHREAD;
166 }
167
168 /*
169 * Release the upstream objects.
170 */
171 RTCritSectEnter(&pThis->IoCritSect);
172
173 RTVfsIoStrmRelease(pThis->hIos);
174 pThis->hIos = NIL_RTVFSIOSTREAM;
175 RTVfsFileRelease(pThis->hFile);
176 pThis->hFile = NIL_RTVFSFILE;
177
178 RTCritSectLeave(&pThis->IoCritSect);
179
180 /*
181 * Free the buffers.
182 */
183 RTCritSectEnter(&pThis->BufferCritSect);
184 if (pThis->pbAllBuffers)
185 {
186 RTMemPageFree(pThis->pbAllBuffers, pThis->cBuffers * pThis->cbBuffer);
187 pThis->pbAllBuffers = NULL;
188 }
189 RTCritSectLeave(&pThis->BufferCritSect);
190
191 /*
192 * Destroy the critical sections.
193 */
194 RTCritSectDelete(&pThis->BufferCritSect);
195 RTCritSectDelete(&pThis->IoCritSect);
196
197 return VINF_SUCCESS;
198}
199
200
201/**
202 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
203 */
204static DECLCALLBACK(int) rtVfsReadAhead_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
205{
206 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
207 return RTVfsIoStrmQueryInfo(pThis->hIos, pObjInfo, enmAddAttr);
208}
209
210
211/**
212 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
213 */
214static DECLCALLBACK(int) rtVfsReadAhead_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
215{
216 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
217
218 Assert(pSgBuf->cSegs == 1); /* Caller deals with multiple SGs. */
219
220 /*
221 * We loop here to repeat the buffer search after entering the I/O critical
222 * section, just in case a buffer got inserted while we were waiting for it.
223 */
224 int rc = VINF_SUCCESS;
225 uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
226 size_t cbDst = pSgBuf->paSegs[0].cbSeg;
227 size_t cbTotalRead = 0;
228 bool fPokeReader = false;
229 bool fOwnsIoCritSect = false;
230 RTCritSectEnter(&pThis->BufferCritSect);
231 for (;;)
232 {
233 /*
234 * Try satisfy the read from the buffers.
235 */
236 uint64_t offCur = pThis->offConsumer;
237 if (off != -1)
238 {
239 offCur = (uint64_t)off;
240 if (pThis->offConsumer != offCur)
241 fPokeReader = true; /* If the current position changed, poke it in case it stopped at EOF. */
242 pThis->offConsumer = offCur;
243 }
244
245 PRTVFSREADAHEADBUFDESC pBufDesc, pNextBufDesc;
246 RTListForEachSafe(&pThis->ConsumerList, pBufDesc, pNextBufDesc, RTVFSREADAHEADBUFDESC, ListEntry)
247 {
248 /* The buffers are sorted and reads must start in a buffer if
249 anything should be taken from the buffer (at least for now). */
250 if (offCur < pBufDesc->off)
251 break;
252
253 /* Anything we can read from this buffer? */
254 uint64_t offCurBuf = offCur - pBufDesc->off;
255 if (offCurBuf < pBufDesc->cbFilled)
256 {
257 size_t const cbFromCurBuf = RT_MIN(pBufDesc->cbFilled - offCurBuf, cbDst);
258 memcpy(pbDst, pBufDesc->pbBuffer + offCurBuf, cbFromCurBuf);
259 pbDst += cbFromCurBuf;
260 cbDst -= cbFromCurBuf;
261 cbTotalRead += cbFromCurBuf;
262 offCur += cbFromCurBuf;
263 }
264
265 /* Discard buffers we've read past. */
266 if (pBufDesc->off + pBufDesc->cbFilled <= offCur)
267 {
268 RTListNodeRemove(&pBufDesc->ListEntry);
269 RTListAppend(&pThis->FreeList, &pBufDesc->ListEntry);
270 fPokeReader = true; /* Poke it as there are now more buffers available. */
271 }
272
273 /* Stop if we're done. */
274 if (!cbDst)
275 break;
276 }
277
278 pThis->offConsumer = offCur;
279 if (off != -1)
280 off = offCur;
281
282 if (!cbDst)
283 break;
284
285 /*
286 * Check if we've reached the end of the file/stream.
287 */
288 if (offCur >= pThis->offEof)
289 {
290 rc = pcbRead ? VINF_EOF : VERR_EOF;
291 Log(("rtVfsReadAhead_Read: ret %Rrc; offCur=%#llx offEof=%#llx\n", rc, offCur, pThis->offEof));
292 break;
293 }
294
295
296 /*
297 * First time around we don't own the I/O critsect and need to take it
298 * and repeat the above buffer reading code.
299 */
300 if (!fOwnsIoCritSect)
301 {
302 RTCritSectLeave(&pThis->BufferCritSect);
303 RTCritSectEnter(&pThis->IoCritSect);
304 RTCritSectEnter(&pThis->BufferCritSect);
305 fOwnsIoCritSect = true;
306 continue;
307 }
308
309
310 /*
311 * Do a direct read of the remaining data.
312 */
313 if (off == -1)
314 {
315 RTFOFF offActual = RTVfsIoStrmTell(pThis->hIos);
316 if (offActual >= 0 && (uint64_t)offActual != offCur)
317 off = offCur;
318 }
319 RTSGSEG TmpSeg = { pbDst, cbDst };
320 RTSGBUF TmpSgBuf;
321 RTSgBufInit(&TmpSgBuf, &TmpSeg, 1);
322 size_t cbThisRead = cbDst;
323 rc = RTVfsIoStrmSgRead(pThis->hIos, off, &TmpSgBuf, fBlocking, pcbRead ? &cbThisRead : NULL);
324 if (RT_SUCCESS(rc))
325 {
326 cbTotalRead += cbThisRead;
327 offCur += cbThisRead;
328 pThis->offConsumer = offCur;
329 if (rc != VINF_EOF)
330 fPokeReader = true;
331 else
332 {
333 pThis->offEof = offCur;
334 Log(("rtVfsReadAhead_Read: EOF %llu (%#llx)\n", pThis->offEof, pThis->offEof));
335 }
336 }
337 /* else if (rc == VERR_EOF): hard to say where exactly the current position
338 is here as cannot have had a non-NULL pcbRead. Set offEof later. */
339 break;
340 }
341 RTCritSectLeave(&pThis->BufferCritSect);
342 if (fOwnsIoCritSect)
343 RTCritSectLeave(&pThis->IoCritSect);
344 if (fPokeReader && rc != VINF_EOF && rc != VERR_EOF)
345 RTThreadUserSignal(pThis->hThread);
346
347 if (pcbRead)
348 *pcbRead = cbTotalRead;
349 Assert(cbTotalRead <= pSgBuf->paSegs[0].cbSeg);
350
351 return rc;
352}
353
354
355/**
356 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
357 */
358static DECLCALLBACK(int) rtVfsReadAhead_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
359{
360 RT_NOREF_PV(pvThis); RT_NOREF_PV(off); RT_NOREF_PV(pSgBuf); RT_NOREF_PV(fBlocking); RT_NOREF_PV(pcbWritten);
361 AssertFailed();
362 return VERR_ACCESS_DENIED;
363}
364
365
366/**
367 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
368 */
369static DECLCALLBACK(int) rtVfsReadAhead_Flush(void *pvThis)
370{
371 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
372 return RTVfsIoStrmFlush(pThis->hIos);
373}
374
375
376/**
377 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
378 */
379static DECLCALLBACK(int) rtVfsReadAhead_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
380 uint32_t *pfRetEvents)
381{
382 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
383 if (pThis->hThread != NIL_RTTHREAD)
384 {
385 /** @todo poll one with read-ahead thread. */
386 return VERR_NOT_IMPLEMENTED;
387 }
388 return RTVfsIoStrmPoll(pThis->hIos, fEvents, cMillies, fIntr, pfRetEvents);
389}
390
391
392/**
393 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
394 */
395static DECLCALLBACK(int) rtVfsReadAhead_Tell(void *pvThis, PRTFOFF poffActual)
396{
397 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
398
399 RTCritSectEnter(&pThis->BufferCritSect);
400 *poffActual = pThis->offConsumer;
401 RTCritSectLeave(&pThis->BufferCritSect);
402
403 return VINF_SUCCESS;
404}
405
406
407/**
408 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
409 */
410static DECLCALLBACK(int) rtVfsReadAhead_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
411{
412 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
413 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
414
415 RTCritSectEnter(&pThis->IoCritSect);
416 RT_NOREF_PV(fMode); RT_NOREF_PV(fMask); /// @todo int rc = RTVfsFileSetMode(pThis->hFile, fMode, fMask);
417 int rc = VERR_NOT_SUPPORTED;
418 RTCritSectLeave(&pThis->IoCritSect);
419
420 return rc;
421}
422
423
424/**
425 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
426 */
427static DECLCALLBACK(int) rtVfsReadAhead_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
428 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
429{
430 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
431 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
432
433 RTCritSectEnter(&pThis->IoCritSect);
434 RT_NOREF_PV(pAccessTime); RT_NOREF_PV(pModificationTime); RT_NOREF_PV(pChangeTime); RT_NOREF_PV(pBirthTime);
435 /// @todo int rc = RTVfsFileSetTimes(pThis->hFile, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
436 int rc = VERR_NOT_SUPPORTED;
437 RTCritSectLeave(&pThis->IoCritSect);
438
439 return rc;
440}
441
442
443/**
444 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
445 */
446static DECLCALLBACK(int) rtVfsReadAhead_SetOwner(void *pvThis, RTUID uid, RTGID gid)
447{
448 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
449 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
450
451 RTCritSectEnter(&pThis->IoCritSect);
452 RT_NOREF_PV(uid); RT_NOREF_PV(gid);
453 /// @todo int rc = RTVfsFileSetOwner(pThis->hFile, uid, gid);
454 int rc = VERR_NOT_SUPPORTED;
455 RTCritSectLeave(&pThis->IoCritSect);
456
457 return rc;
458}
459
460
461/**
462 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
463 */
464static DECLCALLBACK(int) rtVfsReadAhead_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
465{
466 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
467 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
468
469 RTCritSectEnter(&pThis->IoCritSect); /* protects against concurrent I/O using the offset. */
470 RTCritSectEnter(&pThis->BufferCritSect); /* protects offConsumer */
471
472 uint64_t offActual = UINT64_MAX;
473 int rc = RTVfsFileSeek(pThis->hFile, offSeek, uMethod, &offActual);
474 if (RT_SUCCESS(rc))
475 {
476 pThis->offConsumer = offActual;
477 if (poffActual)
478 *poffActual = offActual;
479 }
480
481 RTCritSectLeave(&pThis->BufferCritSect);
482 RTCritSectLeave(&pThis->IoCritSect);
483
484 return rc;
485}
486
487
488/**
489 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
490 */
491static DECLCALLBACK(int) rtVfsReadAhead_QuerySize(void *pvThis, uint64_t *pcbFile)
492{
493 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
494 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
495
496 RTCritSectEnter(&pThis->IoCritSect); /* paranoia */
497 int rc = RTVfsFileQuerySize(pThis->hFile, pcbFile);
498 RTCritSectLeave(&pThis->IoCritSect);
499
500 return rc;
501}
502
503
504/**
505 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
506 */
507static DECLCALLBACK(int) rtVfsReadAhead_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
508{
509 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
510 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
511
512 RTCritSectEnter(&pThis->IoCritSect); /* paranoia */
513 int rc = RTVfsFileSetSize(pThis->hFile, cbFile, fFlags);
514 RTCritSectLeave(&pThis->IoCritSect);
515
516 return rc;
517}
518
519
520/**
521 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
522 */
523static DECLCALLBACK(int) rtVfsReadAhead_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
524{
525 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
526 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
527
528 RTCritSectEnter(&pThis->IoCritSect); /* paranoia */
529 int rc = RTVfsFileQueryMaxSize(pThis->hFile, pcbMax);
530 RTCritSectLeave(&pThis->IoCritSect);
531
532 return rc;
533}
534
535
536/**
537 * Read ahead I/O stream operations.
538 */
539DECL_HIDDEN_CONST(const RTVFSIOSTREAMOPS) g_VfsReadAheadIosOps =
540{ /* Stream */
541 { /* Obj */
542 RTVFSOBJOPS_VERSION,
543 RTVFSOBJTYPE_IO_STREAM,
544 "Read ahead I/O stream",
545 rtVfsReadAhead_Close,
546 rtVfsReadAhead_QueryInfo,
547 NULL,
548 RTVFSOBJOPS_VERSION
549 },
550 RTVFSIOSTREAMOPS_VERSION,
551 RTVFSIOSTREAMOPS_FEAT_NO_SG,
552 rtVfsReadAhead_Read,
553 rtVfsReadAhead_Write,
554 rtVfsReadAhead_Flush,
555 rtVfsReadAhead_PollOne,
556 rtVfsReadAhead_Tell,
557 NULL /*Skip*/,
558 NULL /*ZeroFill*/,
559 RTVFSIOSTREAMOPS_VERSION,
560};
561
562
563/**
564 * Read ahead file operations.
565 */
566DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_VfsReadAheadFileOps =
567{
568 { /* Stream */
569 { /* Obj */
570 RTVFSOBJOPS_VERSION,
571 RTVFSOBJTYPE_FILE,
572 "Read ahead file",
573 rtVfsReadAhead_Close,
574 rtVfsReadAhead_QueryInfo,
575 NULL,
576 RTVFSOBJOPS_VERSION
577 },
578 RTVFSIOSTREAMOPS_VERSION,
579 RTVFSIOSTREAMOPS_FEAT_NO_SG,
580 rtVfsReadAhead_Read,
581 rtVfsReadAhead_Write,
582 rtVfsReadAhead_Flush,
583 rtVfsReadAhead_PollOne,
584 rtVfsReadAhead_Tell,
585 NULL /*Skip*/,
586 NULL /*ZeroFill*/,
587 RTVFSIOSTREAMOPS_VERSION,
588 },
589 RTVFSFILEOPS_VERSION,
590 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
591 { /* ObjSet */
592 RTVFSOBJSETOPS_VERSION,
593 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
594 rtVfsReadAhead_SetMode,
595 rtVfsReadAhead_SetTimes,
596 rtVfsReadAhead_SetOwner,
597 RTVFSOBJSETOPS_VERSION
598 },
599 rtVfsReadAhead_Seek,
600 rtVfsReadAhead_QuerySize,
601 rtVfsReadAhead_SetSize,
602 rtVfsReadAhead_QueryMaxSize,
603 RTVFSFILEOPS_VERSION
604};
605
606
607/**
608 * @callback_method_impl{PFNRTTHREAD, Read ahead thread procedure}
609 */
610static DECLCALLBACK(int) rtVfsReadAheadThreadProc(RTTHREAD hThreadSelf, void *pvUser)
611{
612 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvUser;
613 Assert(pThis);
614
615 while (!pThis->fTerminateThread)
616 {
617 int rc;
618
619 /*
620 * Is there a buffer handy for reading ahead.
621 */
622 PRTVFSREADAHEADBUFDESC pBufDesc = NULL;
623 RTCritSectEnter(&pThis->BufferCritSect);
624 if (!pThis->fTerminateThread)
625 pBufDesc = RTListRemoveFirst(&pThis->FreeList, RTVFSREADAHEADBUFDESC, ListEntry);
626 RTCritSectLeave(&pThis->BufferCritSect);
627
628 if (pBufDesc)
629 {
630 /*
631 * Got a buffer, take the I/O lock and read into it.
632 */
633 rc = VERR_CALLBACK_RETURN;
634 RTCritSectEnter(&pThis->IoCritSect);
635 if (!pThis->fTerminateThread)
636 {
637
638 pBufDesc->off = RTVfsIoStrmTell(pThis->hIos);
639 size_t cbRead = 0;
640 rc = RTVfsIoStrmRead(pThis->hIos, pBufDesc->pbBuffer, pThis->cbBuffer, true /*fBlocking*/, &cbRead);
641 if (RT_SUCCESS(rc))
642 {
643 if (rc == VINF_EOF)
644 {
645 pThis->offEof = pBufDesc->off + cbRead;
646 Log(("rtVfsReadAheadThreadProc: EOF %llu (%#llx)\n", pThis->offEof, pThis->offEof));
647 }
648 pBufDesc->cbFilled = (uint32_t)cbRead;
649
650 /*
651 * Put back the buffer. The consumer list is sorted by offset, but
652 * we should usually end up appending the buffer.
653 */
654 RTCritSectEnter(&pThis->BufferCritSect);
655 PRTVFSREADAHEADBUFDESC pAfter = RTListGetLast(&pThis->ConsumerList, RTVFSREADAHEADBUFDESC, ListEntry);
656 if (!pAfter || pAfter->off <= pBufDesc->off)
657 RTListAppend(&pThis->ConsumerList, &pBufDesc->ListEntry);
658 else
659 {
660 do
661 pAfter = RTListGetPrev(&pThis->ConsumerList, pAfter, RTVFSREADAHEADBUFDESC, ListEntry);
662 while (pAfter && pAfter->off > pBufDesc->off);
663 if (!pAfter)
664 RTListPrepend(&pThis->ConsumerList, &pBufDesc->ListEntry);
665 else
666 {
667 Assert(pAfter->off <= pBufDesc->off);
668 RTListNodeInsertAfter(&pAfter->ListEntry, &pBufDesc->ListEntry);
669 }
670 }
671 RTCritSectLeave(&pThis->BufferCritSect);
672 pBufDesc = NULL;
673
674#ifdef RT_STRICT
675 /* Verify the list ordering. */
676 unsigned cAsserted = 0;
677 uint64_t offAssert = 0;
678 PRTVFSREADAHEADBUFDESC pAssertCur;
679 RTListForEach(&pThis->ConsumerList, pAssertCur, RTVFSREADAHEADBUFDESC, ListEntry)
680 {
681 Assert(offAssert <= pAssertCur->off);
682 offAssert = pAssertCur->off;
683 Assert(cAsserted < pThis->cBuffers);
684 cAsserted++;
685 }
686#endif
687 }
688 else
689 Assert(rc != VERR_EOF);
690 }
691 RTCritSectLeave(&pThis->IoCritSect);
692
693 /*
694 * If we succeeded and we didn't yet reach the end of the stream,
695 * loop without delay to start processing the next buffer.
696 */
697 if (RT_LIKELY(!pBufDesc && rc != VINF_EOF))
698 continue;
699
700 /* Put any unused buffer back in the free list (termination/failure, not EOF). */
701 if (pBufDesc)
702 {
703 RTCritSectEnter(&pThis->BufferCritSect);
704 RTListPrepend(&pThis->FreeList, &pBufDesc->ListEntry);
705 RTCritSectLeave(&pThis->BufferCritSect);
706 }
707 if (pThis->fTerminateThread)
708 break;
709 }
710
711 /*
712 * Wait for more to do.
713 */
714 rc = RTThreadUserWait(hThreadSelf, RT_MS_1MIN);
715 if (RT_SUCCESS(rc))
716 rc = RTThreadUserReset(hThreadSelf);
717 }
718
719 return VINF_SUCCESS;
720}
721
722
723static int rtVfsCreateReadAheadInstance(RTVFSIOSTREAM hVfsIosSrc, RTVFSFILE hVfsFileSrc, uint32_t fFlags,
724 uint32_t cBuffers, uint32_t cbBuffer, PRTVFSIOSTREAM phVfsIos, PRTVFSFILE phVfsFile)
725{
726 /*
727 * Validate input a little.
728 */
729 int rc = VINF_SUCCESS;
730 AssertStmt(cBuffers < _4K, rc = VERR_OUT_OF_RANGE);
731 if (cBuffers == 0)
732 cBuffers = 4;
733 AssertStmt(cbBuffer <= _4M, rc = VERR_OUT_OF_RANGE);
734 if (cbBuffer == 0)
735 cbBuffer = _256K / cBuffers;
736 AssertStmt(cbBuffer * cBuffers < (ARCH_BITS < 64 ? _64M : _256M), rc = VERR_OUT_OF_RANGE);
737 AssertStmt(!fFlags, rc = VERR_INVALID_FLAGS);
738
739 if (RT_SUCCESS(rc))
740 {
741 /*
742 * Create a file or I/O stream instance.
743 */
744 RTVFSFILE hVfsFileReadAhead = NIL_RTVFSFILE;
745 RTVFSIOSTREAM hVfsIosReadAhead = NIL_RTVFSIOSTREAM;
746 PRTVFSREADAHEAD pThis;
747 size_t cbThis = RT_UOFFSETOF_DYN(RTVFSREADAHEAD, aBufDescs[cBuffers]);
748 if (hVfsFileSrc != NIL_RTVFSFILE)
749 rc = RTVfsNewFile(&g_VfsReadAheadFileOps, cbThis, RTFILE_O_READ, NIL_RTVFS, NIL_RTVFSLOCK,
750 &hVfsFileReadAhead, (void **)&pThis);
751 else
752 rc = RTVfsNewIoStream(&g_VfsReadAheadIosOps, cbThis, RTFILE_O_READ, NIL_RTVFS, NIL_RTVFSLOCK,
753 &hVfsIosReadAhead, (void **)&pThis);
754 if (RT_SUCCESS(rc))
755 {
756 RTListInit(&pThis->ConsumerList);
757 RTListInit(&pThis->FreeList);
758 pThis->hThread = NIL_RTTHREAD;
759 pThis->fTerminateThread = false;
760 pThis->fFlags = fFlags;
761 pThis->hFile = hVfsFileSrc;
762 pThis->hIos = hVfsIosSrc;
763 pThis->cBuffers = cBuffers;
764 pThis->cbBuffer = cbBuffer;
765 pThis->offEof = UINT64_MAX;
766 pThis->offConsumer = RTVfsIoStrmTell(hVfsIosSrc);
767 if ((RTFOFF)pThis->offConsumer >= 0)
768 {
769 rc = RTCritSectInit(&pThis->IoCritSect);
770 if (RT_SUCCESS(rc))
771 rc = RTCritSectInit(&pThis->BufferCritSect);
772 if (RT_SUCCESS(rc))
773 {
774 pThis->pbAllBuffers = (uint8_t *)RTMemPageAlloc(pThis->cbBuffer * pThis->cBuffers);
775 if (pThis->pbAllBuffers)
776 {
777 for (uint32_t i = 0; i < cBuffers; i++)
778 {
779 pThis->aBufDescs[i].cbFilled = 0;
780 pThis->aBufDescs[i].off = UINT64_MAX / 2;
781 pThis->aBufDescs[i].pbBuffer = &pThis->pbAllBuffers[cbBuffer * i];
782 RTListAppend(&pThis->FreeList, &pThis->aBufDescs[i].ListEntry);
783 }
784
785 /*
786 * Create thread.
787 */
788 rc = RTThreadCreate(&pThis->hThread, rtVfsReadAheadThreadProc, pThis, 0, RTTHREADTYPE_DEFAULT,
789 RTTHREADFLAGS_WAITABLE, "vfsreadahead");
790 if (RT_SUCCESS(rc))
791 {
792 /*
793 * We're good.
794 */
795 if (phVfsFile)
796 *phVfsFile = hVfsFileReadAhead;
797 else if (hVfsFileReadAhead == NIL_RTVFSFILE)
798 *phVfsIos = hVfsIosReadAhead;
799 else
800 {
801 *phVfsIos = RTVfsFileToIoStream(hVfsFileReadAhead);
802 RTVfsFileRelease(hVfsFileReadAhead);
803 AssertReturn(*phVfsIos != NIL_RTVFSIOSTREAM, VERR_INTERNAL_ERROR_5);
804 }
805 return VINF_SUCCESS;
806 }
807 }
808 }
809 }
810 else
811 rc = (int)pThis->offConsumer;
812 }
813 }
814
815 RTVfsFileRelease(hVfsFileSrc);
816 RTVfsIoStrmRelease(hVfsIosSrc);
817 return rc;
818}
819
820
821RTDECL(int) RTVfsCreateReadAheadForIoStream(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, uint32_t cBuffers, uint32_t cbBuffer,
822 PRTVFSIOSTREAM phVfsIos)
823{
824 AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
825 *phVfsIos = NIL_RTVFSIOSTREAM;
826
827 /*
828 * Retain the input stream, trying to obtain a file handle too so we can
829 * fully mirror it.
830 */
831 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIos);
832 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
833 RTVFSFILE hVfsFile = RTVfsIoStrmToFile(hVfsIos);
834
835 /*
836 * Do the job. (This always consumes the above retained references.)
837 */
838 return rtVfsCreateReadAheadInstance(hVfsIos, hVfsFile, fFlags, cBuffers, cbBuffer, phVfsIos, NULL);
839}
840
841
842RTDECL(int) RTVfsCreateReadAheadForFile(RTVFSFILE hVfsFile, uint32_t fFlags, uint32_t cBuffers, uint32_t cbBuffer,
843 PRTVFSFILE phVfsFile)
844{
845 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
846 *phVfsFile = NIL_RTVFSFILE;
847
848 /*
849 * Retain the input file and cast it o an I/O stream.
850 */
851 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
852 AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_INVALID_HANDLE);
853 uint32_t cRefs = RTVfsFileRetain(hVfsFile);
854 AssertReturnStmt(cRefs != UINT32_MAX, RTVfsIoStrmRelease(hVfsIos), VERR_INVALID_HANDLE);
855
856 /*
857 * Do the job. (This always consumes the above retained references.)
858 */
859 return rtVfsCreateReadAheadInstance(hVfsIos, hVfsFile, fFlags, cBuffers, cbBuffer, NULL, phVfsFile);
860}
861
862
863/**
864 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
865 */
866static DECLCALLBACK(int) rtVfsChainReadAhead_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
867 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
868{
869 RT_NOREF(pProviderReg, poffError, pErrInfo);
870
871 /*
872 * Basics.
873 */
874 if ( pElement->enmType != RTVFSOBJTYPE_FILE
875 && pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
876 return VERR_VFS_CHAIN_ONLY_FILE_OR_IOS;
877 if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
878 return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
879 if ( pElement->enmTypeIn != RTVFSOBJTYPE_FILE
880 && pElement->enmTypeIn != RTVFSOBJTYPE_IO_STREAM)
881 return VERR_VFS_CHAIN_TAKES_FILE_OR_IOS;
882 if (pSpec->fOpenFile & RTFILE_O_WRITE)
883 return VERR_VFS_CHAIN_READ_ONLY_IOS;
884 if (pElement->cArgs > 2)
885 return VERR_VFS_CHAIN_AT_MOST_TWO_ARGS;
886
887 /*
888 * Parse the two optional arguments.
889 */
890 uint32_t cBuffers = 0;
891 if (pElement->cArgs > 0)
892 {
893 const char *psz = pElement->paArgs[0].psz;
894 if (*psz)
895 {
896 int rc = RTStrToUInt32Full(psz, 0, &cBuffers);
897 if (RT_FAILURE(rc))
898 {
899 *poffError = pElement->paArgs[0].offSpec;
900 return VERR_VFS_CHAIN_INVALID_ARGUMENT;
901 }
902 }
903 }
904
905 uint32_t cbBuffer = 0;
906 if (pElement->cArgs > 1)
907 {
908 const char *psz = pElement->paArgs[1].psz;
909 if (*psz)
910 {
911 int rc = RTStrToUInt32Full(psz, 0, &cbBuffer);
912 if (RT_FAILURE(rc))
913 {
914 *poffError = pElement->paArgs[1].offSpec;
915 return VERR_VFS_CHAIN_INVALID_ARGUMENT;
916 }
917 }
918 }
919
920 /*
921 * Save the parsed arguments in the spec since their both optional.
922 */
923 pElement->uProvider = RT_MAKE_U64(cBuffers, cbBuffer);
924
925 return VINF_SUCCESS;
926}
927
928
929/**
930 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
931 */
932static DECLCALLBACK(int) rtVfsChainReadAhead_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
933 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
934 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
935{
936 RT_NOREF(pProviderReg, pSpec, pElement, poffError, pErrInfo);
937 AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
938
939 /* Try for a file if we can. */
940 int rc;
941 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
942 if (hVfsFileIn != NIL_RTVFSFILE)
943 {
944 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
945 rc = RTVfsCreateReadAheadForFile(hVfsFileIn, 0 /*fFlags*/, RT_LO_U32(pElement->uProvider),
946 RT_HI_U32(pElement->uProvider), &hVfsFile);
947 RTVfsFileRelease(hVfsFileIn);
948 if (RT_SUCCESS(rc))
949 {
950 *phVfsObj = RTVfsObjFromFile(hVfsFile);
951 RTVfsFileRelease(hVfsFile);
952 if (*phVfsObj != NIL_RTVFSOBJ)
953 return VINF_SUCCESS;
954 rc = VERR_VFS_CHAIN_CAST_FAILED;
955 }
956 }
957 else if (pElement->enmType == RTVFSOBJTYPE_IO_STREAM)
958 {
959 RTVFSIOSTREAM hVfsIosIn = RTVfsObjToIoStream(hPrevVfsObj);
960 if (hVfsIosIn != NIL_RTVFSIOSTREAM)
961 {
962 RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
963 rc = RTVfsCreateReadAheadForIoStream(hVfsIosIn, 0 /*fFlags*/, RT_LO_U32(pElement->uProvider),
964 RT_HI_U32(pElement->uProvider), &hVfsIos);
965 RTVfsIoStrmRelease(hVfsIosIn);
966 if (RT_SUCCESS(rc))
967 {
968 *phVfsObj = RTVfsObjFromIoStream(hVfsIos);
969 RTVfsIoStrmRelease(hVfsIos);
970 if (*phVfsObj != NIL_RTVFSOBJ)
971 return VINF_SUCCESS;
972 rc = VERR_VFS_CHAIN_CAST_FAILED;
973 }
974 }
975 else
976 rc = VERR_VFS_CHAIN_CAST_FAILED;
977 }
978 else
979 rc = VERR_VFS_CHAIN_CAST_FAILED;
980 return rc;
981}
982
983
984/**
985 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
986 */
987static DECLCALLBACK(bool) rtVfsChainReadAhead_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
988 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
989 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
990{
991 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
992 return false;
993}
994
995
996/** VFS chain element 'pull'. */
997static RTVFSCHAINELEMENTREG g_rtVfsChainReadAheadReg =
998{
999 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
1000 /* fReserved = */ 0,
1001 /* pszName = */ "pull",
1002 /* ListEntry = */ { NULL, NULL },
1003 /* pszHelp = */ "Takes an I/O stream or file and provides read-ahead caching.\n"
1004 "Optional first argument specifies how many buffers to use, 0 indicating the default.\n"
1005 "Optional second argument specifies the buffer size, 0 indicating the default.",
1006 /* pfnValidate = */ rtVfsChainReadAhead_Validate,
1007 /* pfnInstantiate = */ rtVfsChainReadAhead_Instantiate,
1008 /* pfnCanReuseElement = */ rtVfsChainReadAhead_CanReuseElement,
1009 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
1010};
1011
1012RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainReadAheadReg, rtVfsChainReadAheadReg);
1013
Note: See TracBrowser for help on using the repository browser.

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