Lock in "RoslynParser.ForceReparseText"

Hi,

in our app, I observe a deadlock in our call to “RoslynParser.ForceReparseText”. I have to call this method to create a printable RTF stream (by using “SyntaxEdit.SaveStream (…, new RtfExport())”). It if is not called, syntax highlighting might miss.

It hangs in the line “ParseSyntaxAsync(document, 0, int.MaxValue, CancellationToken.None).Wait();” in “ForceReparseText”.
I had already noticed at other places in our apps that “Task.Wait” does not work in WinForms environment. So “ForceReparseText” should probably be async and use “await” calls?

Unfortunately, I cannot reproduce the problem with a standalone sample. So I hope you have an idea.
Or should I extract the code of “ForceReparseText” and call “ParseSyntaxAsync”/“ParseSemanticAsync” myself?

Best regards

Wolfgang

The same code works in the .NET Framework 4.8 branch of our app, but the issue happens in the .NET 6.0 branch.

I now remember why we also need the call to “ForceReparseText” in the filling code of the editor: it is based on the “SnippetParsers” sample, and it might happen sometimes that the code editor cannot resolve code parts from our own application assemblies and highlights them as errors. A call to “ForceReparseText” fixes those errors.

A possible workaround seems to be to add a “ForceReparseTextAsync” method to our “VBParser” subclass:

    public async Task ForceReparseTextAsync(bool withSemantic = true)
    {
      Document document = UpdateDocument();
      if (document != null)
      {
        await ParseSyntaxAsync(document, 0, int.MaxValue, CancellationToken.None);
        if (withSemantic)
        {
          await ParseSemanticAsync(document, CancellationToken.None);
        }

        if ((Options & SyntaxOptions.NotifyOnParse) != 0)
        {
          Notify(new SyntaxParserEventArgs(update: true, withSemantic, 0, int.MaxValue));
        }
      }
    }

Seems to be an issue with our large app, as the problem is not reproduceable in the SnippetParsers sample.

And I will have to make the printing code (which requires a parsed RTF) async.

Hello,

Thank you for reporting this. We added the ForceReparseTextAsync method to the RoslynParser, as you suggested. This change will be published in the next NuGet packages release. In the meantime, please use the workaround you suggested.

We could not reproduce the hanging issue on our tests. If for some reason the solution with ForceReparseTextAsync is not fully suitable, we could arrange a remote debugging session.

Well, I have to admit I don’t know whether “ForceReparseTextAsync” is reasonable. I have no idea why it fails for me. Seems to be related to the way we run our application - we use a thread per ribbon form, so that modal dialogs are only model to the form they were opened from. But even with a similar approach, I could not reproduce the locks.

But I noticed something: when I initialize my code editor in the real application, the initialization code including “ForceReparseText” is called twice. And the second call causes this exception:

