Extract the cache cleaning logic from FFmpegSourceProvider

Originally committed to SVN as r6653.
This commit is contained in:
Thomas Goyne 2012-04-03 22:44:40 +00:00
parent 2a324a56e5
commit bd0f6a4c2b
5 changed files with 132 additions and 142 deletions

View file

@ -55,6 +55,7 @@
#include "main.h"
#include "md5.h"
#include "standard_paths.h"
#include "utils.h"
#ifdef WIN32
static void deinit_com(bool) {
@ -79,8 +80,6 @@ FFmpegSourceProvider::FFmpegSourceProvider()
FFMS_Init(0, 1);
}
wxMutex FFmpegSourceProvider::CleaningInProgress;
/// @brief Callback function that updates the indexing progress dialog
/// @param Current The current file positition in bytes
/// @param Total The total file size in bytes
@ -263,127 +262,12 @@ wxString FFmpegSourceProvider::GetCacheFilename(const wxString& _filename)
return dirfn.GetShortPath() + "/" + fn.GetFullName();
}
/// @brief Starts the cache cleaner thread
/// @return True on success, false if the thread could not be started.
bool FFmpegSourceProvider::CleanCache() {
LOG_D("provider/ffmpegsource/cache") << "attempting to start thread";
FFmpegSourceCacheCleaner *CleaningThread = new FFmpegSourceCacheCleaner(this);
if (CleaningThread->Create() != wxTHREAD_NO_ERROR) {
LOG_D("provider/ffmpegsource/cache") << "thread creation failed";
delete CleaningThread;
CleaningThread = NULL;
return false;
}
if (CleaningThread->Run() != wxTHREAD_NO_ERROR) {
LOG_D("provider/ffmpegsource/cache") << "failed to start thread";
delete CleaningThread;
CleaningThread = NULL;
return false;
}
LOG_D("provider/ffmpegsource/cache") << "thread started successfully";
return true;
void FFmpegSourceProvider::CleanCache() {
::CleanCache(StandardPaths::DecodePath("?local/ffms2cache/"),
"*.ffindex",
OPT_GET("Provider/FFmpegSource/Cache/Size")->GetInt(),
OPT_GET("Provider/FFmpegSource/Cache/Files")->GetInt());
}
/// @brief constructor
/// @param par the parent provider
FFmpegSourceCacheCleaner::FFmpegSourceCacheCleaner(FFmpegSourceProvider *par) : wxThread(wxTHREAD_DETACHED) {
parent = par;
}
/// @brief Cleans the ffms2 index cache folder
/// @return Returns non-0 on error, 0 otherwise.
wxThread::ExitCode FFmpegSourceCacheCleaner::Entry() {
wxMutexLocker lock(FFmpegSourceProvider::CleaningInProgress);
if (!lock.IsOk()) {
LOG_D("provider/ffmpegsource/cache") << "cleaning already in progress, thread exiting";
return (wxThread::ExitCode)1;
}
wxString cachedirname = StandardPaths::DecodePath("?local/ffms2cache/");
wxDir cachedir;
if (!cachedir.Open(cachedirname)) {
LOG_D("provider/ffmpegsource/cache") << "couldn't open cache directory " << STD_STR(cachedirname);
return (wxThread::ExitCode)1;
}
// sleep for a bit so we (hopefully) don't thrash the disk too much while indexing is in progress
wxThread::This()->Sleep(2000);
// the option is in megabytes, we need bytes
// shift left by 20 is CLEARLY more efficient than multiplying by 1048576
int64_t maxsize = OPT_GET("Provider/FFmpegSource/Cache/Size")->GetInt() << 20;
int64_t cursize = wxDir::GetTotalSize(cachedirname).GetValue();
int maxfiles = OPT_GET("Provider/FFmpegSource/Cache/Files")->GetInt();
if (!cachedir.HasFiles("*.ffindex")) {
LOG_D("provider/ffmpegsource/cache") << "no index files in cache folder, exiting";
return (wxThread::ExitCode)0;
}
int deleted = 0;
int numfiles = 0;
std::multimap<int64_t,wxFileName> cachefiles;
wxString curfn_str;
wxFileName curfn;
wxDateTime curatime;
// unusually paranoid sanity check
// (someone might have deleted the file(s) after we did HasFiles() above; does wxDir.Open() lock the dir?)
if (!cachedir.GetFirst(&curfn_str, "*.ffindex", wxDIR_FILES)) {
LOG_D("provider/ffmpegsource/cache") << "undefined error";
return (wxThread::ExitCode)1;
}
numfiles++;
curfn = wxFileName(cachedirname, curfn_str);
curfn.GetTimes(&curatime, NULL, NULL);
// FIXME: will break when the time_t's wrap around!!1!
cachefiles.insert(std::pair<int64_t,wxFileName>(curatime.GetTicks(),curfn));
while (cachedir.GetNext(&curfn_str)) {
curfn = wxFileName(cachedirname, curfn_str);
curfn.GetTimes(&curatime, NULL, NULL);
cachefiles.insert(std::pair<int64_t,wxFileName>(curatime.GetTicks(),curfn));
numfiles++;
wxThread::This()->Sleep(250);
}
if (numfiles <= maxfiles && cursize <= maxsize) {
LOG_D("provider/ffmpegsource/cache") << "cache does not need cleaning (maxsize=" << (int)maxsize << ", cursize=" << (int)cursize << "; maxfiles=" << maxfiles << "numfiles=" << numfiles << ",exiting";
return (wxThread::ExitCode)0;
}
for (std::multimap<int64_t,wxFileName>::iterator i = cachefiles.begin(); i != cachefiles.end(); i++) {
// stop cleaning?
if ((cursize <= maxsize && numfiles <= maxfiles) || numfiles <= 1)
break;
int64_t fsize = i->second.GetSize().GetValue();
if (!wxRemoveFile(i->second.GetFullPath())) {
LOG_D("provider/ffmpegsource/cache") << "failed to remove file " << STD_STR(i->second.GetFullPath());
continue;
}
cursize -= fsize;
numfiles--;
deleted++;
wxThread::This()->Sleep(250);
}
LOG_D("provider/ffmpegsource/cache") << "deleted " << deleted << " files, exiting";
return (wxThread::ExitCode)0;
}
#endif // WITH_FFMS2

View file

@ -74,9 +74,7 @@ public:
FFMS_LOG_DEBUG = 48,
};
/// Mutex preventing two cache cleaner threads from running at the same time
static wxMutex CleaningInProgress;
bool CleanCache();
void CleanCache();
FFMS_Index *DoIndexing(FFMS_Indexer *Indexer, const wxString& Cachename, int Trackmask, FFMS_IndexErrorHandling IndexEH);
std::map<int,wxString> GetTracksOfType(FFMS_Indexer *Indexer, FFMS_TrackType Type);
@ -88,16 +86,4 @@ public:
virtual ~FFmpegSourceProvider() {}
};
/// @class FFmpegSourceCacheCleaner
/// @brief Implements index cache cleaning functionality for the FFMS2 providers
class FFmpegSourceCacheCleaner : public wxThread {
FFmpegSourceProvider *parent;
public:
FFmpegSourceCacheCleaner(FFmpegSourceProvider *par);
~FFmpegSourceCacheCleaner() {};
wxThread::ExitCode Entry();
};
#endif /* WITH_FFMS2 */

