Skip to content

Commit 35688e3

Browse files
waahm7xiazhvera
andauthored
Fix Concurrency Warnings in Swift 6 (#310)
Co-authored-by: Vera Xia <zhvxia@amazon.com>
1 parent ea0aef6 commit 35688e3

55 files changed

Lines changed: 879 additions & 1003 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,35 @@ jobs:
7171
chmod a+x builder
7272
./builder build -p ${{ env.PACKAGE_NAME }}
7373
74+
# Test that everything works with Swift 6 strict concurrency. We can remove this CI in the future once we raise our minimum Swift version to Swift 6.
75+
macos-swift6:
76+
runs-on: macos-15
77+
env:
78+
DEVELOPER_DIR: /Applications/Xcode.app
79+
XCODE_DESTINATION: 'OS X'
80+
NSUnbufferedIO: YES
81+
strategy:
82+
fail-fast: false
83+
steps:
84+
- uses: aws-actions/configure-aws-credentials@v4
85+
with:
86+
role-to-assume: ${{ env.CRT_CI_ROLE }}
87+
aws-region: ${{ env.AWS_DEFAULT_REGION }}
88+
- name: Checkout repository
89+
uses: actions/checkout@v4
90+
with:
91+
submodules: true
92+
fetch-depth: 0
93+
- name: Set Swift version to 6
94+
run: |
95+
sed -i '' 's/swift-tools-version:5\.[0-9][0-9]*/swift-tools-version:6\.0/' Package.swift
96+
# Verify that substitution was successful
97+
grep -q 'swift-tools-version:6\.0' Package.swift || (echo "No version 5.x found to update" && exit 1)
98+
- name: Build ${{ env.PACKAGE_NAME }} + consumers
99+
run: |
100+
swift build
101+
swift test
102+
74103
devices:
75104
runs-on: ${{ matrix.runner }}
76105
env:

Package.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ var package = Package(name: "aws-crt-swift",
1111
products: [
1212
.library(name: "AwsCommonRuntimeKit", targets: ["AwsCommonRuntimeKit"]),
1313
.executable(name: "Elasticurl", targets: ["Elasticurl"])
14+
],
15+
dependencies: [
16+
// Arugment Parser Dependency for ElasticCurl
17+
.package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMajor(from: "1.5.0"))
1418
]
1519
)
1620

@@ -332,7 +336,10 @@ packageTargets.append(contentsOf: [
332336
),
333337
.executableTarget(
334338
name: "Elasticurl",
335-
dependencies: ["AwsCommonRuntimeKit"],
339+
dependencies: [
340+
"AwsCommonRuntimeKit",
341+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
342+
],
336343
path: "Source/Elasticurl"
337344
)
338345
] )

Source/AwsCommonRuntimeKit/CommonRuntimeKit.swift

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
import AwsCEventStream
21
import AwsCAuth
2+
import AwsCEventStream
33
import AwsCMqtt
44
import LibNative
55

