Skip to content

Conversation

@chsienki
Copy link
Member

Previously we were using the fatal error handler to do filtering which has been obsoleted via #58094.

Also fixes #58290 by adding a missing filter to the syntax receiver shim.

@chsienki chsienki added this to the 17.2 milestone Jan 13, 2022
@chsienki chsienki requested a review from a team as a code owner January 13, 2022 19:21
@chsienki
Copy link
Member Author

@dotnet/roslyn-compiler for review please.

@chsienki chsienki force-pushed the source-generators/cancellation-filters branch from 886503d to 0918664 Compare January 13, 2022 19:24
}
}
catch (Exception e)
catch (Exception e) when (ExceptionUtilities.IsNotCancelled(e, cancellationToken))
Copy link
Member

Choose a reason for hiding this comment

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

Is this missing one causing issues? Do we need this in 17.1?

Make all uses take a token
Pass the token through from parent wrapper operations.
@chsienki
Copy link
Member Author

Ping @dotnet/roslyn-compiler for reviews please :)

#pragma warning disable CS0618 // ReportIfNonFatalAndCatchUnlessCanceled is obsolete; tracked by https://github.com/dotnet/roslyn/issues/58375
catch (Exception e) when (FatalError.ReportIfNonFatalAndCatchUnlessCanceled(e, cancellationToken))
#pragma warning restore CS0618 // ReportIfNonFatalAndCatchUnlessCanceled is obsolete
catch (Exception e) when (!ExceptionUtilities.IsCurrentOperationBeingCancelled(e, cancellationToken))
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, I'd imagine Initialize can't ever throw a cancellation exception, right? So should the filter just be removed here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed. Changed

@jasonmalinowski jasonmalinowski dismissed their stale review January 18, 2022 23:20

Code changed and brings new concerns.


