VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 7 weeks ago

Copyright year updates by scm.

  • 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 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Read-Ahead Thread.
4 */
5
6/*
7 * Copyright (C) 2010-2024 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, PRTSGBUF 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 RTSgBufAdvance(pSgBuf, cbTotalRead);
351
352 return rc;
353}
354
355
356/**
357 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
358 */
359static DECLCALLBACK(int) rtVfsReadAhead_Write(void *pvThis, RTFOFF off, PRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
360{
361 RT_NOREF_PV(pvThis); RT_NOREF_PV(off); RT_NOREF_PV(pSgBuf); RT_NOREF_PV(fBlocking); RT_NOREF_PV(pcbWritten);
362 AssertFailed();
363 return VERR_ACCESS_DENIED;
364}
365
366
367/**
368 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
369 */
370static DECLCALLBACK(int) rtVfsReadAhead_Flush(void *pvThis)
371{
372 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
373 return RTVfsIoStrmFlush(pThis->hIos);
374}
375
376
377/**
378 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
379 */
380static DECLCALLBACK(int) rtVfsReadAhead_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
381 uint32_t *pfRetEvents)
382{
383 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
384 if (pThis->hThread != NIL_RTTHREAD)
385 {
386 /** @todo poll one with read-ahead thread. */
387 return VERR_NOT_IMPLEMENTED;
388 }
389 return RTVfsIoStrmPoll(pThis->hIos, fEvents, cMillies, fIntr, pfRetEvents);
390}
391
392
393/**
394 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
395 */
396static DECLCALLBACK(int) rtVfsReadAhead_Tell(void *pvThis, PRTFOFF poffActual)
397{
398 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
399
400 RTCritSectEnter(&pThis->BufferCritSect);
401 *poffActual = pThis->offConsumer;
402 RTCritSectLeave(&pThis->BufferCritSect);
403
404 return VINF_SUCCESS;
405}
406
407
408/**
409 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
410 */
411static DECLCALLBACK(int) rtVfsReadAhead_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
412{
413 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
414 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
415
416 RTCritSectEnter(&pThis->IoCritSect);
417 RT_NOREF_PV(fMode); RT_NOREF_PV(fMask); /// @todo int rc = RTVfsFileSetMode(pThis->hFile, fMode, fMask);
418 int rc = VERR_NOT_SUPPORTED;
419 RTCritSectLeave(&pThis->IoCritSect);
420
421 return rc;
422}
423
424
425/**
426 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
427 */
428static DECLCALLBACK(int) rtVfsReadAhead_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
429 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
430{
431 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
432 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
433
434 RTCritSectEnter(&pThis->IoCritSect);
435 RT_NOREF_PV(pAccessTime); RT_NOREF_PV(pModificationTime); RT_NOREF_PV(pChangeTime); RT_NOREF_PV(pBirthTime);
436 /// @todo int rc = RTVfsFileSetTimes(pThis->hFile, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
437 int rc = VERR_NOT_SUPPORTED;
438 RTCritSectLeave(&pThis->IoCritSect);
439
440 return rc;
441}
442
443
444/**
445 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
446 */
447static DECLCALLBACK(int) rtVfsReadAhead_SetOwner(void *pvThis, RTUID uid, RTGID gid)
448{
449 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
450 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
451
452 RTCritSectEnter(&pThis->IoCritSect);
453 RT_NOREF_PV(uid); RT_NOREF_PV(gid);
454 /// @todo int rc = RTVfsFileSetOwner(pThis->hFile, uid, gid);
455 int rc = VERR_NOT_SUPPORTED;
456 RTCritSectLeave(&pThis->IoCritSect);
457
458 return rc;
459}
460
461
462/**
463 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
464 */
465static DECLCALLBACK(int) rtVfsReadAhead_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
466{
467 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
468 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
469
470 RTCritSectEnter(&pThis->IoCritSect); /* protects against concurrent I/O using the offset. */
471 RTCritSectEnter(&pThis->BufferCritSect); /* protects offConsumer */
472
473 uint64_t offActual = UINT64_MAX;
474 int rc = RTVfsFileSeek(pThis->hFile, offSeek, uMethod, &offActual);
475 if (RT_SUCCESS(rc))
476 {
477 pThis->offConsumer = offActual;
478 if (poffActual)
479 *poffActual = offActual;
480 }
481
482 RTCritSectLeave(&pThis->BufferCritSect);
483 RTCritSectLeave(&pThis->IoCritSect);
484
485 return rc;
486}
487
488
489/**
490 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
491 */
492static DECLCALLBACK(int) rtVfsReadAhead_QuerySize(void *pvThis, uint64_t *pcbFile)
493{
494 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
495 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
496
497 RTCritSectEnter(&pThis->IoCritSect); /* paranoia */
498 int rc = RTVfsFileQuerySize(pThis->hFile, pcbFile);
499 RTCritSectLeave(&pThis->IoCritSect);
500
501 return rc;
502}
503
504
505/**
506 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
507 */
508static DECLCALLBACK(int) rtVfsReadAhead_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
509{
510 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
511 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
512
513 RTCritSectEnter(&pThis->IoCritSect); /* paranoia */
514 int rc = RTVfsFileSetSize(pThis->hFile, cbFile, fFlags);
515 RTCritSectLeave(&pThis->IoCritSect);
516
517 return rc;
518}
519
520
521/**
522 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
523 */
524static DECLCALLBACK(int) rtVfsReadAhead_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
525{
526 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvThis;
527 AssertReturn(pThis->hFile != NIL_RTVFSFILE, VERR_NOT_SUPPORTED);
528
529 RTCritSectEnter(&pThis->IoCritSect); /* paranoia */
530 int rc = RTVfsFileQueryMaxSize(pThis->hFile, pcbMax);
531 RTCritSectLeave(&pThis->IoCritSect);
532
533 return rc;
534}
535
536
537/**
538 * Read ahead I/O stream operations.
539 */
540DECL_HIDDEN_CONST(const RTVFSIOSTREAMOPS) g_VfsReadAheadIosOps =
541{ /* Stream */
542 { /* Obj */
543 RTVFSOBJOPS_VERSION,
544 RTVFSOBJTYPE_IO_STREAM,
545 "Read ahead I/O stream",
546 rtVfsReadAhead_Close,
547 rtVfsReadAhead_QueryInfo,
548 NULL,
549 RTVFSOBJOPS_VERSION
550 },
551 RTVFSIOSTREAMOPS_VERSION,
552 RTVFSIOSTREAMOPS_FEAT_NO_SG,
553 rtVfsReadAhead_Read,
554 rtVfsReadAhead_Write,
555 rtVfsReadAhead_Flush,
556 rtVfsReadAhead_PollOne,
557 rtVfsReadAhead_Tell,
558 NULL /*Skip*/,
559 NULL /*ZeroFill*/,
560 RTVFSIOSTREAMOPS_VERSION,
561};
562
563
564/**
565 * Read ahead file operations.
566 */
567DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_VfsReadAheadFileOps =
568{
569 { /* Stream */
570 { /* Obj */
571 RTVFSOBJOPS_VERSION,
572 RTVFSOBJTYPE_FILE,
573 "Read ahead file",
574 rtVfsReadAhead_Close,
575 rtVfsReadAhead_QueryInfo,
576 NULL,
577 RTVFSOBJOPS_VERSION
578 },
579 RTVFSIOSTREAMOPS_VERSION,
580 RTVFSIOSTREAMOPS_FEAT_NO_SG,
581 rtVfsReadAhead_Read,
582 rtVfsReadAhead_Write,
583 rtVfsReadAhead_Flush,
584 rtVfsReadAhead_PollOne,
585 rtVfsReadAhead_Tell,
586 NULL /*Skip*/,
587 NULL /*ZeroFill*/,
588 RTVFSIOSTREAMOPS_VERSION,
589 },
590 RTVFSFILEOPS_VERSION,
591 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
592 { /* ObjSet */
593 RTVFSOBJSETOPS_VERSION,
594 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
595 rtVfsReadAhead_SetMode,
596 rtVfsReadAhead_SetTimes,
597 rtVfsReadAhead_SetOwner,
598 RTVFSOBJSETOPS_VERSION
599 },
600 rtVfsReadAhead_Seek,
601 rtVfsReadAhead_QuerySize,
602 rtVfsReadAhead_SetSize,
603 rtVfsReadAhead_QueryMaxSize,
604 RTVFSFILEOPS_VERSION
605};
606
607
608/**
609 * @callback_method_impl{PFNRTTHREAD, Read ahead thread procedure}
610 */
611static DECLCALLBACK(int) rtVfsReadAheadThreadProc(RTTHREAD hThreadSelf, void *pvUser)
612{
613 PRTVFSREADAHEAD pThis = (PRTVFSREADAHEAD)pvUser;
614 Assert(pThis);
615
616 while (!pThis->fTerminateThread)
617 {
618 int rc;
619
620 /*
621 * Is there a buffer handy for reading ahead.
622 */
623 PRTVFSREADAHEADBUFDESC pBufDesc = NULL;
624 RTCritSectEnter(&pThis->BufferCritSect);
625 if (!pThis->fTerminateThread)
626 pBufDesc = RTListRemoveFirst(&pThis->FreeList, RTVFSREADAHEADBUFDESC, ListEntry);
627 RTCritSectLeave(&pThis->BufferCritSect);
628
629 if (pBufDesc)
630 {
631 /*
632 * Got a buffer, take the I/O lock and read into it.
633 */
634 rc = VERR_CALLBACK_RETURN;
635 RTCritSectEnter(&pThis->IoCritSect);
636 if (!pThis->fTerminateThread)
637 {
638
639 pBufDesc->off = RTVfsIoStrmTell(pThis->hIos);
640 size_t cbRead = 0;
641 rc = RTVfsIoStrmRead(pThis->hIos, pBufDesc->pbBuffer, pThis->cbBuffer, true /*fBlocking*/, &cbRead);
642 if (RT_SUCCESS(rc))
643 {
644 if (rc == VINF_EOF)
645 {
646 pThis->offEof = pBufDesc->off + cbRead;
647 Log(("rtVfsReadAheadThreadProc: EOF %llu (%#llx)\n", pThis->offEof, pThis->offEof));
648 }
649 pBufDesc->cbFilled = (uint32_t)cbRead;
650
651 /*
652 * Put back the buffer. The consumer list is sorted by offset, but
653 * we should usually end up appending the buffer.
654 */
655 RTCritSectEnter(&pThis->BufferCritSect);
656 PRTVFSREADAHEADBUFDESC pAfter = RTListGetLast(&pThis->ConsumerList, RTVFSREADAHEADBUFDESC, ListEntry);
657 if (!pAfter || pAfter->off <= pBufDesc->off)
658 RTListAppend(&pThis->ConsumerList, &pBufDesc->ListEntry);
659 else
660 {
661 do
662 pAfter = RTListGetPrev(&pThis->ConsumerList, pAfter, RTVFSREADAHEADBUFDESC, ListEntry);
663 while (pAfter && pAfter->off > pBufDesc->off);
664 if (!pAfter)
665 RTListPrepend(&pThis->ConsumerList, &pBufDesc->ListEntry);
666 else
667 {
668 Assert(pAfter->off <= pBufDesc->off);
669 RTListNodeInsertAfter(&pAfter->ListEntry, &pBufDesc->ListEntry);
670 }
671 }
672 RTCritSectLeave(&pThis->BufferCritSect);
673 pBufDesc = NULL;
674
675#ifdef RT_STRICT
676 /* Verify the list ordering. */
677 unsigned cAsserted = 0;
678 uint64_t offAssert = 0;
679 PRTVFSREADAHEADBUFDESC pAssertCur;
680 RTListForEach(&pThis->ConsumerList, pAssertCur, RTVFSREADAHEADBUFDESC, ListEntry)
681 {
682 Assert(offAssert <= pAssertCur->off);
683 offAssert = pAssertCur->off;
684 Assert(cAsserted < pThis->cBuffers);
685 cAsserted++;
686 }
687#endif
688 }
689 else
690 Assert(rc != VERR_EOF);
691 }
692 RTCritSectLeave(&pThis->IoCritSect);
693
694 /*
695 * If we succeeded and we didn't yet reach the end of the stream,
696 * loop without delay to start processing the next buffer.
697 */
698 if (RT_LIKELY(!pBufDesc && rc != VINF_EOF))
699 continue;
700
701 /* Put any unused buffer back in the free list (termination/failure, not EOF). */
702 if (pBufDesc)
703 {
704 RTCritSectEnter(&pThis->BufferCritSect);
705 RTListPrepend(&pThis->FreeList, &pBufDesc->ListEntry);
706 RTCritSectLeave(&pThis->BufferCritSect);
707 }
708 if (pThis->fTerminateThread)
709 break;
710 }
711
712 /*
713 * Wait for more to do.
714 */
715 rc = RTThreadUserWait(hThreadSelf, RT_MS_1MIN);
716 if (RT_SUCCESS(rc))
717 RTThreadUserReset(hThreadSelf);
718 }
719
720 return VINF_SUCCESS;
721}
722
723
724static int rtVfsCreateReadAheadInstance(RTVFSIOSTREAM hVfsIosSrc, RTVFSFILE hVfsFileSrc, uint32_t fFlags,
725 uint32_t cBuffers, uint32_t cbBuffer, PRTVFSIOSTREAM phVfsIos, PRTVFSFILE phVfsFile)
726{
727 /*
728 * Validate input a little.
729 */
730 int rc = VINF_SUCCESS;
731 AssertStmt(cBuffers < _4K, rc = VERR_OUT_OF_RANGE);
732 if (cBuffers == 0)
733 cBuffers = 4;
734 AssertStmt(cbBuffer <= _4M, rc = VERR_OUT_OF_RANGE);
735 if (cbBuffer == 0)
736 cbBuffer = _256K / cBuffers;
737 AssertStmt(cbBuffer * cBuffers < (ARCH_BITS < 64 ? _64M : _256M), rc = VERR_OUT_OF_RANGE);
738 AssertStmt(!fFlags, rc = VERR_INVALID_FLAGS);
739
740 if (RT_SUCCESS(rc))
741 {
742 /*
743 * Create a file or I/O stream instance.
744 */
745 RTVFSFILE hVfsFileReadAhead = NIL_RTVFSFILE;
746 RTVFSIOSTREAM hVfsIosReadAhead = NIL_RTVFSIOSTREAM;
747 PRTVFSREADAHEAD pThis;
748 size_t cbThis = RT_UOFFSETOF_DYN(RTVFSREADAHEAD, aBufDescs[cBuffers]);
749 if (hVfsFileSrc != NIL_RTVFSFILE)
750 rc = RTVfsNewFile(&g_VfsReadAheadFileOps, cbThis, RTFILE_O_READ, NIL_RTVFS, NIL_RTVFSLOCK,
751 &hVfsFileReadAhead, (void **)&pThis);
752 else
753 rc = RTVfsNewIoStream(&g_VfsReadAheadIosOps, cbThis, RTFILE_O_READ, NIL_RTVFS, NIL_RTVFSLOCK,
754 &hVfsIosReadAhead, (void **)&pThis);
755 if (RT_SUCCESS(rc))
756 {
757 RTListInit(&pThis->ConsumerList);
758 RTListInit(&pThis->FreeList);
759 pThis->hThread = NIL_RTTHREAD;
760 pThis->fTerminateThread = false;
761 pThis->fFlags = fFlags;
762 pThis->hFile = hVfsFileSrc;
763 pThis->hIos = hVfsIosSrc;
764 pThis->cBuffers = cBuffers;
765 pThis->cbBuffer = cbBuffer;
766 pThis->offEof = UINT64_MAX;
767 pThis->offConsumer = RTVfsIoStrmTell(hVfsIosSrc);
768 if ((RTFOFF)pThis->offConsumer >= 0)
769 {
770 rc = RTCritSectInit(&pThis->IoCritSect);
771 if (RT_SUCCESS(rc))
772 rc = RTCritSectInit(&pThis->BufferCritSect);
773 if (RT_SUCCESS(rc))
774 {
775 pThis->pbAllBuffers = (uint8_t *)RTMemPageAlloc(pThis->cbBuffer * pThis->cBuffers);
776 if (pThis->pbAllBuffers)
777 {
778 for (uint32_t i = 0; i < cBuffers; i++)
779 {
780 pThis->aBufDescs[i].cbFilled = 0;
781 pThis->aBufDescs[i].off = UINT64_MAX / 2;
782 pThis->aBufDescs[i].pbBuffer = &pThis->pbAllBuffers[cbBuffer * i];
783 RTListAppend(&pThis->FreeList, &pThis->aBufDescs[i].ListEntry);
784 }
785
786 /*
787 * Create thread.
788 */
789 rc = RTThreadCreate(&pThis->hThread, rtVfsReadAheadThreadProc, pThis, 0, RTTHREADTYPE_DEFAULT,
790 RTTHREADFLAGS_WAITABLE, "vfsreadahead");
791 if (RT_SUCCESS(rc))
792 {
793 /*
794 * We're good.
795 */
796 if (phVfsFile)
797 *phVfsFile = hVfsFileReadAhead;
798 else if (hVfsFileReadAhead == NIL_RTVFSFILE)
799 *phVfsIos = hVfsIosReadAhead;
800 else
801 {
802 *phVfsIos = RTVfsFileToIoStream(hVfsFileReadAhead);
803 RTVfsFileRelease(hVfsFileReadAhead);
804 AssertReturn(*phVfsIos != NIL_RTVFSIOSTREAM, VERR_INTERNAL_ERROR_5);
805 }
806 return VINF_SUCCESS;
807 }
808 }
809 }
810 }
811 else
812 rc = (int)pThis->offConsumer;
813 }
814 }
815
816 RTVfsFileRelease(hVfsFileSrc);
817 RTVfsIoStrmRelease(hVfsIosSrc);
818 return rc;
819}
820
821
822RTDECL(int) RTVfsCreateReadAheadForIoStream(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, uint32_t cBuffers, uint32_t cbBuffer,
823 PRTVFSIOSTREAM phVfsIos)
824{
825 AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
826 *phVfsIos = NIL_RTVFSIOSTREAM;
827
828 /*
829 * Retain the input stream, trying to obtain a file handle too so we can
830 * fully mirror it.
831 */
832 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIos);
833 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
834 RTVFSFILE hVfsFile = RTVfsIoStrmToFile(hVfsIos);
835
836 /*
837 * Do the job. (This always consumes the above retained references.)
838 */
839 return rtVfsCreateReadAheadInstance(hVfsIos, hVfsFile, fFlags, cBuffers, cbBuffer, phVfsIos, NULL);
840}
841
842
843RTDECL(int) RTVfsCreateReadAheadForFile(RTVFSFILE hVfsFile, uint32_t fFlags, uint32_t cBuffers, uint32_t cbBuffer,
844 PRTVFSFILE phVfsFile)
845{
846 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
847 *phVfsFile = NIL_RTVFSFILE;
848
849 /*
850 * Retain the input file and cast it o an I/O stream.
851 */
852 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
853 AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_INVALID_HANDLE);
854 uint32_t cRefs = RTVfsFileRetain(hVfsFile);
855 AssertReturnStmt(cRefs != UINT32_MAX, RTVfsIoStrmRelease(hVfsIos), VERR_INVALID_HANDLE);
856
857 /*
858 * Do the job. (This always consumes the above retained references.)
859 */
860 return rtVfsCreateReadAheadInstance(hVfsIos, hVfsFile, fFlags, cBuffers, cbBuffer, NULL, phVfsFile);
861}
862
863
864/**
865 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
866 */
867static DECLCALLBACK(int) rtVfsChainReadAhead_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
868 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
869{
870 RT_NOREF(pProviderReg, poffError, pErrInfo);
871
872 /*
873 * Basics.
874 */
875 if ( pElement->enmType != RTVFSOBJTYPE_FILE
876 && pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
877 return VERR_VFS_CHAIN_ONLY_FILE_OR_IOS;
878 if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
879 return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
880 if ( pElement->enmTypeIn != RTVFSOBJTYPE_FILE
881 && pElement->enmTypeIn != RTVFSOBJTYPE_IO_STREAM)
882 return VERR_VFS_CHAIN_TAKES_FILE_OR_IOS;
883 if (pSpec->fOpenFile & RTFILE_O_WRITE)
884 return VERR_VFS_CHAIN_READ_ONLY_IOS;
885 if (pElement->cArgs > 2)
886 return VERR_VFS_CHAIN_AT_MOST_TWO_ARGS;
887
888 /*
889 * Parse the two optional arguments.
890 */
891 uint32_t cBuffers = 0;
892 if (pElement->cArgs > 0)
893 {
894 const char *psz = pElement->paArgs[0].psz;
895 if (*psz)
896 {
897 int rc = RTStrToUInt32Full(psz, 0, &cBuffers);
898 if (RT_FAILURE(rc))
899 {
900 *poffError = pElement->paArgs[0].offSpec;
901 return VERR_VFS_CHAIN_INVALID_ARGUMENT;
902 }
903 }
904 }
905
906 uint32_t cbBuffer = 0;
907 if (pElement->cArgs > 1)
908 {
909 const char *psz = pElement->paArgs[1].psz;
910 if (*psz)
911 {
912 int rc = RTStrToUInt32Full(psz, 0, &cbBuffer);
913 if (RT_FAILURE(rc))
914 {
915 *poffError = pElement->paArgs[1].offSpec;
916 return VERR_VFS_CHAIN_INVALID_ARGUMENT;
917 }
918 }
919 }
920
921 /*
922 * Save the parsed arguments in the spec since their both optional.
923 */
924 pElement->uProvider = RT_MAKE_U64(cBuffers, cbBuffer);
925
926 return VINF_SUCCESS;
927}
928
929
930/**
931 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
932 */
933static DECLCALLBACK(int) rtVfsChainReadAhead_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
934 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
935 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
936{
937 RT_NOREF(pProviderReg, pSpec, pElement, poffError, pErrInfo);
938 AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
939
940 /* Try for a file if we can. */
941 int rc;
942 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
943 if (hVfsFileIn != NIL_RTVFSFILE)
944 {
945 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
946 rc = RTVfsCreateReadAheadForFile(hVfsFileIn, 0 /*fFlags*/, RT_LO_U32(pElement->uProvider),
947 RT_HI_U32(pElement->uProvider), &hVfsFile);
948 RTVfsFileRelease(hVfsFileIn);
949 if (RT_SUCCESS(rc))
950 {
951 *phVfsObj = RTVfsObjFromFile(hVfsFile);
952 RTVfsFileRelease(hVfsFile);
953 if (*phVfsObj != NIL_RTVFSOBJ)
954 return VINF_SUCCESS;
955 rc = VERR_VFS_CHAIN_CAST_FAILED;
956 }
957 }
958 else if (pElement->enmType == RTVFSOBJTYPE_IO_STREAM)
959 {
960 RTVFSIOSTREAM hVfsIosIn = RTVfsObjToIoStream(hPrevVfsObj);
961 if (hVfsIosIn != NIL_RTVFSIOSTREAM)
962 {
963 RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
964 rc = RTVfsCreateReadAheadForIoStream(hVfsIosIn, 0 /*fFlags*/, RT_LO_U32(pElement->uProvider),
965 RT_HI_U32(pElement->uProvider), &hVfsIos);
966 RTVfsIoStrmRelease(hVfsIosIn);
967 if (RT_SUCCESS(rc))
968 {
969 *phVfsObj = RTVfsObjFromIoStream(hVfsIos);
970 RTVfsIoStrmRelease(hVfsIos);
971 if (*phVfsObj != NIL_RTVFSOBJ)
972 return VINF_SUCCESS;
973 rc = VERR_VFS_CHAIN_CAST_FAILED;
974 }
975 }
976 else
977 rc = VERR_VFS_CHAIN_CAST_FAILED;
978 }
979 else
980 rc = VERR_VFS_CHAIN_CAST_FAILED;
981 return rc;
982}
983
984
985/**
986 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
987 */
988static DECLCALLBACK(bool) rtVfsChainReadAhead_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
989 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
990 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
991{
992 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
993 return false;
994}
995
996
997/** VFS chain element 'pull'. */
998static RTVFSCHAINELEMENTREG g_rtVfsChainReadAheadReg =
999{
1000 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
1001 /* fReserved = */ 0,
1002 /* pszName = */ "pull",
1003 /* ListEntry = */ { NULL, NULL },
1004 /* pszHelp = */ "Takes an I/O stream or file and provides read-ahead caching.\n"
1005 "Optional first argument specifies how many buffers to use, 0 indicating the default.\n"
1006 "Optional second argument specifies the buffer size, 0 indicating the default.",
1007 /* pfnValidate = */ rtVfsChainReadAhead_Validate,
1008 /* pfnInstantiate = */ rtVfsChainReadAhead_Instantiate,
1009 /* pfnCanReuseElement = */ rtVfsChainReadAhead_CanReuseElement,
1010 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
1011};
1012
1013RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainReadAheadReg, rtVfsChainReadAheadReg);
1014
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