Skip to content

Commit cd019f6

Browse files
authored
Enable Swift 6 language mode in GRPCCore (grpc#2046)
Motivation: v2 should use Swift 6 language mode to take full advantage of the compilers data race checking. Modifications: - Enable Swift 6 language more for the code module - Add a bunch of missing explicit 'Sendable's Result: - Compiles with Swift 6 lang mode
1 parent 40f316e commit cd019f6

20 files changed

+206
-100
lines changed

[email protected]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ extension Target {
207207
],
208208
path: "Sources/GRPCCore",
209209
swiftSettings: [
210-
.swiftLanguageMode(.v5),
210+
.swiftLanguageMode(.v6),
211211
.enableUpcomingFeature("ExistentialAny"),
212212
.enableUpcomingFeature("InternalImportsByDefault")
213213
]

Sources/GRPCCore/Call/Client/Internal/ClientRPCExecutor+HedgingExecutor.swift

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,11 @@ extension ClientRPCExecutor {
2121
@usableFromInline
2222
struct HedgingExecutor<
2323
Transport: ClientTransport,
24+
Input: Sendable,
25+
Output: Sendable,
2426
Serializer: MessageSerializer,
2527
Deserializer: MessageDeserializer
26-
> {
27-
@usableFromInline
28-
typealias Input = Serializer.Message
29-
@usableFromInline
30-
typealias Output = Deserializer.Message
31-
28+
>: Sendable where Serializer.Message == Input, Deserializer.Message == Output {
3229
@usableFromInline
3330
let transport: Transport
3431
@usableFromInline
@@ -181,14 +178,14 @@ extension ClientRPCExecutor.HedgingExecutor {
181178
let state = SharedState(policy: self.policy)
182179

183180
// There's always a first attempt, safe to '!'.
184-
let (attempt, scheduleNext) = state.withState({ $0.nextAttemptNumber() })!
181+
let result = state.withState { $0.nextAttemptNumber()! }
185182

186183
group.addTask {
187184
let result = await self._startAttempt(
188185
request: request,
189186
method: method,
190187
options: options,
191-
attempt: attempt,
188+
attempt: result.nextAttempt,
192189
state: state,
193190
picker: picker,
194191
responseHandler: responseHandler
@@ -199,7 +196,7 @@ extension ClientRPCExecutor.HedgingExecutor {
199196

200197
// Schedule the second attempt.
201198
var nextScheduledAttempt = ScheduledState()
202-
if scheduleNext {
199+
if result.scheduleNext {
203200
nextScheduledAttempt.schedule(in: &group, pushback: false, delay: self.policy.hedgingDelay)
204201
}
205202

@@ -212,13 +209,13 @@ extension ClientRPCExecutor.HedgingExecutor {
212209
switch outcome {
213210
case .ran:
214211
// Start a new attempt and possibly schedule the next.
215-
if let (attempt, scheduleNext) = state.withState({ $0.nextAttemptNumber() }) {
212+
if let result = state.withState({ $0.nextAttemptNumber() }) {
216213
group.addTask {
217214
let result = await self._startAttempt(
218215
request: request,
219216
method: method,
220217
options: options,
221-
attempt: attempt,
218+
attempt: result.nextAttempt,
222219
state: state,
223220
picker: picker,
224221
responseHandler: responseHandler
@@ -227,7 +224,7 @@ extension ClientRPCExecutor.HedgingExecutor {
227224
}
228225

229226
// Schedule the next attempt.
230-
if scheduleNext {
227+
if result.scheduleNext {
231228
nextScheduledAttempt.schedule(
232229
in: &group,
233230
pushback: false,
@@ -265,13 +262,13 @@ extension ClientRPCExecutor.HedgingExecutor {
265262

266263
nextScheduledAttempt.cancel()
267264

268-
if let (attempt, scheduleNext) = state.withState({ $0.nextAttemptNumber() }) {
265+
if let result = state.withState({ $0.nextAttemptNumber() }) {
269266
group.addTask {
270267
let result = await self._startAttempt(
271268
request: request,
272269
method: method,
273270
options: options,
274-
attempt: attempt,
271+
attempt: result.nextAttempt,
275272
state: state,
276273
picker: picker,
277274
responseHandler: responseHandler
@@ -280,7 +277,7 @@ extension ClientRPCExecutor.HedgingExecutor {
280277
}
281278

282279
// Schedule the next retry.
283-
if scheduleNext {
280+
if result.scheduleNext {
284281
nextScheduledAttempt.schedule(
285282
in: &group,
286283
pushback: true,
@@ -314,7 +311,7 @@ extension ClientRPCExecutor.HedgingExecutor {
314311
}
315312

316313
@inlinable
317-
func _startAttempt<R>(
314+
func _startAttempt<R: Sendable>(
318315
request: ClientRequest.Stream<Input>,
319316
method: MethodDescriptor,
320317
options: CallOptions,
@@ -431,7 +428,7 @@ extension ClientRPCExecutor.HedgingExecutor {
431428
}
432429

433430
@usableFromInline
434-
final class SharedState {
431+
final class SharedState: Sendable {
435432
@usableFromInline
436433
let state: Mutex<State>
437434

@@ -441,15 +438,15 @@ extension ClientRPCExecutor.HedgingExecutor {
441438
}
442439

443440
@inlinable
444-
func withState<ReturnType>(_ body: @Sendable (inout State) -> ReturnType) -> ReturnType {
441+
func withState<ReturnType: Sendable>(_ body: (inout State) -> ReturnType) -> ReturnType {
445442
self.state.withLock {
446443
body(&$0)
447444
}
448445
}
449446
}
450447

451448
@usableFromInline
452-
struct State {
449+
struct State: Sendable {
453450
@usableFromInline
454451
let _maximumAttempts: Int
455452
@usableFromInline
@@ -474,14 +471,31 @@ extension ClientRPCExecutor.HedgingExecutor {
474471
}
475472
}
476473

474+
@usableFromInline
475+
struct NextAttemptResult: Sendable {
476+
@usableFromInline
477+
var nextAttempt: Int
478+
@usableFromInline
479+
var scheduleNext: Bool
480+
481+
@inlinable
482+
init(nextAttempt: Int, scheduleNext: Bool) {
483+
self.nextAttempt = nextAttempt
484+
self.scheduleNext = scheduleNext
485+
}
486+
}
487+
477488
@inlinable
478-
mutating func nextAttemptNumber() -> (Int, Bool)? {
489+
mutating func nextAttemptNumber() -> NextAttemptResult? {
479490
if self.hasUsableResponse || self.attempt > self._maximumAttempts {
480491
return nil
481492
} else {
482493
let attempt = self.attempt
483494
self.attempt += 1
484-
return (attempt, self.attempt <= self._maximumAttempts)
495+
return NextAttemptResult(
496+
nextAttempt: attempt,
497+
scheduleNext: self.attempt <= self._maximumAttempts
498+
)
485499
}
486500
}
487501
}
@@ -533,28 +547,28 @@ extension ClientRPCExecutor.HedgingExecutor {
533547

534548
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
535549
@usableFromInline
536-
enum _HedgingTaskResult<R> {
550+
enum _HedgingTaskResult<R: Sendable>: Sendable {
537551
case rpcHandled(Result<R, any Error>)
538552
case finishedRequest(Result<Void, any Error>)
539553
case timedOut(Result<Void, any Error>)
540554
}
541555

542556
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
543557
@usableFromInline
544-
enum _HedgingAttemptTaskResult<R, Output> {
558+
enum _HedgingAttemptTaskResult<R: Sendable, Output: Sendable>: Sendable {
545559
case attemptPicked(Bool)
546560
case attemptCompleted(AttemptResult)
547561
case scheduledAttemptFired(ScheduleEvent)
548562

549563
@usableFromInline
550-
enum AttemptResult {
564+
enum AttemptResult: Sendable {
551565
case unusableResponse(ClientResponse.Stream<Output>, Metadata.RetryPushback?)
552566
case usableResponse(Result<R, any Error>)
553567
case noStreamAvailable(any Error)
554568
}
555569

556570
@usableFromInline
557-
enum ScheduleEvent {
571+
enum ScheduleEvent: Sendable {
558572
case ran
559573
case cancelled
560574
}

Sources/GRPCCore/Call/Client/Internal/ClientRPCExecutor+OneShotExecutor.swift

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,11 @@ extension ClientRPCExecutor {
2121
@usableFromInline
2222
struct OneShotExecutor<
2323
Transport: ClientTransport,
24+
Input: Sendable,
25+
Output: Sendable,
2426
Serializer: MessageSerializer,
2527
Deserializer: MessageDeserializer
26-
> {
27-
@usableFromInline
28-
typealias Input = Serializer.Message
29-
@usableFromInline
30-
typealias Output = Deserializer.Message
31-
28+
>: Sendable where Serializer.Message == Input, Deserializer.Message == Output {
3229
@usableFromInline
3330
let transport: Transport
3431
@usableFromInline
@@ -60,7 +57,7 @@ extension ClientRPCExecutor {
6057
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
6158
extension ClientRPCExecutor.OneShotExecutor {
6259
@inlinable
63-
func execute<R>(
60+
func execute<R: Sendable>(
6461
request: ClientRequest.Stream<Input>,
6562
method: MethodDescriptor,
6663
options: CallOptions,
@@ -71,9 +68,10 @@ extension ClientRPCExecutor.OneShotExecutor {
7168
if let deadline = self.deadline {
7269
var request = request
7370
request.metadata.timeout = ContinuousClock.now.duration(to: deadline)
71+
let immutableRequest = request
7472
result = await withDeadline(deadline) {
7573
await self._execute(
76-
request: request,
74+
request: immutableRequest,
7775
method: method,
7876
options: options,
7977
responseHandler: responseHandler
@@ -95,7 +93,7 @@ extension ClientRPCExecutor.OneShotExecutor {
9593
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
9694
extension ClientRPCExecutor.OneShotExecutor {
9795
@inlinable
98-
func _execute<R>(
96+
func _execute<R: Sendable>(
9997
request: ClientRequest.Stream<Input>,
10098
method: MethodDescriptor,
10199
options: CallOptions,
@@ -133,9 +131,9 @@ extension ClientRPCExecutor.OneShotExecutor {
133131

134132
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
135133
@inlinable
136-
func withDeadline<Result>(
134+
func withDeadline<Result: Sendable>(
137135
_ deadline: ContinuousClock.Instant,
138-
execute: @escaping () async -> Result
136+
execute: @Sendable @escaping () async -> Result
139137
) async -> Result {
140138
return await withTaskGroup(of: _DeadlineChildTaskResult<Result>.self) { group in
141139
group.addTask {
@@ -173,7 +171,7 @@ func withDeadline<Result>(
173171
}
174172

175173
@usableFromInline
176-
enum _DeadlineChildTaskResult<Value> {
174+
enum _DeadlineChildTaskResult<Value: Sendable>: Sendable {
177175
case deadlinePassed
178176
case timeoutCancelled
179177
case taskCompleted(Value)

Sources/GRPCCore/Call/Client/Internal/ClientRPCExecutor+RetryExecutor.swift

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,11 @@ extension ClientRPCExecutor {
1919
@usableFromInline
2020
struct RetryExecutor<
2121
Transport: ClientTransport,
22+
Input: Sendable,
23+
Output: Sendable,
2224
Serializer: MessageSerializer,
2325
Deserializer: MessageDeserializer
24-
> {
25-
@usableFromInline
26-
typealias Input = Serializer.Message
27-
@usableFromInline
28-
typealias Output = Deserializer.Message
29-
26+
>: Sendable where Serializer.Message == Input, Deserializer.Message == Output {
3027
@usableFromInline
3128
let transport: Transport
3229
@usableFromInline
@@ -198,7 +195,7 @@ extension ClientRPCExecutor.RetryExecutor {
198195
}
199196

200197
@inlinable
201-
func executeAttempt<R>(
198+
func executeAttempt<R: Sendable>(
202199
stream: RPCStream<ClientTransport.Inbound, ClientTransport.Outbound>,
203200
metadata: Metadata,
204201
retryStream: BroadcastAsyncSequence<Input>,
@@ -307,7 +304,7 @@ extension ClientRPCExecutor.RetryExecutor {
307304

308305
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
309306
@usableFromInline
310-
enum _RetryExecutorTask<R> {
307+
enum _RetryExecutorTask<R: Sendable>: Sendable {
311308
case timedOut(Result<Void, any Error>)
312309
case handledResponse(Result<R, any Error>)
313310
case retry(Duration?)

Sources/GRPCCore/Call/Client/Internal/ClientStreamExecutor.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ internal enum ClientStreamExecutor {
5555
}
5656

5757
let bodyParts = RawBodyPartToMessageSequence(
58-
base: AsyncIteratorSequence(iterator.wrappedValue),
58+
base: UncheckedAsyncIteratorSequence(iterator.wrappedValue),
5959
deserializer: deserializer
6060
)
6161

@@ -168,7 +168,7 @@ internal enum ClientStreamExecutor {
168168
Message: Sendable,
169169
Deserializer: MessageDeserializer<Message>,
170170
Failure: Error
171-
>: AsyncSequence {
171+
>: AsyncSequence, Sendable where Base: Sendable {
172172
@usableFromInline
173173
typealias Element = AsyncIterator.Element
174174

Sources/GRPCCore/Call/Server/Internal/ServerRPCExecutor.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ struct ServerRPCExecutor {
168168
ServerRequest.Stream<Input>
169169
) async throws -> ServerResponse.Stream<Output>
170170
) async {
171-
let messages = AsyncIteratorSequence(inbound.wrappedValue).map { part throws -> Input in
171+
let messages = UncheckedAsyncIteratorSequence(inbound.wrappedValue).map { part in
172172
switch part {
173173
case .message(let bytes):
174174
return try deserializer.deserialize(bytes)
@@ -284,7 +284,7 @@ struct ServerRPCExecutor {
284284
}
285285

286286
@usableFromInline
287-
enum ServerExecutorTask {
287+
enum ServerExecutorTask: Sendable {
288288
case timedOut(Result<Void, any Error>)
289289
case executed
290290
}

Sources/GRPCCore/GRPCClient.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ public final class GRPCClient: Sendable {
256256
/// - handler: A unary response handler.
257257
///
258258
/// - Returns: The return value from the `handler`.
259-
public func unary<Request, Response, ReturnValue>(
259+
public func unary<Request, Response, ReturnValue: Sendable>(
260260
request: ClientRequest.Single<Request>,
261261
descriptor: MethodDescriptor,
262262
serializer: some MessageSerializer<Request>,
@@ -287,7 +287,7 @@ public final class GRPCClient: Sendable {
287287
/// - handler: A unary response handler.
288288
///
289289
/// - Returns: The return value from the `handler`.
290-
public func clientStreaming<Request, Response, ReturnValue>(
290+
public func clientStreaming<Request, Response, ReturnValue: Sendable>(
291291
request: ClientRequest.Stream<Request>,
292292
descriptor: MethodDescriptor,
293293
serializer: some MessageSerializer<Request>,
@@ -318,7 +318,7 @@ public final class GRPCClient: Sendable {
318318
/// - handler: A response stream handler.
319319
///
320320
/// - Returns: The return value from the `handler`.
321-
public func serverStreaming<Request, Response, ReturnValue>(
321+
public func serverStreaming<Request, Response, ReturnValue: Sendable>(
322322
request: ClientRequest.Single<Request>,
323323
descriptor: MethodDescriptor,
324324
serializer: some MessageSerializer<Request>,
@@ -350,7 +350,7 @@ public final class GRPCClient: Sendable {
350350
/// - handler: A response stream handler.
351351
///
352352
/// - Returns: The return value from the `handler`.
353-
public func bidirectionalStreaming<Request, Response, ReturnValue>(
353+
public func bidirectionalStreaming<Request, Response, ReturnValue: Sendable>(
354354
request: ClientRequest.Stream<Request>,
355355
descriptor: MethodDescriptor,
356356
serializer: some MessageSerializer<Request>,

Sources/GRPCCore/Internal/TaskGroup+CancellableTask.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ extension TaskGroup {
5858
}
5959

6060
@usableFromInline
61-
enum _ResultOrCancelled {
61+
enum _ResultOrCancelled: Sendable {
6262
case result(ChildTaskResult)
6363
case cancelled
6464
}

0 commit comments

Comments
 (0)