6-
/**
7-
* Initializes the library.
8-
* `CommonRuntimeKit.initialize` must be called before using any other functionality.
9-
*/
6+
/// Initializes the library.
7+
/// `CommonRuntimeKit.initialize` must be called before using any other functionality.
108
public struct CommonRuntimeKit {
119

1210
/// Initializes the library.
@@ -15,20 +13,23 @@ public struct CommonRuntimeKit {
1513
aws_auth_library_init(allocator.rawValue)
1614
aws_event_stream_library_init(allocator.rawValue)
1715
aws_mqtt_library_init(allocator.rawValue)
18-
aws_register_error_info(&s_crt_swift_error_list)
16+
withUnsafePointer(to: s_crt_swift_error_list) { ptr in
17+
aws_register_error_info(ptr)
18+
}
1919
}
2020

2121
/**
2222
* This is an optional cleanup function which will block until all the CRT resources have cleaned up.
2323
* Use this function only if you want to make sure that there are no memory leaks at the end of the application.
2424
* Warning: It will hang if you are still holding references to any CRT objects such as HostResolver.
2525
*/
26-
public static func cleanUp() {
27-
aws_unregister_error_info(&s_crt_swift_error_list)
26+
public static nonisolated func cleanUp() {
27+
withUnsafePointer(to: s_crt_swift_error_list) { ptr in
28+
aws_unregister_error_info(ptr)
29+
}
2830
aws_mqtt_library_clean_up()
2931
aws_event_stream_library_clean_up()
3032
aws_auth_library_clean_up()
31-
3233
}
3334

3435
private init() {}

Source/AwsCommonRuntimeKit/auth/credentials/Credentials.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import AwsCAuth
55
import Foundation
66

7-
public final class Credentials {
7+
// We can't mutate this class after initialization. Swift can not verify the sendability due to OpaquePointer,
8+
// So mark it unchecked Sendable
9+
public final class Credentials: @unchecked Sendable {
810

911
let rawValue: OpaquePointer
1012

Source/AwsCommonRuntimeKit/auth/credentials/CredentialsProvider.swift

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ public struct CognitoLoginPair: CStruct {
3434
}
3535
}
3636

37-
public class CredentialsProvider: CredentialsProviding {
38-
37+
// We can't mutate this class after initialization. Swift can not verify the sendability due to pointer,
38+
// So mark it unchecked Sendable
39+
public class CredentialsProvider: CredentialsProviding, @unchecked Sendable {
3940
let rawValue: UnsafeMutablePointer<aws_credentials_provider>
4041

4142
init(credentialsProvider: UnsafeMutablePointer<aws_credentials_provider>) {
@@ -667,25 +668,19 @@ private func onGetCredentials(credentials: OpaquePointer?,
667668
continuationCore.continuation.resume(returning: Credentials(rawValue: credentials!))
668669
}
669670

670-
// We need to share this pointer to C in a task block but Swift compiler complains
671-
// that Pointer does not conform to Sendable. Wrap the pointer in a @unchecked Sendable block
672-
// for Swift compiler to stop complaining.
673-
struct SendablePointer: @unchecked Sendable {
674-
let pointer: UnsafeMutableRawPointer
675-
}
676-
677671
private func getCredentialsDelegateFn(_ delegatePtr: UnsafeMutableRawPointer!,
678672
_ callbackFn: (@convention(c) (
679673
OpaquePointer?,
680674
Int32,
681675
UnsafeMutableRawPointer?) -> Void)!,
682676
_ userData: UnsafeMutableRawPointer!) -> Int32 {
683-
let delegate = Unmanaged<Box<CredentialsProviding>>
684-
.fromOpaque(delegatePtr)
685-
.takeUnretainedValue().contents
686-
let userData = SendablePointer(pointer: userData)
677+
let userData = SendableRawPointer(pointer: userData)
678+
let delegatePtr = SendableRawPointer(pointer: delegatePtr)
687679
Task {
688680
do {
681+
let delegate = Unmanaged<Box<CredentialsProviding>>
682+
.fromOpaque(delegatePtr.pointer!)
683+
.takeUnretainedValue().contents
689684
let credentials = try await delegate.getCredentials()
690685
callbackFn(credentials.rawValue, AWS_OP_SUCCESS, userData.pointer)
691686
} catch CommonRunTimeError.crtError(let crtError) {

Source/AwsCommonRuntimeKit/auth/imds/IAMProfile.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import AwsCAuth
55
import Foundation
66

7-
public struct IAMProfile {
7+
public struct IAMProfile: @unchecked Sendable {
88
public let lastUpdated: Date
99
public let profileArn: String
1010
public let profileId: String

Source/AwsCommonRuntimeKit/auth/imds/IMDSClient.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import AwsCAuth
55

66
// swiftlint:disable type_body_length
7-
public class IMDSClient {
7+
// We can't mutate this class after initialization. Swift can not verify the sendability due to OpaquePointer,
8+
// So mark it unchecked Sendable
9+
public class IMDSClient: @unchecked Sendable {
810
let rawValue: OpaquePointer
911

1012
/// Creates an IMDSClient that always uses IMDSv2

Source/AwsCommonRuntimeKit/auth/imds/IMDSInstanceInfo.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import AwsCAuth
55
import Foundation
66

7-
public struct IMDSInstanceInfo {
7+
public struct IMDSInstanceInfo: @unchecked Sendable {
88
public let marketPlaceProductCodes: [String]
99
public let availabilityZone: String
1010
public let privateIp: String
@@ -21,7 +21,8 @@ public struct IMDSInstanceInfo {
2121
public let region: String
2222

2323
init(instanceInfo: aws_imds_instance_info) {
24-
self.marketPlaceProductCodes = instanceInfo.marketplace_product_codes.byteCursorListToStringArray()
24+
self.marketPlaceProductCodes = instanceInfo.marketplace_product_codes
25+
.byteCursorListToStringArray()
2526
self.availabilityZone = instanceInfo.availability_zone.toString()
2627
self.privateIp = instanceInfo.private_ip.toString()
2728
self.version = instanceInfo.version.toString()

Source/AwsCommonRuntimeKit/auth/signing/Signer.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,11 @@ public class Signer {
164164
}
165165
}
166166

167-
class SignRequestCore {
167+
// After signing, we mutate the request and resume the continuation, which may result in a thread change.
168+
// We won't modify it after continuation.resume is called. So we can mark it @unchecked Sendable
169+
class SignRequestCore: @unchecked Sendable {
168170
let request: HTTPRequestBase
169-
var continuation: CheckedContinuation<HTTPRequestBase, Error>
171+
let continuation: CheckedContinuation<HTTPRequestBase, Error>
170172
let shouldSignHeader: ((String) -> Bool)?
171173
init(request: HTTPRequestBase,
172174
continuation: CheckedContinuation<HTTPRequestBase, Error>,
@@ -207,6 +209,7 @@ private func onRequestSigningComplete(signingResult: UnsafeMutablePointer<aws_si
207209
} else {
208210
signRequestCore.continuation.resume(throwing: CommonRunTimeError.crtError(.makeFromLastError()))
209211
}
212+
// It's not thread-safe to modify `signingRequestCore.request` after continuation.resume
210213
}
211214

212215
private func onSigningComplete(signingResult: UnsafeMutablePointer<aws_signing_result>?,
@@ -220,9 +223,10 @@ private func onSigningComplete(signingResult: UnsafeMutablePointer<aws_signing_r
220223

221224
// Success
222225
var awsStringPointer: UnsafeMutablePointer<aws_string>!
226+
let signature = AWSString("signature")
223227
guard aws_signing_result_get_property(
224228
signingResult!,
225-
g_aws_signature_property_name,
229+
signature.rawValue,
226230
&awsStringPointer) == AWS_OP_SUCCESS else {
227231
chunkSignerCore.continuation.resume(throwing: CommonRunTimeError.crtError(.makeFromLastError()))
228232
return

Source/AwsCommonRuntimeKit/auth/signing/SigningConfig.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import AwsCAuth
55
import Foundation
66

7-
public struct SigningConfig: CStructWithUserData {
7+
public struct SigningConfig: CStructWithUserData, @unchecked Sendable {
88

99
/// What signing algorithm to use.
1010
public var algorithm: SigningAlgorithmType
@@ -136,7 +136,7 @@ private func onShouldSignHeader(nameCursor: UnsafePointer<aws_byte_cursor>!,
136136
return signRequestCore.shouldSignHeader!(name)
137137
}
138138

139-
public enum SignatureType {
139+
public enum SignatureType: Sendable {
140140

141141
/// A signature for a full http request should be computed, with header updates applied to the signing result.
142142
case requestHeaders
@@ -162,7 +162,7 @@ public enum SignatureType {
162162
case requestEvent
163163
}
164164

165-
public enum SignedBodyHeaderType {
165+
public enum SignedBodyHeaderType: Sendable {
166166

167167
/// Do not add a header
168168
case none
@@ -174,7 +174,7 @@ public enum SignedBodyHeaderType {
174174
/// Optional string to use as the canonical request's body value.
175175
/// Typically, this is the SHA-256 of the (request/chunk/event) payload, written as lowercase hex.
176176
/// If this has been precalculated, it can be set here. Special values used by certain services can also be set.
177-
public enum SignedBodyValue: CustomStringConvertible, Equatable {
177+
public enum SignedBodyValue: CustomStringConvertible, Equatable, Sendable {
178178
/// if empty, a public value will be calculated from the payload during signing
179179
case empty
180180
/// For empty sha256
@@ -226,7 +226,7 @@ public enum SignedBodyValue: CustomStringConvertible, Equatable {
226226
}
227227
}
228228

229-
public enum SigningAlgorithmType {
229+
public enum SigningAlgorithmType: Sendable {
230230
case signingV4
231231
case signingV4Asymmetric
232232
case signingV4S3Express

0 commit comments

Comments
 (0)