View file

@ -36,12 +36,15 @@
#include "config.h"
#include "utils.h"
#ifndef AGI_PRE
#ifdef __UNIX__
#include <unistd.h>
#endif
#include <map>
#include <wx/dcmemory.h>
#include <wx/dir.h>
#include <wx/filename.h>
#include <wx/stdpaths.h>
#include <wx/window.h>
@ -53,7 +56,7 @@
#include <libaegisub/util_osx.h>
#endif
#include "utils.h"
#include "compat.h"
wxString MakeRelativePath(wxString _path, wxString reference) {
if (_path.empty() || _path[0] == '?') return _path;
@ -305,3 +308,116 @@ bool ForwardMouseWheelEvent(wxWindow *source, wxMouseEvent &evt) {
evt.Skip(false);
return false;
}
namespace {
class cache_cleaner : public wxThread {
wxString directory;
wxString file_type;
int64_t max_size;
size_t max_files;
ExitCode Entry() {
static wxMutex cleaning_mutex;
wxMutexLocker lock(cleaning_mutex);
if (!lock.IsOk()) {
LOG_D("utils/clean_cache") << "cleaning already in progress, thread exiting";
return (ExitCode)1;
}
wxDir cachedir;
if (!cachedir.Open(directory)) {
LOG_D("utils/clean_cache") << "couldn't open cache directory " << STD_STR(directory);
return (wxThread::ExitCode)1;
}
// sleep for a bit so we (hopefully) don't thrash the disk too much while indexing is in progress
wxThread::This()->Sleep(2000);
if (!cachedir.HasFiles(file_type)) {
LOG_D("utils/clean_cache") << "no files of the checked type in directory, exiting";
return (wxThread::ExitCode)0;
}
// unusually paranoid sanity check
// (someone might have deleted the file(s) after we did HasFiles() above; does wxDir.Open() lock the dir?)
wxString curfn_str;
if (!cachedir.GetFirst(&curfn_str, file_type, wxDIR_FILES)) {
LOG_D("utils/clean_cache") << "undefined error";
return (wxThread::ExitCode)1;
}
int64_t total_size = 0;
std::multimap<int64_t,wxFileName> cachefiles;
do {
wxFileName curfn(directory, curfn_str);
wxDateTime curatime;
curfn.GetTimes(&curatime, NULL, NULL);
cachefiles.insert(std::make_pair(curatime.GetTicks(), curfn));
total_size += curfn.GetSize().GetValue();
wxThread::This()->Sleep(250);
} while (cachedir.GetNext(&curfn_str));
if (cachefiles.size() <= max_files && total_size <= max_size) {
LOG_D("utils/clean_cache")
<< "cache does not need cleaning (maxsize=" << max_size
<< ", cursize=" << total_size
<< "; maxfiles=" << max_files
<< ", numfiles=" << cachefiles.size()
<< "), exiting";
return (wxThread::ExitCode)0;
}
int deleted = 0;
for (std::multimap<int64_t,wxFileName>::iterator i = cachefiles.begin(); i != cachefiles.end(); i++) {
// stop cleaning?
if ((total_size <= max_size && cachefiles.size() - deleted <= max_files) || cachefiles.size() - deleted < 2)
break;
int64_t fsize = i->second.GetSize().GetValue();
if (!wxRemoveFile(i->second.GetFullPath())) {
LOG_D("utils/clean_cache") << "failed to remove file " << STD_STR(i->second.GetFullPath());
continue;
}
total_size -= fsize;
++deleted;
wxThread::This()->Sleep(250);
}
LOG_D("utils/clean_cache") << "deleted " << deleted << " files, exiting";
return (wxThread::ExitCode)0;
}
public:
cache_cleaner(wxString const& directory, wxString const& file_type, int64_t max_size, int64_t max_files)
: wxThread(wxTHREAD_DETACHED)
, directory(directory)
, file_type(file_type)
, max_size(max_size << 20)
{
if (max_files < 1)
this->max_files = (size_t)-1;
else
this->max_files = (size_t)max_files;
}
};
}
void CleanCache(wxString const& directory, wxString const& file_type, int64_t max_size, int64_t max_files) {
LOG_D("utils/clean_cache") << "attempting to start cleaner thread";
wxThread *CleaningThread = new cache_cleaner(directory, file_type, max_size, max_files);
if (CleaningThread->Create() != wxTHREAD_NO_ERROR) {
LOG_D("utils/clean_cache") << "thread creation failed";
delete CleaningThread;
}
else if (CleaningThread->Run() != wxTHREAD_NO_ERROR) {
LOG_D("utils/clean_cache") << "failed to start thread";
delete CleaningThread;
}
LOG_D("utils/clean_cache") << "thread started successfully";
}

View file

@ -100,6 +100,12 @@ void RestartAegisub();
/// @return Should the calling code process the event?
bool ForwardMouseWheelEvent(wxWindow *source, wxMouseEvent &evt);
/// Clean up the given cache directory, limiting the size to max_size
/// @param directory Directory to clean
/// @param file_type Wildcard pattern for files to clean up
/// @param max_size Maximum size of directory in MB
/// @param max_files Maximum number of files
void CleanCache(wxString const& directory, wxString const& file_type, int64_t max_size, int64_t max_files = -1);
/// @brief Templated abs() function
template <typename T> T tabs(T x) { return x < 0 ? -x : x; }

View file

@ -138,9 +138,7 @@ void FFmpegSourceVideoProvider::LoadVideo(wxString filename) {
wxFileName(CacheName).Touch();
// we have now read the index and may proceed with cleaning the index cache
if (!CleanCache()) {
//do something?
}
CleanCache();
// track number still not set?
if (TrackNumber < 0) {