diff --git a/src/downloadmanagement.cpp b/src/downloadmanagement.cpp index 3e2cbeb..edb850a 100644 --- a/src/downloadmanagement.cpp +++ b/src/downloadmanagement.cpp @@ -79,6 +79,10 @@ DownloadManager::~DownloadManager() { QThread* t = mp_downloadUpdaterThread; mp_downloadUpdaterThread = nullptr; // tell the thread to terminate + + // At this point the thread may be stuck waiting for data. + // Let's wake it up. + m_requestQueue.enqueue(""); t->wait(); } } @@ -88,20 +92,36 @@ bool DownloadManager::downloadingFunctionalityAvailable() const return mp_downloader != nullptr; } +void DownloadManager::processDownloadActions() +{ + while ( mp_downloadUpdaterThread != nullptr ) { + const QString bookId = m_requestQueue.dequeue(); + if ( !bookId.isEmpty() ) { + updateDownload(bookId); + } + } +} + void DownloadManager::startDownloadUpdaterThread() { // so that DownloadInfo can be copied across threads qRegisterMetaType("DownloadInfo"); mp_downloadUpdaterThread = QThread::create([=]() { - while ( mp_downloadUpdaterThread != nullptr ) { + processDownloadActions(); + }); + + mp_downloadUpdaterThread->start(); + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, [this]() { + if ( m_requestQueue.isEmpty() ) { for ( const auto& bookId : m_downloads.keys() ) { - updateDownload(bookId); + m_requestQueue.enqueue(bookId); } - QThread::msleep(1000); } }); - mp_downloadUpdaterThread->start(); + timer->start(1000); } void DownloadManager::restoreDownloads() diff --git a/src/downloadmanagement.h b/src/downloadmanagement.h index fa80484..311620d 100644 --- a/src/downloadmanagement.h +++ b/src/downloadmanagement.h @@ -5,8 +5,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -17,6 +19,38 @@ typedef QMap DownloadInfo; +template +class ThreadSafeQueue +{ +public: + void enqueue(const T& x) + { + const QMutexLocker threadSafetyGuarantee(&m_mutex); + m_queue.enqueue(x); + m_queueIsNotEmpty.wakeAll(); + } + + T dequeue() + { + const QMutexLocker threadSafetyGuarantee(&m_mutex); + if ( m_queue.isEmpty() ) + m_queueIsNotEmpty.wait(&m_mutex); + + return m_queue.dequeue(); + } + + bool isEmpty() const + { + const QMutexLocker threadSafetyGuarantee(&m_mutex); + return m_queue.isEmpty(); + } + +private: // data + mutable QMutex m_mutex; + QQueue m_queue; + QWaitCondition m_queueIsNotEmpty; +}; + class DownloadState { public: // types @@ -114,7 +148,11 @@ signals: void downloadUpdated(QString bookId, const DownloadInfo& ); void downloadDisappeared(QString bookId); +private: // types + typedef ThreadSafeQueue RequestQueue; + private: // functions + void processDownloadActions(); void updateDownload(QString bookId); private: // data @@ -122,6 +160,7 @@ private: // data kiwix::Downloader* const mp_downloader; Downloads m_downloads; QThread* mp_downloadUpdaterThread = nullptr; + RequestQueue m_requestQueue; }; #endif // DOWNLOADMANAGEMENT_H