Browse Source

Improve Sendable checking for server code (#1605)

Motivation:

The server handler protocol must be Sendable so that any methods on the
handler are also Sendable as annotating methods as `@Sendable` does not
work as expected. It follows from this that generated server interceptor
factories must also be Sendable (they already are for clients). This
puts the `ServerInterceptor` class in an awkward position: it must be
Sendable but is not inherently thread-safe (these restrictions are
documented and existed before Sendable checking was introduced to
Swift). The `ClientInterceptor` has the same restrictions and is
`@unchecked Sendable` so we elect to do the same for the
`ServerInterceptor`.

Modifications:

- Make generated server handlers and server interceptor factories Sendable
- Make `ServerInterceptor` `@unchecked Sendable`
- Regenerate
- Fix some warnings in test code

Result:

Better Sendable checking
George Barnett 2 years ago
parent
commit
2e4184e82d

+ 7 - 8
Sources/protoc-gen-grpc-swift/Generator-Server+AsyncAwait.swift

@@ -29,7 +29,7 @@ extension Generator {
     self.println("/// To implement a server, implement an object which conforms to this protocol.")
     self.println("/// To implement a server, implement an object which conforms to this protocol.")
     self.printAvailabilityForAsyncAwait()
     self.printAvailabilityForAsyncAwait()
     self.withIndentation(
     self.withIndentation(
-      "\(self.access) protocol \(self.asyncProviderName): CallHandlerProvider",
+      "\(self.access) protocol \(self.asyncProviderName): CallHandlerProvider, Sendable",
       braces: .curly
       braces: .curly
     ) {
     ) {
       self.println("static var serviceDescriptor: GRPCServiceDescriptor { get }")
       self.println("static var serviceDescriptor: GRPCServiceDescriptor { get }")
@@ -86,7 +86,7 @@ extension Generator {
       name: self.methodFunctionName,
       name: self.methodFunctionName,
       arguments: arguments,
       arguments: arguments,
       returnType: returnType,
       returnType: returnType,
-      sendable: true,
+      sendable: false,
       async: true,
       async: true,
       throws: true,
       throws: true,
       bodyBuilder: nil
       bodyBuilder: nil
@@ -152,20 +152,19 @@ extension Generator {
               self.println("requestDeserializer: \(Types.deserializer(for: requestType))(),")
               self.println("requestDeserializer: \(Types.deserializer(for: requestType))(),")
               self.println("responseSerializer: \(Types.serializer(for: responseType))(),")
               self.println("responseSerializer: \(Types.serializer(for: responseType))(),")
               self.println("interceptors: self.interceptors?.\(interceptorFactory)() ?? [],")
               self.println("interceptors: self.interceptors?.\(interceptorFactory)() ?? [],")
+              let prefix = "wrapping: { try await self.\(functionName)"
               switch streamingType(self.method) {
               switch streamingType(self.method) {
               case .unary:
               case .unary:
-                self.println("wrapping: self.\(functionName)(request:context:)")
+                self.println("\(prefix)(request: $0, context: $1) }")
 
 
               case .clientStreaming:
               case .clientStreaming:
-                self.println("wrapping: self.\(functionName)(requestStream:context:)")
+                self.println("\(prefix)(requestStream: $0, context: $1) }")
 
 
               case .serverStreaming:
               case .serverStreaming:
-                self.println("wrapping: self.\(functionName)(request:responseStream:context:)")
+                self.println("\(prefix)(request: $0, responseStream: $1, context: $2) }")
 
 
               case .bidirectionalStreaming:
               case .bidirectionalStreaming:
-                self.println(
-                  "wrapping: self.\(functionName)(requestStream:responseStream:context:)"
-                )
+                self.println("\(prefix)(requestStream: $0, responseStream: $1, context: $2) }")
               }
               }
             }
             }
           }
           }

+ 1 - 1
Sources/protoc-gen-grpc-swift/Generator-Server.swift

@@ -148,7 +148,7 @@ extension Generator {
   }
   }
 
 
   private func printServerInterceptorFactoryProtocol() {
   private func printServerInterceptorFactoryProtocol() {
-    self.println("\(self.access) protocol \(self.serverInterceptorProtocolName) {")
+    self.println("\(self.access) protocol \(self.serverInterceptorProtocolName): Sendable {")
     self.withIndentation {
     self.withIndentation {
       // Method specific interceptors.
       // Method specific interceptors.
       for method in service.methods {
       for method in service.methods {