-
Notifications
You must be signed in to change notification settings - Fork 419
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
async-await: Move to completed state before cancelling task during finish() #1302
async-await: Move to completed state before cancelling task during finish() #1302
Conversation
…nish() Signed-off-by: Si Beaumont <beaumont@apple.com>
/// If we are in the completed state then the async writer delegate will have been cancelled, | ||
/// however the cancellation is asynchronous so there's a chance that we receive this callback | ||
/// after that has happened. We can drop the response. | ||
() |
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.
This should fix #1301
@@ -327,6 +327,7 @@ internal final class AsyncServerHandler< | |||
self.state = .completed | |||
|
|||
case .active: | |||
self.state = .completed |
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.
This should fix #1299
@glbrntt what do we want to do about the // AsyncWriter.swift
deinit {
switch self._completionState {
case .completed:
()
case .incomplete, .pending:
assertionFailure("writer has not completed is pending completion")
}
} This will currently cause an diff --git a/Tests/GRPCTests/AsyncAwaitSupport/AsyncIntegrationTests.swift b/Tests/GRPCTests/AsyncAwaitSupport/AsyncIntegrationTests.swift
index bc5f7eb4..298055cb 100644
--- a/Tests/GRPCTests/AsyncAwaitSupport/AsyncIntegrationTests.swift
+++ b/Tests/GRPCTests/AsyncAwaitSupport/AsyncIntegrationTests.swift
@@ -197,2 +197,11 @@ final class AsyncIntegrationTests: GRPCTestCase {
}
+
+ func testServerCloseAfterMessage() {
+ XCTAsyncTest {
+ let update = self.echo.makeUpdateCall()
+ try await update.requestStream.send(.with { $0.text = "hello" })
+ _ = try await update.responses.first(where: { _ in true })
+ // server is closed in tearDown.
+ }
+ }
} |
Is it possible to re-jig the tests a little to explicitly close the server within the test? That way we can |
Signed-off-by: Si Beaumont <beaumont@apple.com>
Signed-off-by: Si Beaumont <beaumont@apple.com>
As reported in #1299 and #1301, there are some scenarios where the closing the channel or using the response stream writer after the channel has been closed will lead to a crash. Specifically, when `finish()` is called the state was not progressed to `.completed` before cancelling the task. This was to maintain parity with the ELG-based API where the status and the trailers were still sent after `finish()` is called. We now believe this to be misguided and we shouldn't expect to be able to send anything on the channel at this point because we are tearing the handler and the channel down. This changes `finish()` to move to the `.completed` state before cancelling the `userHandlerTask`. As a result, when the completion handler for the user function fires, it will call `handleError(_:)` with `CancellationError` (as before) but now the error handler will not attempt to send the status or trailers back via the interceptors because the state will be in `.completed`. Tests for receiving an error after headers and after a message have been added.
As reported in #1299 and #1301, there are some scenarios where the closing the channel or using the response stream writer after the channel has been closed will lead to a crash.
Specifically, when
finish()
is called the state was not progressed to.completed
before cancelling the task. This was to maintain parity with the ELG-based API where the status and the trailers were still sent afterfinish()
is called. We now believe this to be misguided and we shouldn't expect to be able to send anything on the channel at this point because we are tearing the handler and the channel down.This changes
finish()
to move to the.completed
state before cancelling theuserHandlerTask
. As a result, when the completion handler for the user function fires, it will callhandleError(_:)
withCancellationError
(as before) but now the error handler will not attempt to send the status or trailers back via the interceptors because the state will be in.completed
.Tests for receiving an error after headers and after a message have been added.