#include "global.h" #include "RageFileManager_ReadAhead.h" #include "RageThreads.h" #include "RageLog.h" #include #include #if defined(WIN32) #include #endif #if defined(HAVE_POSIX_FADVISE) void RageFileManagerReadAhead::Init() { } void RageFileManagerReadAhead::Shutdown() { } void RageFileManagerReadAhead::ReadAhead( RageFileBasic *pFile, int iBytes ) { int iFD = pFile->GetFD(); if( iFD == -1 ) return; int iStart = lseek( iFD, 0, SEEK_CUR ); posix_fadvise( iFD, iStart, iBytes, POSIX_FADV_WILLNEED ); } void RageFileManagerReadAhead::DiscardCache( RageFileBasic *pFile, int iRelativePosition, int iBytes ) { int iFD = pFile->GetFD(); if( iFD == -1 ) return; int iStart = lseek( iFD, 0, SEEK_CUR ); iStart += iRelativePosition; if( iStart < 0 ) { iBytes -= iStart; iStart = 0; } if( iBytes == 0 ) return; posix_fadvise( iFD, iStart, iBytes, POSIX_FADV_DONTNEED ); } #else #if 0 /* This doesn't currently work, because dup() locks the file position of the new FD with the old * (which makes no sense at all), so it's not easy to read from a copy of the FD without * interfering with the original. */ class RageFileReadAheadThread { public: RageFileReadAheadThread( int iFD, int iFrom, int iBytes ) { m_iFD = iFD; m_iFrom = iFrom; m_iBytes = iBytes; m_bFinished = false; m_WorkerThread.SetName( "Read-ahead thread" ); m_WorkerThread.Create( StartWorkerMain, this ); } ~RageFileReadAheadThread() { close( m_iFD ); m_WorkerThread.Wait(); } bool IsFinished() const { return m_bFinished; } private: static int StartWorkerMain( void *pThis ) { ((RageFileReadAheadThread *) (pThis))->WorkerMain(); return 0; } void WorkerMain() { RString sBuffer; int iFDCopy = dup( m_iFD ); if( iFDCopy != -1 ) { char *pBuf = sBuffer.GetBuffer( m_iBytes ); lseek( iFDCopy, m_iFrom, SEEK_SET ); read( iFDCopy, pBuf, m_iBytes ); sBuffer.ReleaseBuffer( m_iBytes ); close( iFDCopy ); LOG->Trace("read"); } m_bFinished = true; } RageThread m_WorkerThread; bool m_bFinished; int m_iFD; int m_iFrom; int m_iBytes; }; static vector g_apReadAheads; void RageFileManagerReadAhead::Init() { } void RageFileManagerReadAhead::Shutdown() { for( size_t i = 0; i < g_apReadAheads.size(); ++i ) delete g_apReadAheads[i]; g_apReadAheads.clear(); } /* This emulates posix_fadvise(POSIX_FADV_WILLNEED), to force data into cache and hint the * kernel to start read-ahead from that point. */ void RageFileManagerReadAhead::ReadAhead( RageFileBasic *pFile, int iBytes ) { int iFD = pFile->GetFD(); if( iFD == -1 ) return; iFD = dup( iFD ); if( iFD == -1 ) { LOG->Trace( "error: dup(): %s", strerror(errno) ); return; } int iStart = lseek( iFD, 0, SEEK_CUR ); RageFileReadAheadThread *pReadAhead = new RageFileReadAheadThread( iFD, iStart, iBytes ); g_apReadAheads.push_back( pReadAhead ); for( size_t i = 0; i < g_apReadAheads.size(); ++i ) { if( g_apReadAheads[i]->IsFinished() ) { LOG->Trace("cleaned"); delete g_apReadAheads[i]; g_apReadAheads.erase( g_apReadAheads.begin()+i, g_apReadAheads.begin()+i+1 ); --i; } } } void RageFileManagerReadAhead::DiscardCache( RageFileBasic *pFile, int iRelativePosition, int iBytes ) { } #else void RageFileManagerReadAhead::Init() { } void RageFileManagerReadAhead::Shutdown() { } void RageFileManagerReadAhead::ReadAhead( RageFileBasic *pFile, int iBytes ) { } void RageFileManagerReadAhead::DiscardCache( RageFileBasic *pFile, int iRelativePosition, int iBytes ) { } #endif #endif #if defined(HAVE_POSIX_FADVISE) void RageFileManagerReadAhead::CacheHintStreaming( RageFileBasic *pFile ) { /* This guesses at the actual size of the file on disk, which may be smaller if this file is compressed. * Since this is usually used on music and video files, it generally shouldn't be. */ int iFD = pFile->GetFD(); if( iFD == -1 ) return; int iPos = pFile->Tell(); int iFrom = lseek( iFD, 0, SEEK_CUR ); int iBytes = pFile->GetFileSize() - iPos; posix_fadvise( iFD, iFrom, iBytes, POSIX_FADV_SEQUENTIAL ); } #else void RageFileManagerReadAhead::CacheHintStreaming( RageFileBasic *pFile ) { } #endif /* * Copyright (c) 2010 Glenn Maynard * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons to * whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */