Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion src/tools/debugadapter/debugger/python/pythondebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,30 @@ void PythonDebugger::initialize(const QString &pythonExecute,
d->process.setProcessEnvironment(env);
d->process.start("/bin/bash", options);
d->process.waitForStarted();
QThread::msleep(500); // The port may not start listening immediately when Python starts, resulting in the IDE being unable to connect. Wait for 500ms.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the polling loop to an asynchronous approach using a QTimer and asynchronous QProcess calls to reduce complexity.

Consider refactoring the polling loop to an asynchronous approach using a centrally managed QTimer and asynchronous QProcess calls. This would simplify the manual retry logic and eliminate spawning a new process on every cycle. For example, you could extract the port check into a dedicated function and use QTimer to periodically trigger the check:

// In your header, add a signal/slot if not already present:
class PythonDebugger : public QObject {
    Q_OBJECT
    // ...
private slots:
    void checkPortReadiness();
private:
    QTimer *portCheckTimer = nullptr;
};
// In your implementation after starting debugpy:
void PythonDebugger::startDebugpy() {
    // Existing process start code ...

    // Initialize asynchronous port check
    portCheckTimer = new QTimer(this);
    portCheckTimer->setInterval(100);
    connect(portCheckTimer, &QTimer::timeout, this, &PythonDebugger::checkPortReadiness);
    portCheckTimer->start();
}

void PythonDebugger::checkPortReadiness() {
    QProcess *checkPort = new QProcess(this);
    QString cmd = QString("netstat -an | grep LISTEN | grep %1").arg(d->port);
    connect(checkPort, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
            [this, checkPort](int, QProcess::ExitStatus) {
                if (!checkPort->readAll().isEmpty()) {
                    portCheckTimer->stop();
                    qInfo() << "Debugpy port" << d->port << "is ready";
                }
                checkPort->deleteLater();
            });
    checkPort->start("bash", {"-c", cmd});
}

This approach centralizes the retry logic via a timer and creates a new QProcess only when needed. It keeps functionality intact while reducing overall complexity and unnecessary repeated process creations.


// debugpy may not ready when launched,so we should delay when it start listen port.
const int maxRetries = 50;
const int retryInterval = 100;
bool portReady = false;

for (int i = 0; i < maxRetries && !portReady; i++) {
QProcess checkPort;
QString cmd = QString("netstat -an | grep LISTEN | grep %1").arg(d->port);
checkPort.start("bash", {"-c", cmd});
checkPort.waitForFinished();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Add a timeout to waitForFinished().

Specify a timeout for waitForFinished to avoid hanging indefinitely if the netstat command stalls.

Suggested change
checkPort.waitForFinished();
if (!checkPort.waitForFinished(500)) {
// If the netstat command stalls, kill the process and continue the retry loop
checkPort.kill();
qWarning() << "Netstat command timed out for port" << d->port;
QThread::msleep(retryInterval);
continue;
}


if (!checkPort.readAll().isEmpty()) {
portReady = true;
qInfo() << "Debugpy port" << d->port << "is ready after" << (i + 1) * retryInterval << "ms";
break;
}

QThread::msleep(retryInterval);
}

if (!portReady) {
qWarning() << "Debugpy port" << d->port << "failed to start after" << maxRetries * retryInterval << "ms";
}
}

void PythonDebugger::slotReceiveClientInfo(const QString &ppid,
Expand Down
Loading