diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:13:13 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:13:13 +0200 |
commit | 7f23ee90fd545995a29e2175f15e8b97e59ca67a (patch) | |
tree | f8d0afac51995032e58b9a475ccbbc73ba207baf /library/icon_buffer.cpp | |
parent | 3.19 (diff) | |
download | FreeFileSync-7f23ee90fd545995a29e2175f15e8b97e59ca67a.tar.gz FreeFileSync-7f23ee90fd545995a29e2175f15e8b97e59ca67a.tar.bz2 FreeFileSync-7f23ee90fd545995a29e2175f15e8b97e59ca67a.zip |
3.20
Diffstat (limited to 'library/icon_buffer.cpp')
-rw-r--r-- | library/icon_buffer.cpp | 332 |
1 files changed, 149 insertions, 183 deletions
diff --git a/library/icon_buffer.cpp b/library/icon_buffer.cpp index 91487498..5d4dd19b 100644 --- a/library/icon_buffer.cpp +++ b/library/icon_buffer.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "icon_buffer.h" #include <wx/msgdlg.h> #include <map> @@ -13,6 +13,8 @@ #include <wx/log.h> #include "../shared/i18n.h" #include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp> +#include "../shared/loki/ScopeGuard.h" +#include <boost/thread/once.hpp> #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" @@ -28,38 +30,39 @@ using namespace zen; const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to buffer -//--------------------------------------------------------------------------------------------------- -typedef Zbase<Zchar, StorageDeepCopy> BasicString; //thread safe string class -//avoid reference-counted objects for shared data: NOT THREADSAFE!!! (implicitly shared variable: ref-count) -//--------------------------------------------------------------------------------------------------- - #ifdef FFS_WIN -BasicString getFileExtension(const BasicString& filename) +Zstring getFileExtension(const Zstring& filename) { - const BasicString shortName = filename.AfterLast(Zchar('\\')); //warning: using windows file name separator! + const Zstring shortName = afterLast(filename, Zchar('\\')); //warning: using windows file name separator! - return shortName.find(Zchar('.')) != BasicString::npos ? + return shortName.find(Zchar('.')) != Zstring::npos ? filename.AfterLast(Zchar('.')) : - BasicString(); + Zstring(); } +namespace +{ +std::set<Zstring, LessFilename> exceptions; //thread-safe! +boost::once_flag once = BOOST_ONCE_INIT; // +} + //test for extension for icons that physically have to be retrieved from disc -bool isPriceyExtension(const BasicString& extension) +bool isPriceyExtension(const Zstring& extension) { - static std::set<BasicString, LessFilename> exceptions; //not thread-safe, but called from worker thread only! - if (exceptions.empty()) + boost::call_once(once, []() { - exceptions.insert(Zstr("exe")); - exceptions.insert(Zstr("lnk")); - exceptions.insert(Zstr("ico")); - exceptions.insert(Zstr("ani")); - exceptions.insert(Zstr("cur")); - exceptions.insert(Zstr("url")); - exceptions.insert(Zstr("msc")); - exceptions.insert(Zstr("scr")); - } + exceptions.insert(L"exe"); + exceptions.insert(L"lnk"); + exceptions.insert(L"ico"); + exceptions.insert(L"ani"); + exceptions.insert(L"cur"); + exceptions.insert(L"url"); + exceptions.insert(L"msc"); + exceptions.insert(L"scr"); + }); + return exceptions.find(extension) != exceptions.end(); } #endif @@ -82,7 +85,7 @@ public: #ifdef FFS_WIN ::CopyIcon(other.handle_) #elif defined FFS_LINUX - gdk_pixbuf_copy(other.handle_) //create new Pix buf with reference count 1 or return 0 on error + ::gdk_pixbuf_copy(other.handle_) //create new Pix buf with reference count 1 or return 0 on error #endif ) {} @@ -98,14 +101,11 @@ public: #ifdef FFS_WIN ::DestroyIcon(handle_); #elif defined FFS_LINUX - g_object_unref(handle_); + ::g_object_unref(handle_); #endif } - void swap(IconHolder& other) //throw() - { - std::swap(handle_, other.handle_); - } + void swap(IconHolder& other) { std::swap(handle_, other.handle_); } //throw() wxIcon toWxIcon() const //copy HandleType, caller needs to take ownership! { @@ -130,7 +130,7 @@ private: }; -IconHolder getAssociatedIcon(const BasicString& filename) +IconHolder getAssociatedIcon(const Zstring& filename) { #ifdef FFS_WIN //despite what docu says about SHGetFileInfo() it can't handle all relative filenames, e.g. "\DirName" @@ -195,7 +195,7 @@ IconHolder getAssociatedIcon(const BasicString& filename) } #ifdef FFS_WIN -IconHolder getAssociatedIconByExt(const BasicString& extension) +IconHolder getAssociatedIconByExt(const Zstring& extension) { SHFILEINFO fileInfo = {}; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! @@ -211,80 +211,69 @@ IconHolder getAssociatedIconByExt(const BasicString& extension) #endif -const wxIcon& getDirectoryIcon() +wxIcon getDirectoryIcon() { - static wxIcon folderIcon; +#ifdef FFS_WIN + wxIcon folderIcon; - static bool isInitalized = false; //not thread-safe, but called from GUI thread only! - if (!isInitalized) - { - isInitalized = true; + SHFILEINFO fileInfo = {}; //initialize hIcon -#ifdef FFS_WIN - SHFILEINFO fileInfo = {}; //initialize hIcon + //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! + if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string + FILE_ATTRIBUTE_DIRECTORY, + &fileInfo, + sizeof(fileInfo), + SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && - //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string - FILE_ATTRIBUTE_DIRECTORY, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && + fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + { + folderIcon.SetHICON(fileInfo.hIcon); //transfer ownership! + folderIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); + } - fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - { - folderIcon.SetHICON(fileInfo.hIcon); //transfer ownership! - folderIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); - } + return folderIcon; #elif defined FFS_LINUX - folderIcon = ::getAssociatedIcon(Zstr("/usr/")).toWxIcon(); //all directories will look like "/usr/" + return ::getAssociatedIcon(Zstr("/usr/")).toWxIcon(); //all directories will look like "/usr/" #endif - } - return folderIcon; } -const wxIcon& getFileIcon() +wxIcon getFileIcon() { - static wxIcon fileIcon; - - static bool isInitalized = false; //not thread-safe, but called from GUI thread only! - if (!isInitalized) - { - isInitalized = true; + wxIcon fileIcon; #ifdef FFS_WIN - SHFILEINFO fileInfo = {}; //initialize hIcon + SHFILEINFO fileInfo = {}; //initialize hIcon - //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string - FILE_ATTRIBUTE_NORMAL, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && + //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! + if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string + FILE_ATTRIBUTE_NORMAL, + &fileInfo, + sizeof(fileInfo), + SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && - fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - { - fileIcon.SetHICON(fileInfo.hIcon); //transfer ownership! - fileIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); - } + fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + { + fileIcon.SetHICON(fileInfo.hIcon); //transfer ownership! + fileIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); + } #elif defined FFS_LINUX - try + try + { + Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); + if (iconTheme) { - Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); - if (iconTheme) - { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); - if (!iconPixbuf) - iconPixbuf = iconTheme->load_icon("text-x-generic", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); - if (iconPixbuf) - fileIcon.SetPixbuf(iconPixbuf->gobj_copy()); // transfer ownership!! - } + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + if (!iconPixbuf) + iconPixbuf = iconTheme->load_icon("text-x-generic", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + if (iconPixbuf) + fileIcon.SetPixbuf(iconPixbuf->gobj_copy()); // transfer ownership!! } - catch (const Glib::Error&) {} -#endif } + catch (const Glib::Error&) {} +#endif return fileIcon; } @@ -293,37 +282,66 @@ const wxIcon& getFileIcon() //---------------------- Shared Data ------------------------- struct WorkLoad { - std::vector<BasicString> filesToLoad; //processes last elements of vector first! - boost::mutex mutex; - boost::condition_variable condition; //signal event: data for processing available +public: + Zstring extractNextFile() //context of worker thread, blocking + { + boost::unique_lock<boost::mutex> dummy(lockFiles); + + while (filesToLoad.empty()) + conditionNewFiles.timed_wait(dummy, boost::get_system_time() + boost::posix_time::milliseconds(50)); //interruption point! + + Zstring fileName = filesToLoad.back(); + filesToLoad.pop_back(); + return fileName; + } + + void setWorkload(const std::vector<Zstring>& newLoad) //context of main thread + { + boost::unique_lock<boost::mutex> dummy(lockFiles); + filesToLoad = newLoad; + + conditionNewFiles.notify_one(); + //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref + } + +private: + std::vector<Zstring> filesToLoad; //processes last elements of vector first! + boost::mutex lockFiles; + boost::condition_variable conditionNewFiles; //signal event: data for processing available }; -typedef std::map<BasicString, IconHolder, LessFilename> NameIconMap; //entryName/icon -> ATTENTION: avoid ref-counting for this shared data structure! -typedef std::queue<BasicString> IconDbSequence; //entryName -struct Buffer +typedef std::map<Zstring, IconHolder, LessFilename> NameIconMap; //entryName/icon -> note: Zstring is thread-safe +typedef std::queue<Zstring> IconDbSequence; //entryName + +class Buffer { - boost::mutex lockAccess; - NameIconMap iconMappping; //use synchronisation when accessing this! +public: + bool requestFileIcon(const Zstring& fileName, wxIcon* icon = NULL); + void insertIntoBuffer(const Zstring& entryName, const IconHolder& icon); //called by worker thread + +private: + boost::mutex lockBuffer; + NameIconMap iconMappping; //use synchronisation when accessing this! IconDbSequence iconSequence; //save sequence of buffer entry to delete oldest elements }; //------------------------------------------------------------ -bool requestFileIcon(Buffer& buf, const Zstring& fileName, wxIcon* icon = NULL) +bool Buffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) //context of main AND worker thread { - boost::lock_guard<boost::mutex> dummy(buf.lockAccess); + boost::lock_guard<boost::mutex> dummy(lockBuffer); #ifdef FFS_WIN //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - const BasicString extension = getFileExtension(BasicString(fileName)); - const BasicString searchString = isPriceyExtension(extension) ? BasicString(fileName) : extension; - auto iter = buf.iconMappping.find(searchString); + const Zstring extension = getFileExtension(fileName); + const Zstring searchString = isPriceyExtension(extension) ? fileName : extension; + auto iter = iconMappping.find(searchString); #elif defined FFS_LINUX - auto iter = buf.iconMappping.find(BasicString(fileName)); + auto iter = iconMappping.find(fileName); #endif - if (iter == buf.iconMappping.end()) + if (iter == iconMappping.end()) return false; if (icon != NULL) @@ -332,23 +350,23 @@ bool requestFileIcon(Buffer& buf, const Zstring& fileName, wxIcon* icon = NULL) } -void insertIntoBuffer(Buffer& buf, const BasicString& entryName, const IconHolder& icon) //called by worker thread +void Buffer::insertIntoBuffer(const Zstring& entryName, const IconHolder& icon) //called by worker thread { - boost::lock_guard<boost::mutex> dummy(buf.lockAccess); + boost::lock_guard<boost::mutex> dummy(lockBuffer); //thread saftey: icon uses ref-counting! But is NOT shared with main thread! - auto rc = buf.iconMappping.insert(std::make_pair(entryName, icon)); + auto rc = iconMappping.insert(std::make_pair(entryName, icon)); if (rc.second) //if insertion took place - buf.iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! + iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! - assert(buf.iconMappping.size() == buf.iconSequence.size()); + assert(iconMappping.size() == iconSequence.size()); //remove elements if buffer becomes too big: - if (buf.iconMappping.size() > BUFFER_SIZE_MAX) //limit buffer size: critical because GDI resources are limited (e.g. 10000 on XP per process) + if (iconMappping.size() > BUFFER_SIZE_MAX) //limit buffer size: critical because GDI resources are limited (e.g. 10000 on XP per process) { //remove oldest element - buf.iconMappping.erase(buf.iconSequence.front()); - buf.iconSequence.pop(); + iconMappping.erase(iconSequence.front()); + iconSequence.pop(); } } //################################################################################################################################################ @@ -364,10 +382,8 @@ public: void operator()(); //thread entry private: - void doWork(); - - std::shared_ptr<WorkLoad> workload_; - std::shared_ptr<Buffer> buffer_; + std::shared_ptr<WorkLoad> workload_; //main/worker thread may access different shared_ptr instances safely (even though they have the same target!) + std::shared_ptr<Buffer> buffer_; //http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/shared_ptr.htm?sess=8153b05b34d890e02d48730db1ff7ddc#ThreadSafety }; @@ -375,79 +391,34 @@ void WorkerThread::operator()() //thread entry { //failure to initialize COM for each thread is a source of hard to reproduce bugs: https://sourceforge.net/tracker/?func=detail&aid=3160472&group_id=234430&atid=1093080 #ifdef FFS_WIN - struct ThreadInitializer - { - ThreadInitializer () { ::CoInitializeEx(NULL, COINIT_MULTITHREADED); } - ~ThreadInitializer() { ::CoUninitialize(); } - } dummy1; + ::CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); + Loki::ScopeGuard dummy = Loki::MakeGuard([]() { ::CoUninitialize(); }); + (void)dummy; #endif - try - { - while (true) - { - { - boost::unique_lock<boost::mutex> dummy(workload_->mutex); - while(workload_->filesToLoad.empty()) - workload_->condition.wait(dummy); //interruption point! - //shared.condition.timed_wait(dummy, boost::get_system_time() + boost::posix_time::milliseconds(100)); - } - - doWork(); //no need to lock the complete method! - } - } - catch (boost::thread_interrupted&) - { - throw; //this is the only reasonable exception! - } - catch (const std::exception& e) //exceptions must be catched per thread - { - wxSafeShowMessage(wxString(_("An exception occurred!")) + wxT("(Icon buffer)"), wxString::FromAscii(e.what())); //simple wxMessageBox won't do for threads - } - catch (...) //exceptions must be catched per thread - { - wxSafeShowMessage(wxString(_("An exception occurred!")) + wxT("(Icon buffer2)"), wxT("Unknown exception in icon thread!")); //simple wxMessageBox won't do for threads - } -} - - -void WorkerThread::doWork() -{ - //do work: get the file icon. while (true) { - BasicString fileName; - { - boost::lock_guard<boost::mutex> dummy(workload_->mutex); - if (workload_->filesToLoad.empty()) - break; //enter waiting state - fileName = workload_->filesToLoad.back(); //deep copy - workload_->filesToLoad.pop_back(); - } + boost::this_thread::interruption_point(); - if (requestFileIcon(*buffer_, Zstring(fileName))) //thread safety: Zstring okay, won't be reference-counted in requestIcon() + const Zstring fileName = workload_->extractNextFile(); //start work: get next icon to load + + if (buffer_->requestFileIcon(fileName)) continue; //icon already in buffer: skip #ifdef FFS_WIN - const BasicString extension = getFileExtension(fileName); //thread-safe: no sharing! + const Zstring extension = getFileExtension(fileName); if (isPriceyExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - { - const IconHolder newIcon = getAssociatedIcon(fileName); - insertIntoBuffer(*buffer_, fileName, newIcon); - } + buffer_->insertIntoBuffer(fileName, getAssociatedIcon(fileName)); else //no read-access to disk! determine icon by extension - { - const IconHolder newIcon = getAssociatedIconByExt(extension); - insertIntoBuffer(*buffer_, extension, newIcon); - } + buffer_->insertIntoBuffer(extension, getAssociatedIconByExt(extension)); + #elif defined FFS_LINUX const IconHolder newIcon = getAssociatedIcon(fileName); - insertIntoBuffer(*buffer_, fileName, newIcon); + buffer_->insertIntoBuffer(fileName, newIcon); #endif } } - //######################### redirect to impl ##################################################### struct IconBuffer::Pimpl @@ -456,7 +427,6 @@ struct IconBuffer::Pimpl workload(std::make_shared<WorkLoad>()), buffer(std::make_shared<Buffer>()) {} - std::shared_ptr<WorkLoad> workload; std::shared_ptr<Buffer> buffer; @@ -477,9 +447,6 @@ IconBuffer::~IconBuffer() pimpl->worker.join(); } -const wxIcon& IconBuffer::getDirectoryIcon() { return ::getDirectoryIcon(); } - -const wxIcon& IconBuffer::getFileIcon() { return ::getFileIcon(); } IconBuffer& IconBuffer::getInstance() { @@ -487,19 +454,18 @@ IconBuffer& IconBuffer::getInstance() return instance; } -bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) { return ::requestFileIcon(*pimpl->buffer, fileName, icon); } - -void IconBuffer::setWorkload(const std::vector<Zstring>& load) -{ - { - boost::lock_guard<boost::mutex> dummy(pimpl->workload->mutex); +bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) { return pimpl->buffer->requestFileIcon(fileName, icon); } - pimpl->workload->filesToLoad.clear(); +void IconBuffer::setWorkload(const std::vector<Zstring>& load) { pimpl->workload->setWorkload(load); } - std::transform(load.begin(), load.end(), std::back_inserter(pimpl->workload->filesToLoad), - [](const Zstring& file) { return BasicString(file); }); //make DEEP COPY from Zstring - } +const wxIcon& IconBuffer::getDirectoryIcon() +{ + static wxIcon dirIcon = ::getDirectoryIcon(); + return dirIcon; +} - pimpl->workload->condition.notify_one(); - //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref +const wxIcon& IconBuffer::getFileIcon() +{ + static wxIcon fileIcon = ::getFileIcon(); + return fileIcon; } |