GstEnginePipeline: Fix file descriptor exhaustion by using shared thread pool

Replace per-pipeline QThreadPool with a shared static pool to prevent
file descriptor and thread exhaustion. Each GstEnginePipeline was creating
its own thread pool, leading to resource accumulation during frequent
pipeline creation/destruction (track changes, seeking, crossfade).

The shared pool is limited to 2 threads max since state changes are
typically sequential per pipeline. This prevents the crash in g_wakeup_new()
when creating eventfd for new thread event dispatchers.

Fixes #1687
This commit is contained in:
Jonas Kvinge
2025-12-18 19:58:23 +01:00
parent 1d03bb2178
commit c684a95f89
2 changed files with 21 additions and 2 deletions

View File

@@ -42,6 +42,7 @@
#include <QObject> #include <QObject>
#include <QCoreApplication> #include <QCoreApplication>
#include <QtConcurrentRun> #include <QtConcurrentRun>
#include <QThreadPool>
#include <QFuture> #include <QFuture>
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QMutex> #include <QMutex>
@@ -98,6 +99,23 @@ constexpr int kEqBandFrequencies[] = { 60, 170, 310, 600, 1000, 3000, 6000, 1200
int GstEnginePipeline::sId = 1; int GstEnginePipeline::sId = 1;
QThreadPool *GstEnginePipeline::shared_state_threadpool() {
// C++11 guarantees thread-safe initialization of static local variables
static QThreadPool pool;
static const auto init = []() {
// Limit the number of threads to prevent resource exhaustion
// Use 2 threads max since state changes are typically sequential per pipeline
pool.setMaxThreadCount(2);
return true;
}();
Q_UNUSED(init);
return &pool;
}
GstEnginePipeline::GstEnginePipeline(QObject *parent) GstEnginePipeline::GstEnginePipeline(QObject *parent)
: QObject(parent), : QObject(parent),
id_(sId++), id_(sId++),
@@ -1848,7 +1866,7 @@ QFuture<GstStateChangeReturn> GstEnginePipeline::SetState(const GstState state)
watcher->deleteLater(); watcher->deleteLater();
SetStateFinishedSlot(state, state_change_return); SetStateFinishedSlot(state, state_change_return);
}); });
QFuture<GstStateChangeReturn> future = QtConcurrent::run(&set_state_threadpool_, &gst_element_set_state, pipeline_, state); QFuture<GstStateChangeReturn> future = QtConcurrent::run(shared_state_threadpool(), &gst_element_set_state, pipeline_, state);
watcher->setFuture(future); watcher->setFuture(future);
return future; return future;

View File

@@ -215,7 +215,8 @@ class GstEnginePipeline : public QObject {
static int sId; static int sId;
mutex_protected<int> id_; mutex_protected<int> id_;
QThreadPool set_state_threadpool_; // Shared thread pool for all pipeline state changes to prevent thread/FD exhaustion
static QThreadPool *shared_state_threadpool();
bool playbin3_support_; bool playbin3_support_;
bool volume_full_range_support_; bool volume_full_range_support_;