Skip to content
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

Fix a handful of Sendable warnings #1419

Merged

Conversation

glbrntt
Copy link
Collaborator

@glbrntt glbrntt commented May 30, 2022

Motivation:

We require all request/response types to be Sendable as they always cross a
thread boundary. Our various async sequences and async writers are conditionally
Sendable if their Element is Sendable, however, in practice they always will
be. We require them to be Sendable as they are arguments to async functions
(see SE-0338). For the same reason, the async server context (and by extension
the async server handler) must also be Sendable.

Modifications:

  • Require passthrough message/source sequence to have Sendable elements so
    that they are unconditionally Sendable
  • Make the async server context and handler Sendable
  • Make the async writer error Sendable
  • Remove a global variable from protoc-gen-grpc-swift
  • Add appropriate conditions to extensions on Call which define functions
    requiring Call to be Sendable

Result:

Better Sendable support.

Motivation:

We require all request/response types to be `Sendable` as they always cross a
thread boundary. Our various async sequences and async writers are conditionally
Sendable if their `Element` is `Sendable`, however, in practice they always will
be. We require them to be `Sendable` as they are arguments to `async` functions
(see SE-0338). For the same reason, the async server context (and by extension
the async server handler) must also be `Sendable`.

Modifications:

- Require passthrough message/source sequence to have `Sendable` elements so
  that they are unconditionally `Sendable`
- Make the async server context and handler `Sendable`
- Make the async writer error Sendable
- Remove a global variable from protoc-gen-grpc-swift
- Add appropriate conditions to extensions on `Call` which define functions
  requiring `Call` to be `Sendable`

Result:

Better `Sendable` support.
@glbrntt glbrntt requested a review from fabianfett May 30, 2022 14:54
@@ -296,11 +296,11 @@ extension AsyncWriter where End == Void {
}
}

public struct GRPCAsyncWriterError: Error, Hashable {
public struct GRPCAsyncWriterError: Error, Sendable, Hashable {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Sendability is already required from Error conformance.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Collaborator

@fabianfett fabianfett left a comment

Choose a reason for hiding this comment

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

I still think we don't need to require Request and Response to be Sendable.

@@ -18,7 +18,7 @@
/// This is currently a wrapper around AsyncThrowingStream because we want to be
/// able to swap out the implementation for something else in the future.
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public struct GRPCAsyncResponseStream<Element>: AsyncSequence {
public struct GRPCAsyncResponseStream<Element: Sendable>: AsyncSequence {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think, we should enforce this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we have to. The underlying elements are produced from a NIO thread, so they are necessarily Sendable. The type is useless without that.

@@ -16,7 +16,7 @@
#if compiler(>=5.6)

@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Call {
extension Call where Request: Sendable, Response: Sendable {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think, we should enforce this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Conditional extensions aren't enforcements - if Response and a Request don't conform then neither will this

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As GRPCAsyncRequestStreamWriter expects Request to be Sendable we require it be here. We also require Response to be Sendable to that Call is Sendable which we require to capture it in the Sendable closures for the delegate.

@@ -798,7 +803,7 @@ protocol AsyncServerCallContextProvider {

@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@usableFromInline
internal struct ServerHandlerComponents<Request, Delegate: AsyncWriterDelegate> {
internal struct ServerHandlerComponents<Request: Sendable, Delegate: AsyncWriterDelegate> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think, we should enforce this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We need this one to enforce it for the PassthroughMessageSource

@@ -18,7 +18,7 @@
/// An ``AsyncSequence`` adapter for a ``PassthroughMessageSource``.`
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@usableFromInline
internal struct PassthroughMessageSequence<Element, Failure: Error>: AsyncSequence {
internal struct PassthroughMessageSequence<Element: Sendable, Failure: Error>: AsyncSequence {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think, we should enforce this.

@@ -29,7 +29,7 @@ import NIOCore
/// indicate that the sequence should end with an error.
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@usableFromInline
internal final class PassthroughMessageSource<Element, Failure: Error> {
internal final class PassthroughMessageSource<Element: Sendable, Failure: Error> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think, we should enforce this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm curious as to why not. A non-Sendable AsyncSequence is next to useless: you can only send to it and receive from it on the same Task. When is anyone ever going to do that?

Copy link
Collaborator Author

@glbrntt glbrntt left a comment

Choose a reason for hiding this comment

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

We talked a little offline about whether or not the various AsyncSequences should be required to be Sendable here and whether Request and Response are required to be Sendable.

The summary is that Request and Response must always be Sendable: they are always transferred across a thread boundary (event loop to concurrency context). For async sequences in some cases it would be useful to be able to move the ownership of the AsyncSequence to another concurrency context (in particular moving it from the event-loop context to a concurrency context). However we can't currently represent that and so we must make them Sendable.

@@ -16,7 +16,7 @@
#if compiler(>=5.6)

@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Call {
extension Call where Request: Sendable, Response: Sendable {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As GRPCAsyncRequestStreamWriter expects Request to be Sendable we require it be here. We also require Response to be Sendable to that Call is Sendable which we require to capture it in the Sendable closures for the delegate.

@@ -798,7 +803,7 @@ protocol AsyncServerCallContextProvider {

@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@usableFromInline
internal struct ServerHandlerComponents<Request, Delegate: AsyncWriterDelegate> {
internal struct ServerHandlerComponents<Request: Sendable, Delegate: AsyncWriterDelegate> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We need this one to enforce it for the PassthroughMessageSource

@glbrntt glbrntt requested a review from fabianfett May 31, 2022 12:13
Copy link
Collaborator

@fabianfett fabianfett left a comment

Choose a reason for hiding this comment

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

Thanks for the discussion. I like all the changes here, but wish Swift would have gone the send and sync way.

@glbrntt glbrntt merged commit 3426bca into grpc:1.7.1-async-await Jun 1, 2022
@glbrntt glbrntt deleted the gb-more-sendable-conformance branch June 1, 2022 14:41
@glbrntt glbrntt added the 🔨 semver/patch No public API change. label Jun 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔨 semver/patch No public API change.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants