Skip to content

Commit 46b4464

Browse files
authored
Fix some strict concurrency warnings (#310)
# Motivation There were a few new strict concurrency warnings that this PR fixes.
1 parent d162617 commit 46b4464

File tree

5 files changed

+44
-20
lines changed

5 files changed

+44
-20
lines changed

Diff for: Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ let package = Package(
1414
.library(name: "AsyncAlgorithms", targets: ["AsyncAlgorithms"]),
1515
],
1616
dependencies: [
17-
.package(url: "https://github.com/apple/swift-collections.git", from: "1.0.4"),
17+
.package(url: "https://github.com/apple/swift-collections.git", from: "1.1.0"),
1818
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
1919
],
2020
targets: [

Diff for: Sources/AsyncAlgorithms/Buffer/BoundedBufferStateMachine.swift

+10-7
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,19 @@ struct BoundedBufferStateMachine<Base: AsyncSequence> {
1616
typealias SuspendedProducer = UnsafeContinuation<Void, Never>
1717
typealias SuspendedConsumer = UnsafeContinuation<Result<Base.Element, Error>?, Never>
1818

19+
// We are using UnsafeTransfer here since we have to get the elements from the task
20+
// into the consumer task. This is a transfer but we cannot prove this to the compiler at this point
21+
// since next is not marked as transferring the return value.
1922
fileprivate enum State {
2023
case initial(base: Base)
2124
case buffering(
2225
task: Task<Void, Never>,
23-
buffer: Deque<Result<Element, Error>>,
26+
buffer: Deque<Result<UnsafeTransfer<Element>, Error>>,
2427
suspendedProducer: SuspendedProducer?,
2528
suspendedConsumer: SuspendedConsumer?
2629
)
2730
case modifying
28-
case finished(buffer: Deque<Result<Element, Error>>)
31+
case finished(buffer: Deque<Result<UnsafeTransfer<Element>, Error>>)
2932
}
3033

3134
private var state: State
@@ -139,7 +142,7 @@ struct BoundedBufferStateMachine<Base: AsyncSequence> {
139142
// we have to stack the new element or suspend the producer if the buffer is full
140143
precondition(buffer.count < limit, "Invalid state. The buffer should be available for stacking a new element.")
141144
self.state = .modifying
142-
buffer.append(.success(element))
145+
buffer.append(.success(.init(element)))
143146
self.state = .buffering(task: task, buffer: buffer, suspendedProducer: nil, suspendedConsumer: nil)
144147
return .none
145148

@@ -218,7 +221,7 @@ struct BoundedBufferStateMachine<Base: AsyncSequence> {
218221
self.state = .modifying
219222
let result = buffer.popFirst()!
220223
self.state = .buffering(task: task, buffer: buffer, suspendedProducer: nil, suspendedConsumer: nil)
221-
return .returnResult(producerContinuation: suspendedProducer, result: result)
224+
return .returnResult(producerContinuation: suspendedProducer, result: result.map { $0.wrapped })
222225

223226
case .buffering(_, _, _, .some):
224227
preconditionFailure("Invalid states. There is already a suspended consumer.")
@@ -233,7 +236,7 @@ struct BoundedBufferStateMachine<Base: AsyncSequence> {
233236
self.state = .modifying
234237
let result = buffer.popFirst()!
235238
self.state = .finished(buffer: buffer)
236-
return .returnResult(producerContinuation: nil, result: result)
239+
return .returnResult(producerContinuation: nil, result: result.map { $0.wrapped })
237240
}
238241
}
239242

@@ -257,7 +260,7 @@ struct BoundedBufferStateMachine<Base: AsyncSequence> {
257260
self.state = .modifying
258261
let result = buffer.popFirst()!
259262
self.state = .buffering(task: task, buffer: buffer, suspendedProducer: nil, suspendedConsumer: nil)
260-
return .returnResult(producerContinuation: suspendedProducer, result: result)
263+
return .returnResult(producerContinuation: suspendedProducer, result: result.map { $0.wrapped })
261264

262265
case .buffering(_, _, _, .some):
263266
preconditionFailure("Invalid states. There is already a suspended consumer.")
@@ -272,7 +275,7 @@ struct BoundedBufferStateMachine<Base: AsyncSequence> {
272275
self.state = .modifying
273276
let result = buffer.popFirst()!
274277
self.state = .finished(buffer: buffer)
275-
return .returnResult(producerContinuation: nil, result: result)
278+
return .returnResult(producerContinuation: nil, result: result.map { $0.wrapped })
276279
}
277280
}
278281

Diff for: Sources/AsyncAlgorithms/Buffer/UnboundedBufferStateMachine.swift

+14-9
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,18 @@ struct UnboundedBufferStateMachine<Base: AsyncSequence> {
2121
case bufferingOldest(Int)
2222
}
2323

24+
// We are using UnsafeTransfer here since we have to get the elements from the task
25+
// into the consumer task. This is a transfer but we cannot prove this to the compiler at this point
26+
// since next is not marked as transferring the return value.
2427
fileprivate enum State {
2528
case initial(base: Base)
2629
case buffering(
2730
task: Task<Void, Never>,
28-
buffer: Deque<Result<Element, Error>>,
31+
buffer: Deque<Result<UnsafeTransfer<Element>, Error>>,
2932
suspendedConsumer: SuspendedConsumer?
3033
)
3134
case modifying
32-
case finished(buffer: Deque<Result<Element, Error>>)
35+
case finished(buffer: Deque<Result<UnsafeTransfer<Element>, Error>>)
3336
}
3437

3538
private var state: State
@@ -84,15 +87,15 @@ struct UnboundedBufferStateMachine<Base: AsyncSequence> {
8487
self.state = .modifying
8588
switch self.policy {
8689
case .unlimited:
87-
buffer.append(.success(element))
90+
buffer.append(.success(.init(element)))
8891
case .bufferingNewest(let limit):
8992
if buffer.count >= limit {
9093
_ = buffer.popFirst()
9194
}
92-
buffer.append(.success(element))
95+
buffer.append(.success(.init(element)))
9396
case .bufferingOldest(let limit):
9497
if buffer.count < limit {
95-
buffer.append(.success(element))
98+
buffer.append(.success(.init(element)))
9699
}
97100
}
98101
self.state = .buffering(task: task, buffer: buffer, suspendedConsumer: nil)
@@ -170,7 +173,7 @@ struct UnboundedBufferStateMachine<Base: AsyncSequence> {
170173
self.state = .modifying
171174
let result = buffer.popFirst()!
172175
self.state = .buffering(task: task, buffer: buffer, suspendedConsumer: nil)
173-
return .returnResult(result)
176+
return .returnResult(result.map { $0.wrapped })
174177

175178
case .modifying:
176179
preconditionFailure("Invalid state.")
@@ -182,7 +185,7 @@ struct UnboundedBufferStateMachine<Base: AsyncSequence> {
182185
self.state = .modifying
183186
let result = buffer.popFirst()!
184187
self.state = .finished(buffer: buffer)
185-
return .returnResult(result)
188+
return .returnResult(result.map { $0.wrapped })
186189
}
187190
}
188191

@@ -208,7 +211,7 @@ struct UnboundedBufferStateMachine<Base: AsyncSequence> {
208211
self.state = .modifying
209212
let result = buffer.popFirst()!
210213
self.state = .buffering(task: task, buffer: buffer, suspendedConsumer: nil)
211-
return .resumeConsumer(result)
214+
return .resumeConsumer(result.map { $0.wrapped })
212215

213216
case .modifying:
214217
preconditionFailure("Invalid state.")
@@ -220,7 +223,7 @@ struct UnboundedBufferStateMachine<Base: AsyncSequence> {
220223
self.state = .modifying
221224
let result = buffer.popFirst()!
222225
self.state = .finished(buffer: buffer)
223-
return .resumeConsumer(result)
226+
return .resumeConsumer(result.map { $0.wrapped })
224227
}
225228
}
226229

@@ -251,3 +254,5 @@ struct UnboundedBufferStateMachine<Base: AsyncSequence> {
251254

252255
extension UnboundedBufferStateMachine: Sendable where Base: Sendable { }
253256
extension UnboundedBufferStateMachine.State: Sendable where Base: Sendable { }
257+
258+

Diff for: Sources/AsyncAlgorithms/Channels/ChannelStateMachine.swift

-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@
1010
//===----------------------------------------------------------------------===//
1111
import OrderedCollections
1212

13-
// NOTE: this is only marked as unchecked since the swift-collections tag is before auditing for Sendable
14-
extension OrderedSet: @unchecked Sendable where Element: Sendable { }
15-
1613
struct ChannelStateMachine<Element: Sendable, Failure: Error>: Sendable {
1714
private struct SuspendedProducer: Hashable, Sendable {
1815
let id: UInt64

Diff for: Sources/AsyncAlgorithms/UnsafeTransfer.swift

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Async Algorithms open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
/// A wrapper struct to unconditionally to transfer an non-Sendable value.
13+
struct UnsafeTransfer<Element>: @unchecked Sendable {
14+
let wrapped: Element
15+
16+
init(_ wrapped: Element) {
17+
self.wrapped = wrapped
18+
}
19+
}

0 commit comments

Comments
 (0)