var cts = new CancellationTokenSource();
var testGenerator = new CallbackGenerator(
onInit: (i) => i.RegisterForSyntaxNotifications(() => new TestSyntaxReceiver(tag: 0, callback: (a) => { if (a is AssignmentExpressionSyntax){ cts.Cancel(); cts.Token.ThrowIfCancellationRequested(); } })),
Copy link
Member

Choose a reason for hiding this comment

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

This cancellation step is capturing the CancellationTokenSource from the outer context and using it to both cancel and detect cancellation. Is there no way for a receiver to grab the CancellationToken passed into RunGeneratorsAndUpdateCompilation? If so then I would expect the ThrowIfCancellationRequested call to be on that token (make sure we thread it through).

If not how are syntax receivers expected to cancel?

Copy link
Contributor

Choose a reason for hiding this comment

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

SyntaxReceivers do not have access to any cancellation tokens. All cancellation exceptions thrown by a syntax receiver are errors.

Copy link
Member

Choose a reason for hiding this comment

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

I'm unsure what we are testing then here. If user code doesn't have access to the token then they can't exercise this code path. If our internal walk of the syntax throws based on the cancellation token passed to the generator then we shouldn't need a throw here.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll adjust it to just throw a new cancellation exception to indicate its not expected to be coming from the driver. Honestly I think the set up is mostly just a copy paste from the execute test.

#pragma warning disable CS0618 // ReportIfNonFatalAndCatchUnlessCanceled is obsolete; tracked by https://github.com/dotnet/roslyn/issues/58375
catch (Exception e) when (FatalError.ReportIfNonFatalAndCatchUnlessCanceled(e, token))
#pragma warning restore CS0618 // ReportIfNonFatalAndCatchUnlessCanceled is obsolete
catch (Exception e) when (!ExceptionUtilities.IsCurrentOperationBeingCancelled(e, token))
Copy link
Contributor

Choose a reason for hiding this comment

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

❔ Are we still going to get NFW reporting if/when these occur inside the IDE

Copy link
Member

Choose a reason for hiding this comment

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

As a separate change we're going to add an API to the GeneratorDriver to let us hook and report these, which is similar to how we do this for analyzers. Having this via an internal only API that's mixed in for fault reporting is a bit funky.

Copy link
Member

Choose a reason for hiding this comment

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

@chsienki Do we have a tracking bug to actually do that?

/// <param name="cancellationToken">Checked to see if the provided token was cancelled.</param>
/// <returns><see langword="true"/> if the exception was an <see cref="OperationCanceledException" /> and the token was canceled.</returns>
internal static bool IsCurrentOperationBeingCancelled(Exception exception, CancellationToken cancellationToken)
=> exception is OperationCanceledException oce && cancellationToken.IsCancellationRequested;
Copy link
Member

Choose a reason for hiding this comment

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

This could be true when exception and cancellationToken are unrelated instances. Is there a reason you aren't checking && oce.CancellationToken == cancellationToken here?

Effectively I would expect it to be written as such (do the prop check before type check as it's cheaper)

Suggested change
=> exception is OperationCanceledException oce && cancellationToken.IsCancellationRequested;
=>
cancellationToken.IsCancellationRequested &&
exception is OperationCanceledException oce &&
oce.CancellationToken == cancellationToken;

Copy link
Contributor

Choose a reason for hiding this comment

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

➡️ The omission of cancellation identity check is intentional. See #58843 (comment).

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
=> exception is OperationCanceledException oce && cancellationToken.IsCancellationRequested;
=> exception is OperationCanceledException && cancellationToken.IsCancellationRequested;

I think I prefer this better as then there would be no allocation of a new object (as a copy of exception casted to that exception type) needlessly just for this check (increasing performance).


var cts = new CancellationTokenSource();
var testGenerator = new CallbackGenerator(
onInit: (i) => i.RegisterForSyntaxNotifications(() => new TestSyntaxReceiver(tag: 0, callback: (a) => { if (a is AssignmentExpressionSyntax){ cts.Cancel(); cts.Token.ThrowIfCancellationRequested(); } })),
Copy link
Member

Choose a reason for hiding this comment

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

I'm unsure what we are testing then here. If user code doesn't have access to the token then they can't exercise this code path. If our internal walk of the syntax throws based on the cancellation token passed to the generator then we shouldn't need a throw here.

@sharwell sharwell dismissed their stale review January 27, 2022 02:11

Original cancellation detection behavior was restored

@chsienki chsienki enabled auto-merge (squash) January 27, 2022 20:51
auto-merge was automatically disabled January 27, 2022 21:18

Pull Request is not mergeable

@chsienki chsienki enabled auto-merge (squash) January 27, 2022 21:42
Comment on lines +1993 to +1995
Assert.Single(results.Results);
Assert.IsType<OperationCanceledException>(results.Results[0].Exception);
Assert.Equal("Simulated cancellation from external source", results.Results[0].Exception!.Message);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Assert.Single(results.Results);
Assert.IsType<OperationCanceledException>(results.Results[0].Exception);
Assert.Equal("Simulated cancellation from external source", results.Results[0].Exception!.Message);
var result = Assert.Single(results.Results);
Assert.IsType<OperationCanceledException>(result.Exception);
Assert.Equal("Simulated cancellation from external source", result.Exception!.Message);

#pragma warning disable CS0618 // ReportIfNonFatalAndCatchUnlessCanceled is obsolete; tracked by https://github.com/dotnet/roslyn/issues/58375
catch (Exception e) when (FatalError.ReportIfNonFatalAndCatchUnlessCanceled(e, token))
#pragma warning restore CS0618 // ReportIfNonFatalAndCatchUnlessCanceled is obsolete
catch (Exception e) when (!ExceptionUtilities.IsCurrentOperationBeingCancelled(e, token))
Copy link
Member

Choose a reason for hiding this comment

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

@chsienki Do we have a tracking bug to actually do that?

@chsienki chsienki merged commit 82ea47b into dotnet:main Jan 28, 2022
@ghost ghost modified the milestones: 17.2, Next Jan 28, 2022
@chsienki
Copy link
Member Author

@jasonmalinowski Filed #59141 to track

333fred added a commit to YairHalberstadt/roslyn that referenced this pull request Jan 28, 2022
…ess-instance-members

* upstream/main: (669 commits)
  Fix 'hasStaticConstructor' check in MethodCompiler (dotnet#59116)
  Update dependencies from https://github.com/dotnet/arcade build 20220127.8 (dotnet#59134)
  Update StreamJsonRpc (dotnet#59073)
  Resources
  Filter cancellation exceptions in generator driver (dotnet#58843)
  Revert "Remove dependency on EditorFeatures from Remote.ServiceHub project (dotnet#59059)"
  Strings
  Inline
  Convert to switch expression
  Explicitly test empty string case.
  Fix comment
  Simplify test code
  Run all
  Add tests
  Delete test generator
  Add support for specifying server in tests
  Remove options
  review feedback
  Format document after each provider (dotnet#59091)
  [main] Update dependencies from dotnet/arcade (dotnet#59015)
  ...
@RikkiGibson RikkiGibson modified the milestones: Next, 17.2.P1 Feb 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UserFunctionException --> OperationCanceledException in SourceGeneration/Nodes/SyntaxReceiverInputNode.cs:line 81

7 participants