-
Notifications
You must be signed in to change notification settings - Fork 146
Define commands as asynchronous and use Task for preview cancellation #1049
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
How much do we care about source compatibility for the command line actions? I find that I need to write literal anti-patterns to make it compatible and I don't know if there's an expectation that that code should be used by anyone outside of this package. Should we stop surfacing it as a "product"? |
I made this new commit to stop surfacing the command line actions as a library product. This would be source breaking since consumers of this repo no longer can import it. |
6861333
to
0e00de0
Compare
I moved that change to #1052 instead. |
0e00de0
to
a5038c3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great.
Are there any implications for switching to a async functions throughout the CLI and top level action structs?
Is there a way we can refactor the code in DocumentationContext.register to use async notation to manage the various discovery tasks, instead of using DispatchGroup
and DispatchQueue
?
|
||
// If cancelled, return early before we emit diagnostics. | ||
func isConversionCancelled() -> Bool { | ||
Task.isCancelled || isCancelled?.sync({ $0 }) == true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need isCancelled
? I don't see code that sets it anywhere. Is calling Task.isCanclled
sufficient?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DocumentationConverter
is public API it's possible that a caller is passing a shared isCancelled
during initialization and setting it to true
to cancel the conversion.
@@ -303,11 +268,9 @@ public struct ConvertAction: Action, RecreatingContext { | |||
let totalTimeMetric = benchmark(begin: Benchmark.Duration(id: "convert-total-time")) | |||
|
|||
// While running this method keep the `isPerforming` flag up. | |||
isPerforming.sync({ $0 = true }) | |||
willPerformFuture?() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we ever use willPerformFuture
? Or didPerformFuture
? Can we remove them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only in tests. PreviewActionIntegrationTests.testCancelsConversion()
sets a willPerformFuture
that sleeps to artificially slow down the conversation time to a controllable number to give the test time to modify a monitored file to trigger another conversion which cancels the previous one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only willPerformFuture
was ever used so I renamed it to _extraTestWork
to better indicate what it is and removed didPerformFuture
.
Not really since they're top-level. The implication of switching to asynchronous functions is that they can only be called from other asynchronous functions (or main). This implication can be seen in all the tests that needed to add This problem is illustrated in the first half of What Color is Your Function?. Since asynchronous functions are able to call both asynchronous and synchronous functions but synchronous functions are only able call other synchronous functions, asynchronous functions spread outwards. Because of this, it's sometimes recommended to make updates like this from the outside it.
Yes, but it's out of scope for this PR. Using asynchronous functions in In the fourth PR in this series—or possibly fifth PR depending on how I split up the changes—I plan on deprecating both those callers and introducing a new context initializer which will be asynchronous even though it only calls synchronous code. This will open up to a future where we can make |
@swift-ci please test |
@swift-ci please test |
I noticed that the updated tests were flaky because of their use of While I was investigating that I also found and fixed a couple of memory leaks. |
@swift-ci please test |
This sounds great - looking forward to seeing that! |
Bug/issue #, if applicable:
Summary
This is the third of many changes to redefine how documentation contexts are created.
This redefined the various commands to be asynchronous so that preview action cancellation, documentation converter cancellation, and context registration cancellation can use
Task.checkCancellation()
instead of sharing a Boolean wrapped in a class and polling for cancellation on a timer.This change also supports cancelling context registration during initialization, which enables us to move away from deferred context registration in a follow up PR.
Dependencies
This builds on top of #1052
Testing
Nothing in particular. This isn't a user-facing change.
Checklist
Make sure you check off the following items. If they cannot be completed, provide a reason.
AddedUpdated tests./bin/test
script and it succeeded[ ] Updated documentation if necessary