vlc | branch: master | Romain Vimont <[email protected]> | Thu Nov 19 16:00:55 2020 +0100| [e5be287fcab83aff14cd825bfd3ba91ca4cdbc2a] | committer: Pierre Lamot
qt: medialib: make cache load() asynchronous Make the list cache use async tasks to load the data asynchronously (and notify data changes once available). Signed-off-by: Pierre Lamot <[email protected]> > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=e5be287fcab83aff14cd825bfd3ba91ca4cdbc2a --- modules/gui/qt/medialibrary/mlbasemodel.hpp | 3 +- modules/gui/qt/util/listcache.hpp | 125 ++++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 10 deletions(-) diff --git a/modules/gui/qt/medialibrary/mlbasemodel.hpp b/modules/gui/qt/medialibrary/mlbasemodel.hpp index 39a67cdd46..22ae5e6300 100644 --- a/modules/gui/qt/medialibrary/mlbasemodel.hpp +++ b/modules/gui/qt/medialibrary/mlbasemodel.hpp @@ -219,8 +219,9 @@ protected: if (m_cache) return; + auto &threadPool = m_mediaLib->threadPool(); auto loader = createLoader(); - m_cache.reset(new ListCache<std::unique_ptr<T>>(loader)); + m_cache.reset(new ListCache<std::unique_ptr<T>>(threadPool, loader)); connect(&*m_cache, &BaseListCache::localDataChanged, this, &MLSlidingWindowModel<T>::onLocalDataChanged); diff --git a/modules/gui/qt/util/listcache.hpp b/modules/gui/qt/util/listcache.hpp index c762e84ede..fb9cfb5814 100644 --- a/modules/gui/qt/util/listcache.hpp +++ b/modules/gui/qt/util/listcache.hpp @@ -24,10 +24,13 @@ #endif #include "vlc_common.h" +#include <cassert> #include <memory> #include <vector> #include <QtGlobal> #include <QObject> +#include <QSharedPointer> +#include "asynctask.hpp" /** * `ListCache<T>` represents a cache for a (constant) list of items. @@ -37,6 +40,9 @@ * - `count()` returns the number of items in the list; * - `load(index, count)` returning the items for the requested interval. * + * These functions are assumed to be long-running, so they executed from a + * separate thread, not to block the UI thread. + * * The precise cache strategy is unspecified (it may change in the future), but * the general principle is to keep locally only a part of the whole data. * @@ -67,6 +73,34 @@ class BaseListCache : public QObject signals: void localDataChanged(size_t index, size_t count); + +protected slots: + virtual void onLoadResult() = 0; +}; + +template <typename T> +class LoadTask; + +struct MLRange +{ + size_t offset = 0; + size_t count = 0; + + MLRange() = default; + + MLRange(size_t offset, size_t count) + : offset(offset) + , count(count) + { + } + + bool isEmpty() { + return count == 0; + } + + bool contains(size_t index) { + return index >= offset && index < offset + count; + } }; template <typename T> @@ -75,9 +109,12 @@ class ListCache : public BaseListCache public: static constexpr ssize_t COUNT_UNINITIALIZED = -1; - ListCache(ListCacheLoader<T> *loader, size_t chunkSize = 100) - : m_loader(loader) + ListCache(QThreadPool &threadPool, ListCacheLoader<T> *loader, + size_t chunkSize = 100) + : m_threadPool(threadPool) + , m_loader(loader) , m_chunkSize(chunkSize) {} + ~ListCache(); /** * Return the item at specified index @@ -110,14 +147,32 @@ public: void refer(size_t index); private: - std::unique_ptr<ListCacheLoader<T>> m_loader; + void onLoadResult() override; + + void asyncLoad(size_t offset, size_t count); + + QThreadPool &m_threadPool; + /* Ownershipshared between this cache and the runnable spawned to execute + * loader callbacks */ + QSharedPointer<ListCacheLoader<T>> m_loader; size_t m_chunkSize; std::vector<T> m_list; ssize_t m_total_count = COUNT_UNINITIALIZED; size_t m_offset = 0; + + MLRange m_lastRangeRequested; + + LoadTask<T> *m_loadTask = nullptr; }; +template <typename T> +ListCache<T>::~ListCache() +{ + if (m_loadTask) + m_loadTask->abandon(); +} + template <typename T> const T *ListCache<T>::get(size_t index) const { @@ -156,16 +211,68 @@ void ListCache<T>::refer(size_t index) } /* index outside the known portion of the list */ - if (index < m_offset || index >= m_offset + m_list.size()) + if (!m_lastRangeRequested.contains(index)) { /* FIXME bad heuristic if the interval of visible items crosses a cache * page boundary */ - m_offset = index - index % m_chunkSize; - size_t count = qMin(m_total_count - m_offset, m_chunkSize); - m_list = m_loader->load(m_offset, count); - if (m_list.size()) - emit localDataChanged(m_offset, m_list.size()); + size_t offset = index - index % m_chunkSize; + size_t count = qMin(m_total_count - offset, m_chunkSize); + asyncLoad(offset, count); } } +template <typename T> +class LoadTask : public AsyncTask<std::vector<T>> +{ +public: + LoadTask(QSharedPointer<ListCacheLoader<T>> loader, size_t offset, + size_t count) + : m_loader(loader) + , m_offset(offset) + , m_count(count) + { + } + + std::vector<T> execute() override + { + return m_loader->load(m_offset, m_count); + } + +private: + QSharedPointer<ListCacheLoader<T>> m_loader; + size_t m_offset; + size_t m_count; + + friend class ListCache<T>; +}; + +template <typename T> +void ListCache<T>::asyncLoad(size_t offset, size_t count) +{ + if (m_loadTask) + /* Cancel any current pending task */ + m_loadTask->abandon(); + + m_loadTask = new LoadTask<T>(m_loader, offset, count); + connect(m_loadTask, &BaseAsyncTask::result, + this, &ListCache<T>::onLoadResult); + m_lastRangeRequested = { offset, count }; + m_loadTask->start(m_threadPool); +} + +template <typename T> +void ListCache<T>::onLoadResult() +{ + LoadTask<T> *task = static_cast<LoadTask<T> *>(sender()); + assert(task == m_loadTask); + + m_offset = task->m_offset; + m_list = task->takeResult(); + if (m_list.size()) + emit localDataChanged(m_offset, m_list.size()); + + task->abandon(); + m_loadTask = nullptr; +} + #endif _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