System.OperationCanceledException
  HResult=0x8013153B
  Message=The operation was canceled.
  Source=System.Private.CoreLib
  StackTrace:
   at System.Threading.CancellationToken.ThrowOperationCanceledException()
   at Microsoft.CodeAnalysis.Classification.AbstractSyntaxClassificationService.Worker.ClassifyNode(SyntaxNode syntax)
   at Microsoft.CodeAnalysis.Classification.AbstractSyntaxClassificationService.Worker.ProcessNodes()
   at Microsoft.CodeAnalysis.Classification.AbstractSyntaxClassificationService.Worker.Classify(SemanticModel semanticModel, TextSpan textSpan, ArrayBuilder`1 list, Func`2 getNodeClassifiers, Func`2 getTokenClassifiers, ClassificationOptions options, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Classification.AbstractSyntaxClassificationService.AddSemanticClassifications(SemanticModel semanticModel, TextSpan textSpan, Func`2 getNodeClassifiers, Func`2 getTokenClassifiers, ArrayBuilder`1 result, ClassificationOptions options, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Classification.Classifier.GetClassifiedSpans(HostWorkspaceServices workspaceServices, SemanticModel semanticModel, TextSpan textSpan, ClassificationOptions options, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Classification.Classifier.<GetClassifiedSpansAsync>d__0.MoveNext()
   at Alternet.Syntax.Parsers.Roslyn.RoslynParser.Tokenizer.<ParsedAsync>d__10.MoveNext()
   at Alternet.Syntax.Parsers.Roslyn.RoslynParser.<ParseSyntaxAsync>d__50.MoveNext()
   at HG.Tools.Guiwin.AlternetWrapper.VBMethodParser.<ParseSyntaxAsync>d__18.MoveNext() in E:\...VBSnippetParser.cs:line 529

  This exception was originally thrown at this call stack:
    System.Threading.CancellationToken.ThrowOperationCanceledException()
    Microsoft.CodeAnalysis.Classification.AbstractSyntaxClassificationService.Worker.ClassifyNode(Microsoft.CodeAnalysis.SyntaxNode)
    Microsoft.CodeAnalysis.Classification.AbstractSyntaxClassificationService.Worker.ProcessNodes()
    Microsoft.CodeAnalysis.Classification.AbstractSyntaxClassificationService.Worker.Classify(Microsoft.CodeAnalysis.SemanticModel, Microsoft.CodeAnalysis.Text.TextSpan, Microsoft.CodeAnalysis.PooledObjects.ArrayBuilder<Microsoft.CodeAnalysis.Classification.ClassifiedSpan>, System.Func<Microsoft.CodeAnalysis.SyntaxNode, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Classification.Classifiers.ISyntaxClassifier>>, System.Func<Microsoft.CodeAnalysis.SyntaxToken, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Classification.Classifiers.ISyntaxClassifier>>, Microsoft.CodeAnalysis.Classification.ClassificationOptions, System.Threading.CancellationToken)
    Microsoft.CodeAnalysis.Classification.AbstractSyntaxClassificationService.AddSemanticClassifications(Microsoft.CodeAnalysis.SemanticModel, Microsoft.CodeAnalysis.Text.TextSpan, System.Func<Microsoft.CodeAnalysis.SyntaxNode, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Classification.Classifiers.ISyntaxClassifier>>, System.Func<Microsoft.CodeAnalysis.SyntaxToken, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Classification.Classifiers.ISyntaxClassifier>>, Microsoft.CodeAnalysis.PooledObjects.ArrayBuilder<Microsoft.CodeAnalysis.Classification.ClassifiedSpan>, Microsoft.CodeAnalysis.Classification.ClassificationOptions, System.Threading.CancellationToken)
    Microsoft.CodeAnalysis.Classification.Classifier.GetClassifiedSpans(Microsoft.CodeAnalysis.Host.HostWorkspaceServices, Microsoft.CodeAnalysis.SemanticModel, Microsoft.CodeAnalysis.Text.TextSpan, Microsoft.CodeAnalysis.Classification.ClassificationOptions, System.Threading.CancellationToken)
    Microsoft.CodeAnalysis.Classification.Classifier.GetClassifiedSpansAsync(Microsoft.CodeAnalysis.Document, Microsoft.CodeAnalysis.Text.TextSpan, System.Threading.CancellationToken)
    Alternet.Syntax.Parsers.Roslyn.RoslynParser.Tokenizer.ParsedAsync(Microsoft.CodeAnalysis.Document, int, int, System.Threading.CancellationToken)
    Alternet.Syntax.Parsers.Roslyn.RoslynParser.ParseSyntaxAsync(Microsoft.CodeAnalysis.Document, int, int, System.Threading.CancellationToken)
    HG.Tools.Guiwin.AlternetWrapper.VBMethodParser.ParseSyntaxAsync(Microsoft.CodeAnalysis.Document, int, int, System.Threading.CancellationToken) in VBSnippetParser.cs

If I remove the second call, then the lock does not occur and the editor opens fine.

Maybe something is in an invalid state due to the cancel error? But I also could not reproduce this with your sample.

All this async/await is off my head I fear…

Best regards

Wolfgang

So, the bugfix is probably to remove the duplicate filling of our control.

Hi Wolfgan,

Let us know if you get it working without locks/OperationCanceledException exception, or if we need to look further at it.

BTW, catching OperationCanceledException seems to be a valid scenario in case the async task gets cancelled - we do it on other async methods.

Kind regards,
Dmitry

Well, it works for me now, so there is no need to change anything in your code. I don’t have an explanation for the initial error and cannot reproduce it, so let’s keep it as is.

Thanks for your help!

Wolfgang

1 Like