Skip to content

Don't throw on cancellation in chained command handlers#52514

Merged
allisonchou merged 4 commits intodotnet:mainfrom
allisonchou:DontThrowCancellationInCommandHandlers
Apr 9, 2021
Merged

Don't throw on cancellation in chained command handlers#52514
allisonchou merged 4 commits intodotnet:mainfrom
allisonchou:DontThrowCancellationInCommandHandlers

Conversation

@allisonchou
Copy link
Contributor

According to these Editor API guidelines for command handlers, we should return early when cancellation is requested instead of throwing an exception, specifically in handlers that may produce side effects upon cancellation.
For example, in Roslyn's format handler, nextHandler() is called which triggers the character insertion into the buffer. If cancellation is thrown later on in the format handler, these changes either need to reverted or we need to return early, otherwise the character might be inserted into the buffer twice.

I think I addressed most command handlers that were doing the wrong thing, but let me know if I missed any.

Fixes #52382.

@allisonchou allisonchou requested a review from sharwell April 8, 2021 21:50
@allisonchou allisonchou requested a review from a team as a code owner April 8, 2021 21:50
@CyrusNajmabadi
Copy link
Contributor

This is super weird :-/

This is the part that feels very underspecified:

In other words, the command handler service host must be able to recover to a known consistent state after cancelling your work,

What defines a 'known consistent state'. It sounds like it's saying: if you bubble cancellation up, you can't have mutated anything on the UI at all.

If that's a requirement, i'd say we need to go further here and we must make all handlers go through a path that simply does not allow cancellation to ever bleed out (as 'no mutation on cancellation' has never been a metaphor we've supported for handlers).

@CyrusNajmabadi
Copy link
Contributor

CyrusNajmabadi commented Apr 8, 2021

Tagging @jasonmalinowski

The weird thign here is that editor is both cancelling, but also then deciding taht after cancelling that they want to do something. That feels weird ot me as i would expect that once a user cancels something, nothing else happens at that point. If they do feel that things should rollback, i would recommend that they themslves start a transaction and they rollback to the starting point so that everything returns back and there doesn't need to be this handshake between us :(

@allisonchou
Copy link
Contributor Author

If that's a requirement, i'd say we need to go further here and we must make all handlers go through a path that simply does not allow cancellation to ever bleed out (as 'no mutation on cancellation' has never been a metaphor we've supported for handlers).

Most handlers already do the right thing today, there's just a couple that have fallen through the cracks. Furthermore, after addressing Sam's feedback, it seems like we can't just take a "never let cancellation bleed out" approach, as different handlers will need to catch the exception at different locations.

If they do feel that things should rollback, i would recommend that they themslves start a transaction and they rollback to the starting point so that everything returns back and there doesn't need to be this handshake between us :(

I agree that this would be best for long-term, however I feel like this would require significant rework/design on Editor side for how they do things. For now, I feel like this is at least a short-term solution that mitigates the problem.
@olegtk Do you have a perspective on this? Would Editor ever consider doing the rollback work to the starting point for command handlers instead of leaving the work up to individual languages?

@allisonchou allisonchou requested a review from sharwell April 9, 2021 00:04
Copy link
Member

@Youssef1313 Youssef1313 left a comment

Choose a reason for hiding this comment

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

Does CompleteStatementCommandHandler (and potentially other command handlers) need a similar change?

@allisonchou
Copy link
Contributor Author

Does CompleteStatementCommandHandler (and potentially other command handlers) need a similar change?

Hmm, I don't think so. The main issue in the problematic handlers was that nextCommandHandler() would be called, and that call could modify the text buffer, and then after that call we could throw cancellation in the handler, which violates the Editor API guidelines and could mess up the current state of things. CompleteStatementCommandHandler doesn't do any work that may trigger cancellation after nextCommandHandler() is called, so I think it's safe.

@allisonchou
Copy link
Contributor Author

@CyrusNajmabadi Do you have any further concerns with this PR? I agree that the work to potentially rollback changes would ideally be done on the Editor side, however that seems like it would take quite a bit of planning and work on their end. At least for now, I feel like this PR is a good mitigation to the issues being observed.

@CyrusNajmabadi
Copy link
Contributor

Effectively it feels fragile. let me review again.

@allisonchou
Copy link
Contributor Author

Talked with Cyrus/Sam offline - seems like this is OK to be merged as a temporary solution, however in the near future we'd like to make cancellation handling in command handlers more robust so that each handler does not need to deal with cancellation individually. This is tracked by #52528.

@allisonchou allisonchou merged commit 45f1b4a into dotnet:main Apr 9, 2021
@ghost ghost added this to the Next milestone Apr 9, 2021
@allisonchou allisonchou deleted the DontThrowCancellationInCommandHandlers branch April 9, 2021 20:23
@dibarbet dibarbet modified the milestones: Next, 16.10.P3 Apr 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

When cancellation is requested in formatter, double semicolons are inserted

5 participants