diff --git a/aegisub/src/ffmpegsource_common.cpp b/aegisub/src/ffmpegsource_common.cpp index 5a94c6883..52148f467 100644 --- a/aegisub/src/ffmpegsource_common.cpp +++ b/aegisub/src/ffmpegsource_common.cpp @@ -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 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(curatime.GetTicks(),curfn)); - - while (cachedir.GetNext(&curfn_str)) { - curfn = wxFileName(cachedirname, curfn_str); - curfn.GetTimes(&curatime, NULL, NULL); - cachefiles.insert(std::pair(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::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 - - diff --git a/aegisub/src/ffmpegsource_common.h b/aegisub/src/ffmpegsource_common.h index 7cfc87c66..cfa84e4f2 100644 --- a/aegisub/src/ffmpegsource_common.h +++ b/aegisub/src/ffmpegsource_common.h @@ -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 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 */ diff --git a/aegisub/src/utils.cpp b/aegisub/src/utils.cpp index b5b73b730..d9e704b51 100644 --- a/aegisub/src/utils.cpp +++ b/aegisub/src/utils.cpp @@ -36,12 +36,15 @@ #include "config.h" +#include "utils.h" + #ifndef AGI_PRE #ifdef __UNIX__ #include #endif +#include -#include +#include #include #include #include @@ -53,7 +56,7 @@ #include #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 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::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"; +} diff --git a/aegisub/src/utils.h b/aegisub/src/utils.h index 77d2e8096..6837ba329 100644 --- a/aegisub/src/utils.h +++ b/aegisub/src/utils.h @@ -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 T tabs(T x) { return x < 0 ? -x : x; } diff --git a/aegisub/src/video_provider_ffmpegsource.cpp b/aegisub/src/video_provider_ffmpegsource.cpp index 4e1df3275..a614ae855 100644 --- a/aegisub/src/video_provider_ffmpegsource.cpp +++ b/aegisub/src/video_provider_ffmpegsource.cpp @@ -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) {