Skip to content

Commit 5c512cf

Browse files
committed
wip
1 parent 7605549 commit 5c512cf

File tree

1 file changed

+99
-73
lines changed

1 file changed

+99
-73
lines changed

Sources/ConcurrencyExtras/LockIsolated.swift

+99-73
Original file line numberDiff line numberDiff line change
@@ -4,90 +4,116 @@ import Foundation
44
///
55
/// If you trust the sendability of the underlying value, consider using ``UncheckedSendable``,
66
/// instead.
7-
#if compiler(>=6)
8-
@available(iOS, deprecated: 10000, message: "Use 'Mutex', instead.")
9-
@available(macOS, deprecated: 10000, message: "Use 'Mutex', instead.")
10-
@available(tvOS, deprecated: 10000, message: "Use 'Mutex', instead.")
11-
@available(watchOS, deprecated: 10000, message: "Use 'Mutex', instead.")
12-
#endif
137
@dynamicMemberLookup
148
public final class LockIsolated<Value>: @unchecked Sendable {
159
private var _value: Value
1610
private let lock = NSRecursiveLock()
1711

18-
/// Initializes lock-isolated state around a value.
19-
///
20-
/// - Parameter value: A value to isolate with a lock.
21-
public init(_ value: @autoclosure @Sendable () throws -> Value) rethrows {
22-
self._value = try value()
23-
}
12+
#if compiler(>=6)
13+
/// Initializes lock-isolated state around a value.
14+
///
15+
/// - Parameter value: A value to isolate with a lock.
16+
public init(_ value: sending @autoclosure () throws -> sending Value) rethrows {
17+
self._value = try value()
18+
}
19+
#else
20+
public init(_ value: @autoclosure @Sendable () throws -> Value) rethrows {
21+
self._value = try value()
22+
}
23+
#endif
2424

25-
public subscript<Subject: Sendable>(dynamicMember keyPath: KeyPath<Value, Subject>) -> Subject {
26-
self.lock.sync {
27-
self._value[keyPath: keyPath]
25+
#if compiler(>=6)
26+
public subscript<Subject>(dynamicMember keyPath: KeyPath<Value, Subject>) -> sending Subject {
27+
self.lock.sync {
28+
self._value[keyPath: keyPath]
29+
}
2830
}
29-
}
31+
#else
32+
public subscript<Subject: Sendable>(dynamicMember keyPath: KeyPath<Value, Subject>) -> Subject {
33+
self.lock.sync {
34+
self._value[keyPath: keyPath]
35+
}
36+
}
37+
#endif
3038

31-
/// Perform an operation with isolated access to the underlying value.
32-
///
33-
/// Useful for modifying a value in a single transaction.
34-
///
35-
/// ```swift
36-
/// // Isolate an integer for concurrent read/write access:
37-
/// var count = LockIsolated(0)
38-
///
39-
/// func increment() {
40-
/// // Safely increment it:
41-
/// self.count.withValue { $0 += 1 }
42-
/// }
43-
/// ```
44-
///
45-
/// - Parameter operation: An operation to be performed on the the underlying value with a lock.
46-
/// - Returns: The result of the operation.
47-
public func withValue<T: Sendable>(
48-
_ operation: @Sendable (inout Value) throws -> T
49-
) rethrows -> T {
50-
try self.lock.sync {
51-
var value = self._value
52-
defer { self._value = value }
53-
return try operation(&value)
39+
#if compiler(>=6)
40+
/// Perform an operation with isolated access to the underlying value.
41+
///
42+
/// Useful for modifying a value in a single transaction.
43+
///
44+
/// ```swift
45+
/// // Isolate an integer for concurrent read/write access:
46+
/// var count = LockIsolated(0)
47+
///
48+
/// func increment() {
49+
/// // Safely increment it:
50+
/// self.count.withValue { $0 += 1 }
51+
/// }
52+
/// ```
53+
///
54+
/// - Parameter operation: An operation to be performed on the the underlying value with a lock.
55+
/// - Returns: The result of the operation.
56+
public func withValue<T>(
57+
_ operation: sending (inout sending Value) throws -> sending T
58+
) rethrows -> T {
59+
try self.lock.sync {
60+
try operation(&_value)
61+
}
5462
}
55-
}
63+
#else
64+
public func withValue<T: Sendable>(
65+
_ operation: @Sendable (inout Value) throws -> T
66+
) rethrows -> T {
67+
try self.lock.sync {
68+
var value = self._value
69+
defer { self._value = value }
70+
return try operation(&value)
71+
}
72+
}
73+
#endif
5674

57-
/// Overwrite the isolated value with a new value.
58-
///
59-
/// ```swift
60-
/// // Isolate an integer for concurrent read/write access:
61-
/// var count = LockIsolated(0)
62-
///
63-
/// func reset() {
64-
/// // Reset it:
65-
/// self.count.setValue(0)
66-
/// }
67-
/// ```
68-
///
69-
/// > Tip: Use ``withValue(_:)`` instead of ``setValue(_:)`` if the value being set is derived
70-
/// > from the current value. That is, do this:
71-
/// >
72-
/// > ```swift
73-
/// > self.count.withValue { $0 += 1 }
74-
/// > ```
75-
/// >
76-
/// > ...and not this:
77-
/// >
78-
/// > ```swift
79-
/// > self.count.setValue(self.count + 1)
80-
/// > ```
81-
/// >
82-
/// > ``withValue(_:)`` isolates the entire transaction and avoids data races between reading and
83-
/// > writing the value.
84-
///
85-
/// - Parameter newValue: The value to replace the current isolated value with.
86-
public func setValue(_ newValue: @autoclosure @Sendable () throws -> Value) rethrows {
87-
try self.lock.sync {
88-
self._value = try newValue()
75+
#if compiler(>=6)
76+
/// Overwrite the isolated value with a new value.
77+
///
78+
/// ```swift
79+
/// // Isolate an integer for concurrent read/write access:
80+
/// var count = LockIsolated(0)
81+
///
82+
/// func reset() {
83+
/// // Reset it:
84+
/// self.count.setValue(0)
85+
/// }
86+
/// ```
87+
///
88+
/// > Tip: Use ``withValue(_:)`` instead of ``setValue(_:)`` if the value being set is derived
89+
/// > from the current value. That is, do this:
90+
/// >
91+
/// > ```swift
92+
/// > self.count.withValue { $0 += 1 }
93+
/// > ```
94+
/// >
95+
/// > ...and not this:
96+
/// >
97+
/// > ```swift
98+
/// > self.count.setValue(self.count + 1)
99+
/// > ```
100+
/// >
101+
/// > ``withValue(_:)`` isolates the entire transaction and avoids data races between reading and
102+
/// > writing the value.
103+
///
104+
/// - Parameter newValue: The value to replace the current isolated value with.
105+
public func setValue(_ newValue: sending @autoclosure () throws -> sending Value) rethrows {
106+
try self.lock.sync {
107+
self._value = try newValue()
108+
}
89109
}
90-
}
110+
#else
111+
public func setValue(_ newValue: @autoclosure @Sendable () throws -> Value) rethrows {
112+
try self.lock.sync {
113+
self._value = try newValue()
114+
}
115+
}
116+
#endif
91117
}
92118

93119
extension LockIsolated where Value: Sendable {

0 commit comments

Comments
 (0)