@@ -209,6 +209,9 @@ private:
off_t end;
off_t ahead;
ssize_t written;
+ ssize_t totwritten;
+ size_t readahead;
+ size_t pendingreadahead;
public:
cUnbufferedFile(void);
~cUnbufferedFile();
@@ -851,8 +851,7 @@ bool cSafeFile::Close(void)
// --- cUnbufferedFile -------------------------------------------------------
-#define READ_AHEAD MEGABYTE(2)
-#define WRITE_BUFFER MEGABYTE(10)
+#define WRITE_BUFFER KILOBYTE(800)
cUnbufferedFile::cUnbufferedFile(void)
{
@@ -869,7 +868,13 @@ int cUnbufferedFile::Open(const char *Fi
Close();
fd = open(FileName, Flags, Mode);
begin = end = ahead = -1;
+ readahead = 16*1024;
+ pendingreadahead = 0;
written = 0;
+ totwritten = 0;
+ if (fd >= 0) {
+ posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
+ }
return fd;
}
@@ -880,10 +885,10 @@ int cUnbufferedFile::Close(void)
end = ahead;
if (begin >= 0 && end > begin) {
//dsyslog("close buffer: %d (flush: %d bytes, %ld-%ld)", fd, written, begin, end);
- if (written)
+ if (0 && written)
fdatasync(fd);
- posix_fadvise(fd, begin, end - begin, POSIX_FADV_DONTNEED);
}
+ posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
begin = end = ahead = -1;
written = 0;
}
@@ -899,35 +904,92 @@ off_t cUnbufferedFile::Seek(off_t Offset
return -1;
}
+// when replaying and going eg FF->PLAY the position jumps back 2..8M
+// hence we might not want to drop that data at once.
+// Ignoring for now to avoid making this even more complex, but we could
+// at least try to handle the common cases
+// (PLAY->FF->PLAY, small jumps, moving editing marks etc)
+
ssize_t cUnbufferedFile::Read(void *Data, size_t Size)
{
if (fd >= 0) {
off_t pos = lseek(fd, 0, SEEK_CUR);
- // jump forward - adjust end position
- if (pos > end)
- end = pos;
- // after adjusting end - don't clear more than previously requested
- if (end > ahead)
- end = ahead;
- // jump backward - drop read ahead of previous run
- if (pos < begin)
- end = ahead;
+ off_t jumped = end - pos; // nonzero means we're not at the last offset - some kind of jump happened.
+ if (jumped) {
+ pendingreadahead += ahead-end+KILOBYTE(64);
+ // jumped forward? - treat as if we did read all the way to current pos.
+ if (pos > end) {
+ end = pos;
+ // but clamp at ahead so we don't clear more than previously requested.
+ // (would be mostly harmless anyway, unless we got more than one reader of this file)
+ // add a little extra readahead, JIC the kernel prefethed more than we requested.
+ if (end > (ahead+KILOBYTE(512)))
+ end = ahead+KILOBYTE(512);
+ }
+ // jumped backward? - drop both last read _and_ read-ahead
+ if (pos < begin)
+ end = ahead+KILOBYTE(512);
+ // jumped backward, but still inside prev read window? - pretend we read less.
+ if ((pos >= begin) && (pos < end) )
+ end = pos;
+ }
+
+ ssize_t bytesRead = safe_read(fd, Data, Size);
+
+ // now drop all data accesed during _previous_ Read().
if (begin >= 0 && end > begin)
- posix_fadvise(fd, begin - KILOBYTE(200), end - begin + KILOBYTE(200), POSIX_FADV_DONTNEED);//XXX macros/parameters???
+ posix_fadvise(fd, begin, end-begin, POSIX_FADV_DONTNEED);
+
begin = pos;
- ssize_t bytesRead = safe_read(fd, Data, Size);
if (bytesRead > 0) {
pos += bytesRead;
- end = pos;
// this seems to trigger a non blocking read - this
// may or may not have been finished when we will be called next time.
// If it is not finished we can't release the not yet filled buffers.
// So this is commented out till we find a better solution.
- //posix_fadvise(fd, pos, READ_AHEAD, POSIX_FADV_WILLNEED);
- ahead = pos + READ_AHEAD;
+
+ // Hmm, it's obviously harmless if we're actually going to read the data
+ // -- the whole point of read-ahead is to start the IO early...
+ // The comment above applies only when we jump somewhere else _before_ the
+ // IO started here finishes. How common would that be? Could be handled eg
+ // by posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED) called some time after
+ // we detect a jump. Ignoring this for now. /AS
+
+ // Ugh, it seems to cause some "leaks" at every jump... Either the
+ // brute force approach mentioned above should work (it's not like this is
+ // much different than O_DIRECT) or keeping notes about the ahead reads and
+ // flushing them after some time. the latter seems overkill though, trying
+ // the former...
+
+ if ( !jumped ) {
+ if ( readahead <= Size ) // automagically tune readahead size.
+ readahead = Size*2;
+ posix_fadvise(fd, pos, readahead, POSIX_FADV_WILLNEED);
+ ahead = pos + readahead;
+ }
+ else {
+ // flush it all; mostly to get rid of nonflushed readahead
+ // coming from _previous_ jumps. possibly ratelimit this in
+ // some way (bytes prefetched? nr of jumps? time?)
+
+ //// first-jump-after-every-60s seemed as good as any :^)
+ //time_t t = time(NULL);
+ //if (t >= nextflush) {
+ // posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
+ // nextflush = t+60;
+ // }
+
+ // the accounting is _very_ unaccurate, i've seen ~50M get flushed
+ // when the limit was set to 4M. As long as this triggers after
+ // _some_ jumps we should be ok though.
+ if (pendingreadahead > MEGABYTE(1)) {
+ posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
+ pendingreadahead = 0;
+ }
+ ahead = pos;
+ }
}
- else
- end = pos;
+ end = pos;
return bytesRead;
}
return -1;
@@ -950,12 +1012,18 @@ ssize_t cUnbufferedFile::Write(const voi
end = pos + bytesWritten;
if (written > WRITE_BUFFER) {
//dsyslog("flush buffer: %d (%d bytes, %ld-%ld)", fd, written, begin, end);
- fdatasync(fd);
- if (begin >= 0 && end > begin)
- posix_fadvise(fd, begin, end - begin, POSIX_FADV_DONTNEED);
- begin = end = -1;
+ if (begin >= 0 && end > begin) {
+ off_t headdrop = max((long)begin,(long)WRITE_BUFFER*2);
+ posix_fadvise(fd, (begin&~4095)-headdrop, end - begin + headdrop, POSIX_FADV_DONTNEED);
+ }
+ begin = end = -1;
+ totwritten += written;
written = 0;
}
+ if (totwritten > MEGABYTE(10)) {
+ posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
+ totwritten = 0;
+ }
}
return bytesWritten;
}