dotnet / dotnet/fsharp

VS hangs: project options reactor reads the caret through UI-thread COM while the UI thread waits for the reactor

Ouverte
#20,522 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
Needs-Triage
Langage dominant
F#
Étoiles
4.3k
Forks
876
Merge moyen
5 j 9 h
PR mergées (30 j)
153

Description

Visual Studio can hang for good when the project options reactor computes options for a document in *F# Miscellaneous Files* or for a script while the UI thread synchronously waits for those options. The reactor asks the UI thread for the caret, and the UI thread is blocked waiting for the reactor.

The caret lookup came with #18393, which gave `GetProjectOptionsFromScript` a `caret` so that a `#r "nuget: …"` line is not resolved while it is still being typed (#18231).

**Repro steps**

Caught once in the debugger, not reproduced on demand. The window is any synchronous UI-thread wait on project options for such a document while the reactor is busy with it. Here it was:

1. A solution restored with an F# document tab whose project is not loaded, so the file opened in *F# Miscellaneous Files*, with breakpoints set in it.
2. An extension's `QueryStatus` asked the not-yet-loaded frame for its text view, which forced `EnsureDocumentFrameLoaded`.
3. Showing the frame made the debugger validate the breakpoint locations on the UI thread.

**Actual behavior**

Project options reactor thread:

```
Microsoft.VisualStudio.Services.VsTask.InternalGetResult
Microsoft.VisualStudio.Shell.ServiceProvider.QueryService
Microsoft.VisualStudio.Shell.ServiceProvider.GetService
Extensions.Document.TryGetIVsTextView Common/Extensions.fs
Extensions.Document.TryGetTextViewAndCaretPos Common/Extensions.fs
FSharpProjectOptionsReactor.textViewAndCaret LanguageService/FSharpProjectOptionsManager.fs
FSharpProjectOptionsReactor.getProjectOptionsFromScript
… tryComputeOptionsBySingleScriptOrFile, started from the reactor's MailboxProcessor loop
```

UI thread:

```
Microsoft.VisualStudio.Threading.JoinableTaskFactory.WaitSynchronouslyCore
Microsoft.VisualStudio.Threading.JoinableTask.CompleteOnCurrentThread
AbstractLanguageService.VsLanguageDebugInfo.ValidateBreakpointLocation
Microsoft.VisualStudio.Platform.WindowManagement.Rdt.NotifyOnBeforeShow
WindowFrame.NotifyFrameShowing / ShowInternal / Show / TryReplacePlaceholderView
WindowFrame.LoadDocumentFrameInternalAsync
WindowFrame.EnsureDocumentFrameLoaded / GetProperty
… an extension's IOleCommandTarget.QueryStatus asking for the active text view
```

`ValidateBreakpointLocation` goes through F# breakpoint resolution to the parse results and so to the reactor, which sits on the first stack. `ServiceProvider.GlobalProvider.GetService` called off the UI thread needs the UI thread, and so do the RDT, `IVsTextManager.GetActiveView`, `IVsTextView.GetCaretPos` and the `IVsTextViewEvents` connection point that follow it. The reactor is a `MailboxProcessor`, so its work is not joined to the `JoinableTask` the UI thread waits for, and `JTF.Run` never runs the request. Switching the reactor to the UI thread with `SwitchToMainThreadAsync` would hang the same way.

The document here was a plain `.fs` file, for which the caret is useless: `ScriptClosure.resolveDependencyManagerSources` is its only reader, and only scripts have `#r "nuget: …"` lines. The same path also subscribed to `IVsTextViewEvents` for every such `.fs` file and recomputed script options on every caret line change.

**Expected behavior**

The reactor never waits for the UI thread: the UI thread publishes the caret, the reactor reads it, and files that are not scripts do not look for it at all.

**Related information**

* Windows 11, Visual Studio 18 Insiders, experimental instance
* The code path is unchanged on `main`

Guide de contribution

Ouvrir le guide de contribution

Piste de recherche

Commencez dans Common/Extensions.fs et LanguageService/FSharpProjectOptionsManager.fs, en suivant FSharpProjectOptionsReactor.textViewAndCaret jusqu’à getProjectOptionsFromScript. Vérifiez comment ScriptClosure.resolveDependencyManagerSources utilise le caret et comment les fichiers .fs qui ne sont pas des scripts s’abonnent à IVsTextViewEvents. Le travail est terminé lorsque le reactor n’attend jamais le UI thread et que les fichiers qui ne sont pas des scripts ne demandent pas de données de caret.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
fsharp
Domaine
devtools
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
Active
Clarté
Plutôt claire
Accessibilité débutants
48/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.