diff options
Diffstat (limited to 'library/iconBuffer.cpp')
-rw-r--r-- | library/iconBuffer.cpp | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/library/iconBuffer.cpp b/library/iconBuffer.cpp new file mode 100644 index 00000000..a969f689 --- /dev/null +++ b/library/iconBuffer.cpp @@ -0,0 +1,298 @@ +#include "iconBuffer.h" +#include <wx/thread.h> +#include "globalFunctions.h" +#include <wx/utils.h> +#include <wx/bitmap.h> +#include <wx/msw/wrapwin.h> //includes "windows.h" +#include <wx/msgdlg.h> +#include "../algorithm.h" +#include <wx/icon.h> +#include <map> +#include <queue> + +using FreeFileSync::IconBuffer; + + +class BasicString //simple thread safe string class +{ +public: + BasicString() + { + data = new DefaultChar[1]; //compliance with delete [] + *data = 0; //compliance with c_str() + } + + BasicString(const Zstring& other) //make DEEP COPY from Zstring! + { + data = new DefaultChar[other.length() + 1]; + memcpy(data, other.c_str(), (other.length() + 1) * sizeof(DefaultChar)); + } + + BasicString(const BasicString& other) + { + const size_t otherLen = defaultLength(other.c_str()); + data = new DefaultChar[otherLen + 1]; + memcpy(data, other.c_str(), (otherLen + 1) * sizeof(DefaultChar)); + } + + ~BasicString() + { + delete [] data; + } + + BasicString& operator=(const BasicString& other) + { //exception safety; handle self-assignment implicitly + const size_t otherLen = defaultLength(other.c_str()); + DefaultChar* dataNew = new DefaultChar[otherLen + 1]; + memcpy(dataNew, other.c_str(), (otherLen + 1) * sizeof(DefaultChar)); + + delete data; + data = dataNew; + + return *this; + } + + const DefaultChar* c_str() const + { + return data; + } + +private: + DefaultChar* data; +}; + + +class WorkerThread : public wxThread +{ +public: + WorkerThread(IconBuffer* iconBuff); + + void setWorkload(const std::vector<Zstring>& load); //(re-)set new workload of icons to be retrieved + void quitThread(); + + virtual ExitCode Entry(); + +private: + void doWork(); + +//---------------------- Shared Data ------------------------- + typedef BasicString FileName; + + wxCriticalSection lockWorkload; //use for locking shared data + std::vector<FileName> workload; //processes last elements of vector first! + bool threadHasMutex; + bool threadExitIsRequested; +//------------------------------------------------------------ + + //event: icon buffer -> woker thread + wxMutex threadIsListening; + wxCondition continueWork; //wake up thread + + IconBuffer* iconBuffer; +}; + + +WorkerThread::WorkerThread(IconBuffer* iconBuff) : + wxThread(wxTHREAD_JOINABLE), + threadHasMutex(false), + threadExitIsRequested(false), + threadIsListening(), + continueWork(threadIsListening), + iconBuffer(iconBuff) +{ + if (Create() != wxTHREAD_NO_ERROR) + throw RuntimeException(wxString(wxT("Error creating icon buffer worker thread!"))); + + if (Run() != wxTHREAD_NO_ERROR) + throw RuntimeException(wxString(wxT("Error starting icon buffer worker thread!"))); + + //wait until thread has aquired mutex + bool hasMutex = false; + while (!hasMutex) + { + wxMilliSleep(5); + wxCriticalSectionLocker dummy(lockWorkload); + hasMutex = threadHasMutex; + } //polling -> it's not nice, but works and is no issue +} + + +void WorkerThread::setWorkload(const std::vector<Zstring>& load) //(re-)set new workload of icons to be retrieved +{ + wxCriticalSectionLocker dummy(lockWorkload); + + workload.clear(); + for (std::vector<Zstring>::const_iterator i = load.begin(); i != load.end(); ++i) + workload.push_back(*i); //make DEEP COPY from Zstring! + + if (!workload.empty()) + continueWork.Signal(); //wake thread IF he is waiting +} + + +void WorkerThread::quitThread() +{ + wxMutexLocker dummy(threadIsListening); //wait until thread is in waiting state + threadExitIsRequested = true; //no sharing conflicts in this situation + continueWork.Signal(); //exit thread +} + + +wxThread::ExitCode WorkerThread::Entry() +{ + try + { + wxMutexLocker dummy(threadIsListening); //this lock needs to be called from WITHIN the thread => calling it from constructor(Main thread) would be useless + { //this mutex STAYS locked all the time except of continueWork.Wait()! + wxCriticalSectionLocker dummy(lockWorkload); + threadHasMutex = true; + } + + while (true) + { + continueWork.Wait(); //waiting for continueWork.Signal(); unlocks Mutex "threadIsListening" + + //no mutex needed in this context + if (threadExitIsRequested) //no mutex here: atomicity is not prob for a bool, but visibility (e.g. caching in registers) + return 0; //shouldn't be a problem nevertheless because of implicit memory barrier caused by mutex.Lock() in .Wait() + + //do work: get the file icon. + doWork(); + } + } + catch (RuntimeException& e) //exceptions must be catched per thread + { + wxMessageBox(e.show()); + return 0; + } +} + + +void WorkerThread::doWork() +{ + BasicString fileName; //don't use Zstring: reference-counted objects are NOT THREADSAFE!!! e.g. double deletion might happen + + //do work: get the file icon. + while (true) + { + { + wxCriticalSectionLocker dummy(lockWorkload); + if (workload.empty()) + break; //enter waiting state + fileName = workload.back(); + workload.pop_back(); + } + + if (iconBuffer->requestIcon(Zstring(fileName.c_str()))) //thread safety: Zstring okay, won't be reference-counted in requestIcon() + break; //icon already in buffer: enter waiting state + + //load icon + SHFILEINFO fileInfo; + fileInfo.hIcon = 0; //initialize hIcon + + if (SHGetFileInfo(fileName.c_str(), //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup! + 0, + &fileInfo, + sizeof(fileInfo), + SHGFI_ICON | SHGFI_SMALLICON) && + + fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + { //bug report: https://sourceforge.net/tracker/?func=detail&aid=2768004&group_id=234430&atid=1093080 + + wxIcon newIcon; //attention: wxIcon uses reference counting! + newIcon.SetHICON(fileInfo.hIcon); + newIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); + + iconBuffer->insertIntoBuffer(fileName.c_str(), newIcon); //thread safety: icon may be deleted only within insertIntoBuffer() + + //freeing of icon handle seems to happen somewhere beyond wxIcon destructor + //if (!DestroyIcon(fileInfo.hIcon)) + // throw RuntimeException(wxString(wxT("Error deallocating Icon handle!\n\n")) + FreeFileSync::getLastErrorFormatted()); + + } + else + { + //if loading of icon fails for whatever reason, just save a dummy icon to avoid re-loading + iconBuffer->insertIntoBuffer(fileName.c_str(), wxNullIcon); + } + } +} + +//--------------------------------------------------------------------------------------------------- + +typedef Zstring FileName; +class IconDB : public std::map<FileName, wxIcon> {}; +class IconDbSequence : public std::queue<FileName> {}; + +//--------------------------------------------------------------------------------------------------- + + +IconBuffer& IconBuffer::getInstance() +{ + static IconBuffer instance; + return instance; +} + + +IconBuffer::IconBuffer() : + lockIconDB( new wxCriticalSection), + buffer( new IconDB), + bufSequence(new IconDbSequence), + worker( new WorkerThread(this)) //might throw exceptions! +{} + + +IconBuffer::~IconBuffer() +{ + worker->quitThread(); + worker->Wait(); //wait until thread has exitted +} + + +bool IconBuffer::requestIcon(const Zstring& fileName, wxIcon* icon) +{ + wxCriticalSectionLocker dummy(*lockIconDB); + IconDB::const_iterator i = buffer->find(fileName); + + if (i != buffer->end()) + { + if (icon != NULL) + *icon = i->second; + return true; + } + + return false; +} + + +void IconBuffer::setWorkload(const std::vector<Zstring>& load) +{ + worker->setWorkload(load); +} + + +void IconBuffer::insertIntoBuffer(const DefaultChar* fileName, const wxIcon& icon) //called by worker thread +{ + if (icon.IsOk()) //this check won't hurt + { + wxCriticalSectionLocker dummy(*lockIconDB); + + const Zstring fileNameZ = fileName; + + const std::pair<IconDB::iterator, bool> rc = buffer->insert(IconDB::value_type(fileNameZ, icon)); + + if (rc.second) //if insertion took place + bufSequence->push(fileNameZ); + + assert(buffer->size() == bufSequence->size()); + + //remove elements if buffer becomes too big: + if (buffer->size() > 1000) //limit buffer size: critical because large buffers seem to cause various wxIcon/wxBitmap issues! + { + //remove oldest element + buffer->erase(bufSequence->front()); + bufSequence->pop(); + } + } +} + |