Skip to content

Commit f994485

Browse files
committed
Make AsyncExpression conform to Sendable (#1067)
1 parent eb1013d commit f994485

File tree

2 files changed

+35
-13
lines changed

2 files changed

+35
-13
lines changed

Sources/Nimble/AsyncExpression.swift

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,34 @@
1+
private actor MemoizedClosure<T> {
2+
var closure: @Sendable () async throws -> T
3+
var cache: T?
4+
5+
init(_ closure: @escaping @Sendable () async throws -> T) {
6+
self.closure = closure
7+
}
8+
9+
func set(_ cache: T) -> T {
10+
self.cache = cache
11+
return cache
12+
}
13+
14+
func call(_ withoutCaching: Bool) async throws -> T {
15+
if withoutCaching {
16+
return try await closure()
17+
}
18+
if let cache {
19+
return cache
20+
} else {
21+
return set(try await closure())
22+
}
23+
}
24+
}
25+
126
// Memoizes the given closure, only calling the passed
227
// closure once; even if repeat calls to the returned closure
3-
private func memoizedClosure<T>(_ closure: @escaping () async throws -> T) -> (Bool) async throws -> T {
4-
var cache: T?
28+
private func memoizedClosure<T>(_ closure: @escaping @Sendable () async throws -> T) -> @Sendable (Bool) async throws -> T {
29+
let memoized = MemoizedClosure(closure)
530
return { withoutCaching in
6-
if withoutCaching || cache == nil {
7-
cache = try await closure()
8-
}
9-
return cache!
31+
try await memoized.call(withoutCaching)
1032
}
1133
}
1234

@@ -21,8 +43,8 @@ private func memoizedClosure<T>(_ closure: @escaping () async throws -> T) -> (B
2143
///
2244
/// This provides a common consumable API for matchers to utilize to allow
2345
/// Nimble to change internals to how the captured closure is managed.
24-
public struct AsyncExpression<Value> {
25-
internal let _expression: (Bool) async throws -> Value?
46+
public struct AsyncExpression<Value>: Sendable {
47+
internal let _expression: @Sendable (Bool) async throws -> Value?
2648
internal let _withoutCaching: Bool
2749
public let location: SourceLocation
2850
public let isClosure: Bool
@@ -38,7 +60,7 @@ public struct AsyncExpression<Value> {
3860
/// requires an explicit closure. This gives Nimble
3961
/// flexibility if @autoclosure behavior changes between
4062
/// Swift versions. Nimble internals always sets this true.
41-
public init(expression: @escaping () async throws -> Value?, location: SourceLocation, isClosure: Bool = true) {
63+
public init(expression: @escaping @Sendable () async throws -> Value?, location: SourceLocation, isClosure: Bool = true) {
4264
self._expression = memoizedClosure(expression)
4365
self.location = location
4466
self._withoutCaching = false
@@ -59,7 +81,7 @@ public struct AsyncExpression<Value> {
5981
/// requires an explicit closure. This gives Nimble
6082
/// flexibility if @autoclosure behavior changes between
6183
/// Swift versions. Nimble internals always sets this true.
62-
public init(memoizedExpression: @escaping (Bool) async throws -> Value?, location: SourceLocation, withoutCaching: Bool, isClosure: Bool = true) {
84+
public init(memoizedExpression: @escaping @Sendable (Bool) async throws -> Value?, location: SourceLocation, withoutCaching: Bool, isClosure: Bool = true) {
6385
self._expression = memoizedExpression
6486
self.location = location
6587
self._withoutCaching = withoutCaching
@@ -90,15 +112,15 @@ public struct AsyncExpression<Value> {
90112
///
91113
/// - Parameter block: The block that can cast the current Expression value to a
92114
/// new type.
93-
public func cast<U>(_ block: @escaping (Value?) throws -> U?) -> AsyncExpression<U> {
115+
public func cast<U>(_ block: @escaping @Sendable (Value?) throws -> U?) -> AsyncExpression<U> {
94116
AsyncExpression<U>(
95117
expression: ({ try await block(self.evaluate()) }),
96118
location: self.location,
97119
isClosure: self.isClosure
98120
)
99121
}
100122

101-
public func cast<U>(_ block: @escaping (Value?) async throws -> U?) -> AsyncExpression<U> {
123+
public func cast<U>(_ block: @escaping @Sendable (Value?) async throws -> U?) -> AsyncExpression<U> {
102124
AsyncExpression<U>(
103125
expression: ({ try await block(self.evaluate()) }),
104126
location: self.location,

Sources/Nimble/Utils/SourceLocation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public typealias FileString = StaticString
1111
public typealias FileString = String
1212
#endif
1313

14-
public final class SourceLocation: NSObject {
14+
public final class SourceLocation: NSObject, Sendable {
1515
public let file: FileString
1616
public let line: UInt
1717

0 commit comments

Comments
 (0)