Skip to content

Commit 2b08113

Browse files
authored
Enable strict concurrency checking in CI (#11)
Enable strict concurrency checking in CI ### Motivation To further avoid concurrency bugs, enable complete concurrency checking in CI. ### Modifications Added the compiler flag to the docker-compose scripts. ### Result If a warning of this nature comes up, because we have warnings-as-errors, it'll fail CI. ### Test Plan Locally built without any warnings with the flag enabled. Reviewed by: glbrntt Builds: ✔︎ pull request validation (5.8) - Build finished. ✔︎ pull request validation (5.9) - Build finished. ✔︎ pull request validation (nightly) - Build finished. ✔︎ pull request validation (soundness) - Build finished. #11
1 parent f6e6c2f commit 2b08113

File tree

7 files changed

+80
-12
lines changed

7 files changed

+80
-12
lines changed

Sources/OpenAPIURLSession/URLSessionTransport.swift

+11-1
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,19 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414
import OpenAPIRuntime
15+
#if canImport(Darwin)
1516
import Foundation
17+
#else
18+
@preconcurrency import struct Foundation.URL
19+
@preconcurrency import struct Foundation.URLComponents
20+
@preconcurrency import struct Foundation.Data
21+
@preconcurrency import protocol Foundation.LocalizedError
22+
#endif
1623
#if canImport(FoundationNetworking)
17-
@preconcurrency import FoundationNetworking
24+
@preconcurrency import struct FoundationNetworking.URLRequest
25+
@preconcurrency import class FoundationNetworking.URLSession
26+
@preconcurrency import class FoundationNetworking.URLResponse
27+
@preconcurrency import class FoundationNetworking.HTTPURLResponse
1828
#endif
1929

2030
/// A client transport that performs HTTP operations using the URLSession type
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftOpenAPIGenerator open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Foundation
16+
17+
/// A wrapper providing locked access to a value.
18+
///
19+
/// Marked as @unchecked Sendable due to the synchronization being
20+
/// performed manually using locks.
21+
///
22+
/// Note: Use the `package` access modifier once min Swift version is increased.
23+
@_spi(Locking)
24+
public final class LockedValueBox<Value: Sendable>: @unchecked Sendable {
25+
private let lock: NSLock = {
26+
let lock = NSLock()
27+
lock.name = "com.apple.swift-openapi-urlsession.lock.LockedValueBox"
28+
return lock
29+
}()
30+
private var value: Value
31+
public init(_ value: Value) {
32+
self.value = value
33+
}
34+
public func withValue<R>(_ work: (inout Value) throws -> R) rethrows -> R {
35+
lock.lock()
36+
defer {
37+
lock.unlock()
38+
}
39+
return try work(&value)
40+
}
41+
}

Tests/OpenAPIURLSessionTests/URLSessionTransportTests.swift

+24-10
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,18 @@
1313
//===----------------------------------------------------------------------===//
1414
import XCTest
1515
import OpenAPIRuntime
16+
#if canImport(Darwin)
1617
import Foundation
18+
#else
19+
@preconcurrency import struct Foundation.URL
20+
#endif
1721
#if canImport(FoundationNetworking)
18-
import FoundationNetworking
22+
@preconcurrency import struct FoundationNetworking.URLRequest
23+
@preconcurrency import class FoundationNetworking.URLProtocol
24+
@preconcurrency import class FoundationNetworking.URLSession
25+
@preconcurrency import class FoundationNetworking.HTTPURLResponse
26+
@preconcurrency import class FoundationNetworking.URLResponse
27+
@preconcurrency import class FoundationNetworking.URLSessionConfiguration
1928
#endif
2029
@testable import OpenAPIURLSession
2130

@@ -53,12 +62,14 @@ class URLSessionTransportTests: XCTestCase {
5362

5463
func testSend() async throws {
5564
let endpointURL = URL(string: "http://example.com/api/hello/Maria?greeting=Howdy")!
56-
MockURLProtocol.mockHTTPResponses[endpointURL] = .success(
57-
(
58-
HTTPURLResponse(url: endpointURL, statusCode: 201, httpVersion: nil, headerFields: [:])!,
59-
body: Data("👋".utf8)
65+
MockURLProtocol.mockHTTPResponses.withValue { map in
66+
map[endpointURL] = .success(
67+
(
68+
HTTPURLResponse(url: endpointURL, statusCode: 201, httpVersion: nil, headerFields: [:])!,
69+
body: Data("👋".utf8)
70+
)
6071
)
61-
)
72+
}
6273
let transport: any ClientTransport = URLSessionTransport(
6374
configuration: .init(session: MockURLProtocol.mockURLSession)
6475
)
@@ -81,9 +92,10 @@ class URLSessionTransportTests: XCTestCase {
8192
}
8293

8394
class MockURLProtocol: URLProtocol {
84-
static var mockHTTPResponses: [URL: Result<(response: HTTPURLResponse, body: Data?), any Error>] = [:]
95+
typealias MockHTTPResponseMap = [URL: Result<(response: HTTPURLResponse, body: Data?), any Error>]
96+
static let mockHTTPResponses = LockedValueBox<MockHTTPResponseMap>([:])
8597

86-
static var recordedHTTPRequests: [URLRequest] = []
98+
static let recordedHTTPRequests = LockedValueBox<[URLRequest]>([])
8799

88100
override class func canInit(with request: URLRequest) -> Bool { true }
89101

@@ -92,9 +104,11 @@ class MockURLProtocol: URLProtocol {
92104
override func stopLoading() {}
93105

94106
override func startLoading() {
95-
Self.recordedHTTPRequests.append(self.request)
107+
Self.recordedHTTPRequests.withValue { $0.append(self.request) }
96108
guard let url = self.request.url else { return }
97-
guard let response = Self.mockHTTPResponses[url] else { return }
109+
guard let response = Self.mockHTTPResponses.withValue({ $0[url] }) else {
110+
return
111+
}
98112
switch response {
99113
case .success(let mockResponse):
100114
client?.urlProtocol(self, didReceive: mockResponse.response, cacheStoragePolicy: .notAllowed)

docker/docker-compose.2204.58.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ services:
1313
environment:
1414
- WARN_AS_ERROR_ARG=-Xswiftc -warnings-as-errors
1515
- IMPORT_CHECK_ARG=--explicit-target-dependency-import-check error
16+
- STRICT_CONCURRENCY_ARG=-Xswiftc -strict-concurrency=complete
1617

1718
shell:
1819
image: *image

docker/docker-compose.2204.59.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ services:
1212
environment:
1313
- WARN_AS_ERROR_ARG=-Xswiftc -warnings-as-errors
1414
- IMPORT_CHECK_ARG=--explicit-target-dependency-import-check error
15+
- STRICT_CONCURRENCY_ARG=-Xswiftc -strict-concurrency=complete
1516

1617
shell:
1718
image: *image

docker/docker-compose.2204.main.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ services:
1313
environment:
1414
- WARN_AS_ERROR_ARG=-Xswiftc -warnings-as-errors
1515
- IMPORT_CHECK_ARG=--explicit-target-dependency-import-check error
16+
- STRICT_CONCURRENCY_ARG=-Xswiftc -strict-concurrency=complete
1617

1718
shell:
1819
image: *image

docker/docker-compose.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ services:
3030

3131
test:
3232
<<: *common
33-
command: /bin/bash -xcl "swift $${SWIFT_TEST_VERB-test} $${WARN_AS_ERROR_ARG-} $${SANITIZER_ARG-} $${IMPORT_CHECK_ARG-}"
33+
command: /bin/bash -xcl "swift $${SWIFT_TEST_VERB-test} $${WARN_AS_ERROR_ARG-} $${SANITIZER_ARG-} $${IMPORT_CHECK_ARG-} $${STRICT_CONCURRENCY_ARG-}"
3434

3535
shell:
3636
<<: *common

0 commit comments

Comments
 (0)