From 3855b4610654aa4b926061e0e9d66f64566ca6f4 Mon Sep 17 00:00:00 2001 From: Pierre Lamot <pierre@videolabs.io> Date: Mon, 2 Sep 2024 11:49:46 +0200 Subject: [PATCH] qt: make MLThreadRunner a disctinctive object from MediaLib this allows to handle the part that requires the deleteLater within the MediaLib class, this will allow us to handle the lifecycle of MediaLib like the other singletons --- modules/gui/qt/maininterface/mainctx.cpp | 2 +- modules/gui/qt/medialibrary/medialib.cpp | 97 ++++++++++++++---------- modules/gui/qt/medialibrary/medialib.hpp | 68 ++++++++++++----- 3 files changed, 105 insertions(+), 62 deletions(-) diff --git a/modules/gui/qt/maininterface/mainctx.cpp b/modules/gui/qt/maininterface/mainctx.cpp index e7fc42a38bfd..c68ff2848fcf 100644 --- a/modules/gui/qt/maininterface/mainctx.cpp +++ b/modules/gui/qt/maininterface/mainctx.cpp @@ -294,7 +294,7 @@ MainCtx::~MainCtx() var_DelCallback( libvlc, "intf-popupmenu", PopupMenuCB, p_intf ); if (m_medialib) - m_medialib->destroy(); + delete m_medialib; p_intf->p_mi = NULL; } diff --git a/modules/gui/qt/medialibrary/medialib.cpp b/modules/gui/qt/medialibrary/medialib.cpp index 5fb0b8fb21f1..df00cbb57bf3 100644 --- a/modules/gui/qt/medialibrary/medialib.cpp +++ b/modules/gui/qt/medialibrary/medialib.cpp @@ -34,42 +34,12 @@ MediaLib::MediaLib(qt_intf_t *_intf, vlc::playlist::PlaylistController* playlist { m_event_cb.reset( vlc_ml_event_register_callback( m_ml, MediaLib::onMediaLibraryEvent, this ) ); - - /* https://xkcd.com/221/ */ - m_mlThreadPool.setMaxThreadCount(4); + m_runner = new MLThreadRunner(m_ml); } MediaLib::~MediaLib() { - assert(m_objectTasks.empty()); - assert(m_runningTasks.empty()); -} - -void MediaLib::destroy() -{ - m_shuttingDown = true; - //try to cancel as many tasks as possible - for (auto taskIt = m_objectTasks.begin(); taskIt != m_objectTasks.end(); /**/) - { - const QObject* object = taskIt.key(); - quint64 key = taskIt.value(); - auto task = m_runningTasks.value(key, nullptr); - if (m_mlThreadPool.tryTake(task)) - { - delete task; - m_runningTasks.remove(key); - taskIt = m_objectTasks.erase(taskIt); - if (m_objectTasks.count(object) == 0) - disconnect(object, &QObject::destroyed, this, &MediaLib::runOnMLThreadTargetDestroyed); - } - else - ++taskIt; - } - - if (m_runningTasks.empty()) - { - deleteLater(); - } + m_runner->destroy(); } static void convertMLItemToPlaylistMedias(vlc_medialibrary_t* ml, const MLItemId & itemId, const QStringList &options, QVector<vlc::playlist::Media>& medias) @@ -434,13 +404,54 @@ void MediaLib::onMediaLibraryEvent( void* data, const vlc_ml_event_t* event ) } } + + +MLThreadRunner::MLThreadRunner(vlc_medialibrary_t* ml) + : m_ml(ml) +{ + m_mlThreadPool.setMaxThreadCount(4); +} + +MLThreadRunner::~MLThreadRunner() +{ + assert(m_objectTasks.empty()); + assert(m_runningTasks.empty()); +} + +void MLThreadRunner::destroy() +{ + m_shuttingDown = true; + //try to cancel as many tasks as possible + for (auto taskIt = m_objectTasks.begin(); taskIt != m_objectTasks.end(); /**/) + { + const QObject* object = taskIt.key(); + quint64 key = taskIt.value(); + auto task = m_runningTasks.value(key, nullptr); + if (m_mlThreadPool.tryTake(task)) + { + delete task; + m_runningTasks.remove(key); + taskIt = m_objectTasks.erase(taskIt); + if (m_objectTasks.count(object) == 0) + disconnect(object, &QObject::destroyed, this, &MLThreadRunner::runOnMLThreadTargetDestroyed); + } + else + ++taskIt; + } + + if (m_runningTasks.empty()) + { + deleteLater(); + } +} + quint64 MediaLib::runOnMLThread(const QObject* obj, std::function< void(vlc_medialibrary_t* ml)> mlCb, std::function< void()> uiCb, const char* queue) { struct NoCtx{}; - return runOnMLThread<NoCtx>(obj, + return m_runner->runOnMLThread<NoCtx>(obj, [mlCb](vlc_medialibrary_t* ml, NoCtx&){ mlCb(ml); }, @@ -455,7 +466,7 @@ quint64 MediaLib::runOnMLThread(const QObject* obj, std::function< void(quint64)> uiCb, const char* queue) { struct NoCtx{}; - return runOnMLThread<NoCtx>(obj, + return m_runner->runOnMLThread<NoCtx>(obj, [mlCb](vlc_medialibrary_t* ml, NoCtx&){ mlCb(ml); }, @@ -470,7 +481,7 @@ quint64 MediaLib::runOnMLThread(const QObject* obj, const char* queue) { struct NoCtx{}; - return runOnMLThread<NoCtx>(obj, + return m_runner->runOnMLThread<NoCtx>(obj, [mlCb](vlc_medialibrary_t* ml, NoCtx&){ mlCb(ml); }, @@ -479,8 +490,12 @@ quint64 MediaLib::runOnMLThread(const QObject* obj, queue); } - void MediaLib::cancelMLTask(const QObject* object, quint64 taskId) +{ + m_runner->cancelMLTask(object, taskId); +} + +void MLThreadRunner::cancelMLTask(const QObject* object, quint64 taskId) { assert(taskId != 0); @@ -494,10 +509,10 @@ void MediaLib::cancelMLTask(const QObject* object, quint64 taskId) m_runningTasks.remove(taskId); m_objectTasks.remove(object, taskId); if (m_objectTasks.count(object) == 0) - disconnect(object, &QObject::destroyed, this, &MediaLib::runOnMLThreadTargetDestroyed); + disconnect(object, &QObject::destroyed, this, &MLThreadRunner::runOnMLThreadTargetDestroyed); } -void MediaLib::runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target, const QObject* object, int status) +void MLThreadRunner::runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target, const QObject* object, int status) { if (m_shuttingDown) { @@ -506,7 +521,7 @@ void MediaLib::runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target m_runningTasks.remove(target); m_objectTasks.remove(object, target); if (m_objectTasks.count(object) == 0) - disconnect(object, &QObject::destroyed, this, &MediaLib::runOnMLThreadTargetDestroyed); + disconnect(object, &QObject::destroyed, this, &MLThreadRunner::runOnMLThreadTargetDestroyed); } if (m_runningTasks.empty()) deleteLater(); @@ -518,12 +533,12 @@ void MediaLib::runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target m_runningTasks.remove(target); m_objectTasks.remove(object, target); if (m_objectTasks.count(object) == 0) - disconnect(object, &QObject::destroyed, this, &MediaLib::runOnMLThreadTargetDestroyed); + disconnect(object, &QObject::destroyed, this, &MLThreadRunner::runOnMLThreadTargetDestroyed); } runner->deleteLater(); } -void MediaLib::runOnMLThreadTargetDestroyed(QObject * object) +void MLThreadRunner::runOnMLThreadTargetDestroyed(QObject * object) { if (m_objectTasks.contains(object)) { diff --git a/modules/gui/qt/medialibrary/medialib.hpp b/modules/gui/qt/medialibrary/medialib.hpp index 68d0b763e124..0bb30b02cd71 100644 --- a/modules/gui/qt/medialibrary/medialib.hpp +++ b/modules/gui/qt/medialibrary/medialib.hpp @@ -35,6 +35,7 @@ class Media; struct vlc_medialibrary_t; +class MLThreadRunner; class RunOnMLThreadBaseRunner; class MediaLib : public QObject @@ -47,15 +48,9 @@ public: Q_PROPERTY(QString discoveryEntryPoint READ discoveryEntryPoint NOTIFY discoveryEntryPointChanged FINAL) Q_PROPERTY(bool idle READ idle NOTIFY idleChanged FINAL) - enum MLTaskStatus { - ML_TASK_STATUS_SUCCEED, - ML_TASK_STATUS_CANCELED - }; - public: MediaLib(qt_intf_t* _intf, vlc::playlist::PlaylistController* playlistController, QObject* _parent = nullptr ); - - void destroy(); + ~MediaLib(); Q_INVOKABLE void addToPlaylist(const MLItemId &itemId, const QStringList &options = {}); Q_INVOKABLE void addToPlaylist(const QString& mrl, const QStringList &options = {}); @@ -183,14 +178,8 @@ signals: void idleChanged(); private: - //use the destroy function - ~MediaLib(); static void onMediaLibraryEvent( void* data, const vlc_ml_event_t* event ); -private slots: - void runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target, const QObject* object, int status); - void runOnMLThreadTargetDestroyed(QObject * object); - private: qt_intf_t* m_intf; vlc::playlist::PlaylistController* m_playlistController = nullptr; @@ -203,16 +192,46 @@ private: /* Medialibrary */ vlc_medialibrary_t* m_ml; std::unique_ptr<vlc_ml_event_callback_t, std::function<void(vlc_ml_event_callback_t*)>> m_event_cb; + MLThreadRunner* m_runner = nullptr; + + QMap<QVector<MLItemId>, QVector<QJSValue>> m_inputItemQuery; +}; +class MLThreadRunner : public QObject +{ + Q_OBJECT + +public: + enum MLTaskStatus { + ML_TASK_STATUS_SUCCEED, + ML_TASK_STATUS_CANCELED + }; + + MLThreadRunner(vlc_medialibrary_t* ml); + ~MLThreadRunner(); + + void destroy(); + void cancelMLTask(const QObject* object, quint64 taskId); + + template<typename Ctx> + quint64 runOnMLThread(const QObject* obj, + std::function<void (vlc_medialibrary_t*, Ctx&)> mlFun, + std::function<void (quint64 taskId, Ctx&)> uiFun, + const char* queue); + +private: + vlc_medialibrary_t* m_ml = nullptr; MLThreadPool m_mlThreadPool; - /* run on ml thread properties */ bool m_shuttingDown = false; quint64 m_taskId = 1; QMap<quint64, RunOnMLThreadBaseRunner*> m_runningTasks; QMultiMap<const QObject*, quint64> m_objectTasks; - QMap<QVector<MLItemId>, QVector<QJSValue>> m_inputItemQuery; + +private slots: + void runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target, const QObject* object, int status); + void runOnMLThreadTargetDestroyed(QObject * object); }; class RunOnMLThreadBaseRunner : public QObject, public QRunnable @@ -250,11 +269,11 @@ public: { if (m_canceled) { - emit done(this, m_taskId, m_obj, MediaLib::ML_TASK_STATUS_CANCELED); + emit done(this, m_taskId, m_obj, MLThreadRunner::ML_TASK_STATUS_CANCELED); return; } m_mlFun(m_ml, m_ctx); - emit done(this, m_taskId, m_obj, MediaLib::ML_TASK_STATUS_SUCCEED); + emit done(this, m_taskId, m_obj, MLThreadRunner::ML_TASK_STATUS_SUCCEED); } //called from UI thread @@ -278,7 +297,7 @@ private: }; template<typename Ctx> -quint64 MediaLib::runOnMLThread(const QObject* obj, +quint64 MLThreadRunner::runOnMLThread(const QObject* obj, std::function<void (vlc_medialibrary_t*, Ctx&)> mlFun, std::function<void (quint64 taskId, Ctx&)> uiFun, const char* queue) @@ -288,10 +307,19 @@ quint64 MediaLib::runOnMLThread(const QObject* obj, auto taskId = m_taskId++; auto runnable = new RunOnMLThreadRunner<Ctx>(taskId, obj, mlFun, uiFun, m_ml); - connect(runnable, &RunOnMLThreadBaseRunner::done, this, &MediaLib::runOnMLThreadDone); - connect(obj, &QObject::destroyed, this, &MediaLib::runOnMLThreadTargetDestroyed); + connect(runnable, &RunOnMLThreadBaseRunner::done, this, &MLThreadRunner::runOnMLThreadDone); + connect(obj, &QObject::destroyed, this, &MLThreadRunner::runOnMLThreadTargetDestroyed); m_runningTasks.insert(taskId, runnable); m_objectTasks.insert(obj, taskId); m_mlThreadPool.start(runnable, queue); return taskId; } + +template<typename Ctx> +quint64 MediaLib::runOnMLThread(const QObject* obj, + std::function<void (vlc_medialibrary_t*, Ctx&)> mlFun, + std::function<void (quint64 taskId, Ctx&)> uiFun, + const char* queue) +{ + return m_runner->runOnMLThread<Ctx>(obj, mlFun, uiFun, queue); +} -- GitLab