mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-91167 stop a background SDK update that outlives its project or SDK
An update starts while a project is open and can still be running when the project closes or the SDK is removed. `PyUpdateSdkTask.run` then works with a disposed SDK. Registering the indicator disposable under it throws IncorrectOperationException, and `PythonPackageManager.forSdk` can throw AlreadyDisposedException. In tests both surface as uncaught exceptions during fixture teardown. `scheduleUpdate` and `run` now bail out when the project or the SDK is gone, and the indicator disposable uses `tryRegister`, which reports a disposed parent instead of throwing. `onFinished` decided the same thing too late. It removed the SDK from `ourUnderRefresh`, added it back for the queued update, and only then checked disposal, so the entry stayed in a static set forever. That retained a disposed SDK and made every later `scheduleUpdate` believe a refresh was in flight, which blocked all further updates for that SDK. The decision now happens once, under `ourLock`. (cherry picked from commit 37010251b6d5b9798d29a03488984047a7272328) GitOrigin-RevId: ca0ac05248574297525455bab8144e8bbfe6626d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
67c65997d7
commit
34b07455ae
@@ -156,7 +156,7 @@ public final class PythonSdkUpdater {
|
||||
}
|
||||
|
||||
private static void scheduleUpdate(@NotNull Sdk sdk, @NotNull Project project, @NotNull PyUpdateSdkRequestData requestData) {
|
||||
if (project.isDisposed()) {
|
||||
if (project.isDisposed() || (sdk instanceof Disposable sdkDisposable && Disposer.isDisposed(sdkDisposable))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -261,12 +261,17 @@ public final class PythonSdkUpdater {
|
||||
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
if (myProject.isDisposed() || isSdkDisposed()) {
|
||||
return;
|
||||
}
|
||||
PythonPackageManager manager = PythonPackageManager.Companion.forSdk(myProject, mySdk);
|
||||
// Cancel the indicator when the SDK is disposed to terminate any running processes (e.g., skeleton generation).
|
||||
// This explicit cancellation should become unnecessary on migrating PythonSdkUpdater to coroutines and withBackgroundProgress.
|
||||
Disposable indicatorDisposable = getIndicatorDisposable(indicator);
|
||||
if (mySdk instanceof Disposable sdkDisposable) {
|
||||
Disposer.register(sdkDisposable, indicatorDisposable);
|
||||
if (!Disposer.tryRegister(sdkDisposable, indicatorDisposable)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Disposer.register(PythonPluginDisposable.getInstance(myProject), indicatorDisposable);
|
||||
@@ -420,15 +425,18 @@ public final class PythonSdkUpdater {
|
||||
boolean existed = ourUnderRefresh.remove(mySdk);
|
||||
LOG.assertTrue(existed, "Error in SDK refresh scheduling: refreshed SDK is not in the set.");
|
||||
requestData = ourToBeRefreshed.remove(mySdk);
|
||||
if (requestData != null) {
|
||||
// Decide under the lock whether the queued update really starts: re-adding the SDK and only then
|
||||
// discovering that the project or the SDK is gone would leave it in ourUnderRefresh forever, which
|
||||
// both retains a disposed SDK and makes every later scheduleUpdate believe a refresh is in flight.
|
||||
if (requestData != null && !myProject.isDisposed() && !isSdkDisposed()) {
|
||||
ourUnderRefresh.add(mySdk);
|
||||
}
|
||||
else {
|
||||
requestData = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (requestData != null) {
|
||||
if (Disposer.isDisposed(myProject)) {
|
||||
return;
|
||||
}
|
||||
ProgressManager.getInstance().run(new PyUpdateSdkTask(myProject, mySdk, requestData));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user