Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qprocessscheduler.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <QtQmlDom/private/qqmldom_utils_p.h>
6
8
9namespace QmlLsp {
10
11Q_STATIC_LOGGING_CATEGORY(schedulerLog, "qt.languageserver.qprocessscheduler")
12
13using namespace Qt::StringLiterals;
14
15/*!
16 \internal
17 \class QProcessScheduler
18
19 \brief Runs multiple processes sequentially via a QProcess, and signals once they are done.
20
21 QProcessScheduler runs multiple programs sequentially through a QProcess, and emits the \c done
22 signal once all programs finished. Use schedule(programs, id) to add new programs to be run. The
23 id is used to signal their completion. Duplicate programs that already are in the queue are not
24 re-added to the queue.
25 */
27{
28 QObject::connect(&m_process, &QProcess::finished, this, &QProcessScheduler::processNext);
29 QObject::connect(&m_process, &QProcess::errorOccurred, this,
30 &QProcessScheduler::onErrorOccurred);
31}
33{
34 QObject::disconnect(&m_process, nullptr);
35 m_process.kill();
36 m_process.waitForFinished();
37}
38
39QT_WARNING_PUSH
40QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // use of std::variant
41void QProcessScheduler::schedule(const QList<Command> &list, const Id &id)
42{
43 m_queue.enqueue(StartMarker{ id });
44 const int queueSize = m_queue.size();
45 for (const auto &x : list) {
46 const QueueElement queueElement{ x };
47 if (!m_queue.contains(queueElement))
48 m_queue.enqueue(queueElement);
49 }
50
51 // remove startmarker if no queue element was added, instead just emit that this task was done
52 if (queueSize == m_queue.size()) {
53 m_queue.removeLast(); // remove start marker
54 }
55
56 m_queue.enqueue(EndMarker{ id });
57 if (!std::exchange(m_isRunning, true))
58 QMetaObject::invokeMethod(this, &QProcessScheduler::processNext, Qt::QueuedConnection);
59}
61
62static bool isStartMarkerOf(const QProcessScheduler::QueueElement &e,
63 const QProcessScheduler::Id &id)
64{
65 const auto startMarker = std::get_if<QProcessScheduler::StartMarker>(&e);
66 if (!startMarker)
67 return false;
68 return startMarker->id == id;
69}
70
71static bool isEndMarkerOf(const QProcessScheduler::QueueElement &e, const QProcessScheduler::Id &id)
72{
73 const auto endMarker = std::get_if<QProcessScheduler::EndMarker>(&e);
74 if (!endMarker)
75 return false;
76 return endMarker->id == id;
77}
78
79void QProcessScheduler::cancel(const Id &id)
80{
81 auto removeQueueElementsForStartMarkerIt = [this, &id](auto begin) {
82 auto end = std::find_if(begin, m_queue.cend(),
83 std::bind(isEndMarkerOf, std::placeholders::_1, id));
84
85 // also include the marker during erase
86 if (end != m_queue.cend())
87 std::advance(end, 1);
88 m_queue.erase(begin, end);
89 };
90
91 if (m_current != id) {
92 const auto begin = std::find_if(m_queue.cbegin(), m_queue.cend(),
93 std::bind(isStartMarkerOf, std::placeholders::_1, id));
94 removeQueueElementsForStartMarkerIt(begin);
95 emit cancelled(id);
96 return;
97 }
98
99 {
100 QObject::disconnect(&m_process, &QProcess::finished, this, &QProcessScheduler::processNext);
101 QObject::disconnect(&m_process, &QProcess::errorOccurred, this,
102 &QProcessScheduler::onErrorOccurred);
103 // note: kill and waitForFinished triggers the finished signal, which we don't want to trigger
104 m_process.kill();
105 m_process.waitForFinished();
106 removeQueueElementsForStartMarkerIt(m_queue.cbegin());
107 QObject::connect(&m_process, &QProcess::finished, this, &QProcessScheduler::processNext);
108 QObject::connect(&m_process, &QProcess::errorOccurred, this,
109 &QProcessScheduler::onErrorOccurred);
110 }
111
112 emit cancelled(id);
113 if (!std::exchange(m_isRunning, true))
114 QMetaObject::invokeMethod(this, &QProcessScheduler::processNext, Qt::QueuedConnection);
115}
116
117void QProcessScheduler::processNext()
118{
119 m_isRunning = m_queue.size() > 0;
120 if (!m_isRunning)
121 return;
122
123 std::visit(qOverloadedVisitor{
124 [this](const StartMarker &start) {
125 emit started(start.id);
126 m_current = start.id;
127 processNext();
128 },
129 [this](const EndMarker &end) {
130 m_current.reset();
131 emit done(end.id);
132 processNext();
133 },
134 [this](const Command &command) {
135 m_process.setProgram(command.program);
136 m_process.setArguments(command.arguments);
137 m_process.setProcessEnvironment(command.customEnvironment);
138 m_process.start();
139 },
140 },
141 m_queue.dequeue());
142}
143
144void QProcessScheduler::onErrorOccurred(QProcess::ProcessError error)
145{
146 qCDebug(schedulerLog) << "Process" << m_process.program() << m_process.arguments().join(" "_L1)
147 << "had an error:" << error;
148 processNext();
149}
150
151} // namespace QmlLsp
152
153QT_END_NAMESPACE
Runs multiple processes sequentially via a QProcess, and signals once they are done.
Combined button and popup list for selecting options.
static bool isEndMarkerOf(const QProcessScheduler::QueueElement &e, const QProcessScheduler::Id &id)
static QT_WARNING_POP bool isStartMarkerOf(const QProcessScheduler::QueueElement &e, const QProcessScheduler::Id &id)