summaryrefslogtreecommitdiff
path: root/library/icon_buffer.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:13:13 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:13:13 +0200
commit7f23ee90fd545995a29e2175f15e8b97e59ca67a (patch)
treef8d0afac51995032e58b9a475ccbbc73ba207baf /library/icon_buffer.cpp
parent3.19 (diff)
downloadFreeFileSync-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.cpp332
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;
}
bgstack15