Skip to content

Commit 0d179b2

Browse files
authored
fix: Make Context thread-safe (#853)
1 parent 697ab72 commit 0d179b2

File tree

13 files changed

+79
-61
lines changed

13 files changed

+79
-61
lines changed

Diff for: Sources/ClientRuntime/Idempotency/Context+Idempotency.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import class Smithy.ContextBuilder
1212
extension Context {
1313

1414
public func getIdempotencyTokenGenerator() -> IdempotencyTokenGenerator {
15-
return attributes.get(key: idempotencyTokenGeneratorKey)!
15+
get(key: idempotencyTokenGeneratorKey)!
1616
}
1717
}
1818

Diff for: Sources/Smithy/Context.swift

+29-12
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,63 @@
55
// SPDX-License-Identifier: Apache-2.0
66
//
77

8+
import class Foundation.NSRecursiveLock
9+
810
public class Context {
9-
public var attributes: Attributes
11+
private var _attributes: Attributes
12+
private let lock = NSRecursiveLock()
1013

1114
public init(attributes: Attributes) {
12-
self.attributes = attributes
15+
self._attributes = attributes
1316
}
1417

1518
public func toBuilder() -> ContextBuilder {
16-
let builder = ContextBuilder()
17-
builder.attributes = self.attributes
18-
return builder
19+
ContextBuilder(attributes: accessAttributes())
1920
}
2021

2122
public func getLogger() -> LogAgent? {
22-
return attributes.get(key: AttributeKeys.logger)
23+
return accessAttributes().get(key: AttributeKeys.logger)
24+
}
25+
26+
@discardableResult
27+
private func accessAttributes(accessor: ((inout Attributes) -> Void)? = nil) -> Attributes {
28+
lock.lock()
29+
defer { lock.unlock() }
30+
accessor?(&_attributes)
31+
return _attributes
2332
}
2433
}
2534

2635
extension Context {
36+
2737
public func get<T>(key: AttributeKey<T>) -> T? {
28-
self.attributes.get(key: key)
38+
accessAttributes().get(key: key)
2939
}
3040

3141
public func contains<T>(key: AttributeKey<T>) -> Bool {
32-
self.attributes.contains(key: key)
42+
accessAttributes().contains(key: key)
3343
}
3444

3545
public func set<T>(key: AttributeKey<T>, value: T?) {
36-
self.attributes.set(key: key, value: value)
46+
accessAttributes { attributes in
47+
attributes.set(key: key, value: value)
48+
}
3749
}
3850

3951
public func remove<T>(key: AttributeKey<T>) {
40-
self.attributes.remove(key: key)
52+
accessAttributes { attributes in
53+
attributes.remove(key: key)
54+
}
4155
}
4256
}
4357

4458
public class ContextBuilder {
45-
public init() {}
4659

47-
public var attributes: Attributes = Attributes()
60+
public init(attributes: Attributes = Attributes()) {
61+
self.attributes = attributes
62+
}
63+
64+
public var attributes: Attributes
4865

4966
// We follow the convention of returning the builder object
5067
// itself from any configuration methods, and by adding the

Diff for: Sources/SmithyChecksumsAPI/Context+Checksum.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import struct Smithy.AttributeKey
1111
public extension Context {
1212

1313
var checksum: ChecksumAlgorithm? {
14-
get { attributes.get(key: checksumKey) }
15-
set { attributes.set(key: checksumKey, value: newValue) }
14+
get { get(key: checksumKey) }
15+
set { set(key: checksumKey, value: newValue) }
1616
}
1717

1818
var checksumString: String? { self.checksum?.toString() }

Diff for: Sources/SmithyEventStreamsAPI/Context+EventStreamsAPI.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import struct Smithy.AttributeKey
1111
public extension Context {
1212

1313
var messageEncoder: MessageEncoder? {
14-
get { attributes.get(key: messageEncoderKey) }
15-
set { attributes.set(key: messageEncoderKey, value: newValue) }
14+
get { get(key: messageEncoderKey) }
15+
set { set(key: messageEncoderKey, value: newValue) }
1616
}
1717
}
1818

Diff for: Sources/SmithyEventStreamsAuthAPI/Context+EventStreamsAuthAPI.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import struct Smithy.AttributeKey
1111
public extension Context {
1212

1313
var messageSigner: MessageSigner? {
14-
get { attributes.get(key: messageSignerKey) }
15-
set { attributes.set(key: messageSignerKey, value: newValue) }
14+
get { get(key: messageSignerKey) }
15+
set { set(key: messageSignerKey, value: newValue) }
1616
}
1717
}
1818

Diff for: Sources/SmithyHTTPAPI/Context+HTTP.swift

+24-24
Original file line numberDiff line numberDiff line change
@@ -15,73 +15,73 @@ import struct Foundation.TimeInterval
1515
extension Context {
1616

1717
public var httpResponse: HTTPResponse? {
18-
get { attributes.get(key: httpResponseKey) }
19-
set { attributes.set(key: httpResponseKey, value: newValue) }
18+
get { get(key: httpResponseKey) }
19+
set { set(key: httpResponseKey, value: newValue) }
2020
}
2121

2222
public var expiration: TimeInterval {
23-
get { attributes.get(key: expirationKey) ?? 0 }
24-
set { attributes.set(key: expirationKey, value: newValue) }
23+
get { get(key: expirationKey) ?? 0 }
24+
set { set(key: expirationKey, value: newValue) }
2525
}
2626

2727
public var host: String? {
28-
get { attributes.get(key: hostKey) }
29-
set { attributes.set(key: hostKey, value: newValue) }
28+
get { get(key: hostKey) }
29+
set { set(key: hostKey, value: newValue) }
3030
}
3131

3232
public var hostPrefix: String? {
33-
get { attributes.get(key: hostPrefixKey) }
34-
set { attributes.set(key: hostPrefixKey, value: newValue) }
33+
get { get(key: hostPrefixKey) }
34+
set { set(key: hostPrefixKey, value: newValue) }
3535
}
3636

3737
public var method: HTTPMethodType {
38-
get { attributes.get(key: methodKey) ?? .get }
39-
set { attributes.set(key: methodKey, value: newValue) }
38+
get { get(key: methodKey) ?? .get }
39+
set { set(key: methodKey, value: newValue) }
4040
}
4141

4242
public func getOperation() -> String? {
43-
return attributes.get(key: SmithyHTTPAPIKeys.operation)
43+
get(key: SmithyHTTPAPIKeys.operation)
4444
}
4545

4646
/// The partition ID to be used for this context.
4747
///
4848
/// Requests made with the same partition ID will be grouped together for retry throttling purposes.
4949
/// If no partition ID is provided, requests will be partitioned based on the hostname.
5050
public var partitionID: String? {
51-
get { attributes.get(key: partitionIDKey) }
52-
set { attributes.set(key: partitionIDKey, value: newValue) }
51+
get { get(key: partitionIDKey) }
52+
set { set(key: partitionIDKey, value: newValue) }
5353
}
5454

5555
public var path: String {
56-
get { attributes.get(key: pathKey)! }
57-
set { attributes.set(key: pathKey, value: newValue) }
56+
get { get(key: pathKey)! }
57+
set { set(key: pathKey, value: newValue) }
5858
}
5959

6060
public func getRegion() -> String? {
61-
return attributes.get(key: SmithyHTTPAPIKeys.region)
61+
return get(key: SmithyHTTPAPIKeys.region)
6262
}
6363

6464
public func getServiceName() -> String {
65-
return attributes.get(key: SmithyHTTPAPIKeys.serviceName)!
65+
return get(key: SmithyHTTPAPIKeys.serviceName)!
6666
}
6767

6868
public var signingName: String? {
69-
get { attributes.get(key: signingNameKey) }
70-
set { attributes.set(key: signingNameKey, value: newValue) }
69+
get { get(key: signingNameKey) }
70+
set { set(key: signingNameKey, value: newValue) }
7171
}
7272

7373
public var signingRegion: String? {
74-
get { attributes.get(key: signingRegionKey) }
75-
set { attributes.set(key: signingRegionKey, value: newValue) }
74+
get { get(key: signingRegionKey) }
75+
set { set(key: signingRegionKey, value: newValue) }
7676
}
7777

7878
public func hasUnsignedPayloadTrait() -> Bool {
79-
return attributes.get(key: SmithyHTTPAPIKeys.hasUnsignedPayloadTrait) ?? false
79+
return get(key: SmithyHTTPAPIKeys.hasUnsignedPayloadTrait) ?? false
8080
}
8181

8282
public var isBidirectionalStreamingEnabled: Bool {
83-
get { attributes.get(key: isBidirectionalStreamingEnabledKey) ?? false }
84-
set { attributes.set(key: isBidirectionalStreamingEnabledKey, value: newValue) }
83+
get { get(key: isBidirectionalStreamingEnabledKey) ?? false }
84+
set { set(key: isBidirectionalStreamingEnabledKey, value: newValue) }
8585
}
8686

8787
/// Returns `true` if the request should use `http2` and only `http2` without falling back to `http1`

Diff for: Sources/SmithyHTTPAuthAPI/Context/Context+Chunked.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import struct Smithy.AttributeKey
1010

1111
public extension Context {
1212
var isChunkedEligibleStream: Bool? {
13-
get { attributes.get(key: isChunkedEligibleStreamKey) }
14-
set { attributes.set(key: isChunkedEligibleStreamKey, value: newValue) }
13+
get { get(key: isChunkedEligibleStreamKey) }
14+
set { set(key: isChunkedEligibleStreamKey, value: newValue) }
1515
}
1616
}
1717

Diff for: Sources/SmithyHTTPAuthAPI/Context/Context+EstimatedSkew.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ import struct Smithy.AttributeKey
1212

1313
public extension Context {
1414
var estimatedSkew: TimeInterval? {
15-
get { attributes.get(key: estimatedSkewKey) }
16-
set { attributes.set(key: estimatedSkewKey, value: newValue) }
15+
get { get(key: estimatedSkewKey) }
16+
set { set(key: estimatedSkewKey, value: newValue) }
1717
}
1818

1919
var socketTimeout: TimeInterval? {
20-
get { attributes.get(key: socketTimeoutKey) }
21-
set { attributes.set(key: socketTimeoutKey, value: newValue) }
20+
get { get(key: socketTimeoutKey) }
21+
set { set(key: socketTimeoutKey, value: newValue) }
2222
}
2323
}
2424

Diff for: Sources/SmithyHTTPAuthAPI/Context/Context+HTTPAuth.swift

+8-7
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,27 @@ import struct Smithy.Attributes
1111
import struct Smithy.AttributeKey
1212

1313
public extension Context {
14+
1415
func getAuthSchemes() -> Attributes? {
15-
return attributes.get(key: authSchemesKey)
16+
get(key: authSchemesKey)
1617
}
1718

1819
var selectedAuthScheme: SelectedAuthScheme? {
19-
get { attributes.get(key: selectedAuthSchemeKey) }
20-
set { attributes.set(key: selectedAuthSchemeKey, value: newValue) }
20+
get { get(key: selectedAuthSchemeKey) }
21+
set { set(key: selectedAuthSchemeKey, value: newValue) }
2122
}
2223

2324
func setSelectedAuthScheme(_ value: SelectedAuthScheme?) {
24-
attributes.set(key: selectedAuthSchemeKey, value: value)
25+
set(key: selectedAuthSchemeKey, value: value)
2526
}
2627

2728
func getAuthSchemeResolver() -> AuthSchemeResolver? {
28-
return attributes.get(key: authSchemeResolverKey)
29+
get(key: authSchemeResolverKey)
2930
}
3031

3132
var signingAlgorithm: SigningAlgorithm? {
32-
get { attributes.get(key: signingAlgorithmKey) }
33-
set { attributes.set(key: signingAlgorithmKey, value: newValue) }
33+
get { get(key: signingAlgorithmKey) }
34+
set { set(key: signingAlgorithmKey, value: newValue) }
3435
}
3536
}
3637

Diff for: Sources/SmithyHTTPAuthAPI/Context/Context+RequestSignature.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import struct Smithy.AttributeKey
1212
public extension Context {
1313

1414
var requestSignature: String {
15-
get { attributes.get(key: requestSignatureKey) ?? "" }
16-
set { attributes.set(key: requestSignatureKey, value: newValue) }
15+
get { get(key: requestSignatureKey) ?? "" }
16+
set { set(key: requestSignatureKey, value: newValue) }
1717
}
1818
}
1919

Diff for: Sources/SmithyIdentityAPI/Context/Context+FlowType.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public let flowTypeKey = AttributeKey<FlowType>(name: "FlowType")
1313

1414
extension Context {
1515
public func getFlowType() -> FlowType {
16-
return attributes.get(key: flowTypeKey) ?? .NORMAL
16+
get(key: flowTypeKey) ?? .NORMAL
1717
}
1818
}
1919

Diff for: Sources/SmithyIdentityAPI/Context/Context+IdentityResolver.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import class Smithy.ContextBuilder
1212

1313
extension Context {
1414
public func getIdentityResolvers() -> Attributes? {
15-
return attributes.get(key: identityResolversKey)
15+
get(key: identityResolversKey)
1616
}
1717
}
1818

Diff for: Tests/ClientRuntimeTests/InterceptorTests/InterceptorTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class InterceptorTests: XCTestCase {
123123
let updatedInput = interceptorContext.getInput()
124124
XCTAssertEqual(updatedInput.property, "bar")
125125
XCTAssertEqual(updatedInput.otherProperty, 1)
126-
XCTAssertEqual(interceptorContext.getAttributes().attributes.get(key: AttributeKey(name: "foo")), "bar")
126+
XCTAssertEqual(interceptorContext.getAttributes().get(key: AttributeKey(name: "foo")), "bar")
127127
XCTAssertEqual(interceptorContext.getRequest().headers.value(for: "foo"), "bar")
128128
XCTAssertEqual(interceptorContext.getRequest().headers.value(for: "otherProperty"), "1")
129129
}

0 commit comments

Comments
 (0)