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
39void QProcessScheduler::schedule(const QList<Command> &list, const Id &id)
40{
41 m_queue.enqueue(StartMarker{ id });
42 for (const auto &x : list) {
43 const QueueElement queueElement{ x };
44 if (!m_queue.contains(queueElement))
45 m_queue.enqueue(queueElement);
46 }
47 m_queue.enqueue(EndMarker{ id });
48 if (!m_isRunning)
49 processNext();
50}
51
52static bool isStartMarkerOf(const QProcessScheduler::QueueElement &e,
53 const QProcessScheduler::Id &id)
54{
55 const auto startMarker = std::get_if<QProcessScheduler::StartMarker>(&e);
56 if (!startMarker)
57 return false;
58 return startMarker->id == id;
59}
60
61static bool isEndMarkerOf(const QProcessScheduler::QueueElement &e, const QProcessScheduler::Id &id)
62{
63 const auto endMarker = std::get_if<QProcessScheduler::EndMarker>(&e);
64 if (!endMarker)
65 return false;
66 return endMarker->id == id;
67}
68
69void QProcessScheduler::cancel(const Id &id)
70{
71 auto removeQueueElementsForStartMarkerIt = [this, &id](auto begin) {
72 auto end = std::find_if(begin, m_queue.cend(),
73 std::bind(isEndMarkerOf, std::placeholders::_1, id));
74
75 // also include the marker during erase
76 if (end != m_queue.cend())
77 std::advance(end, 1);
78 m_queue.erase(begin, end);
79 };
80
81 if (m_current != id) {
82 const auto begin = std::find_if(m_queue.cbegin(), m_queue.cend(),
83 std::bind(isStartMarkerOf, std::placeholders::_1, id));
84 removeQueueElementsForStartMarkerIt(begin);
85 emit cancelled(id);
86 return;
87 }
88
89 {
90 QObject::disconnect(&m_process, &QProcess::finished, this, &QProcessScheduler::processNext);
91 QObject::disconnect(&m_process, &QProcess::errorOccurred, this,
92 &QProcessScheduler::onErrorOccurred);
93 // note: kill and waitForFinished triggers the finished signal, which we don't want to trigger
94 m_process.kill();
95 m_process.waitForFinished();
96 removeQueueElementsForStartMarkerIt(m_queue.cbegin());
97 QObject::connect(&m_process, &QProcess::finished, this, &QProcessScheduler::processNext);
98 QObject::connect(&m_process, &QProcess::errorOccurred, this,
99 &QProcessScheduler::onErrorOccurred);
100 }
101
102 emit cancelled(id);
103 processNext();
104}
105
106void QProcessScheduler::processNext()
107{
108 m_isRunning = m_queue.size() > 0;
109 if (!m_isRunning)
110 return;
111
112 std::visit(qOverloadedVisitor{
113 [this](const StartMarker &start) {
114 emit started(start.id);
115 m_current = start.id;
116 processNext();
117 },
118 [this](const EndMarker &end) {
119 m_current.reset();
120 emit done(end.id);
121 processNext();
122 },
123 [this](const Command &command) {
124 m_process.setProgram(command.program);
125 m_process.setArguments(command.arguments);
126 m_process.setProcessEnvironment(command.customEnvironment);
127 m_process.start();
128 },
129 },
130 m_queue.dequeue());
131}
132
133void QProcessScheduler::onErrorOccurred(QProcess::ProcessError error)
134{
135 qCDebug(schedulerLog) << "Process" << m_process.program() << m_process.arguments().join(" "_L1)
136 << "had an error:" << error;
137 processNext();
138}
139
140} // namespace QmlLsp
141
142QT_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 isStartMarkerOf(const QProcessScheduler::QueueElement &e, const QProcessScheduler::Id &id)
static bool isEndMarkerOf(const QProcessScheduler::QueueElement &e, const QProcessScheduler::Id &id)