@@ -141,18 +141,41 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
141
141
@_backDeploy ( before: SwiftStdlib 5.8 )
142
142
public func withValue< R> ( _ valueDuringOperation: Value , operation: ( ) async throws -> R ,
143
143
file: String = #fileID, line: UInt = #line) async rethrows -> R {
144
+ return try await withValueImpl ( valueDuringOperation, operation: operation, file: file, line: line)
145
+ }
146
+
147
+ /// Implementation for withValue that consumes valueDuringOperation.
148
+ ///
149
+ /// Because _taskLocalValuePush and _taskLocalValuePop involve calls to
150
+ /// swift_task_alloc/swift_task_dealloc respectively unbeknownst to the
151
+ /// compiler, compiler-emitted calls to swift_task_de/alloc must be avoided
152
+ /// in a function that calls them.
153
+ ///
154
+ /// A copy of valueDuringOperation is required because withValue borrows its
155
+ /// argument but _taskLocalValuePush consumes its. Because
156
+ /// valueDuringOperation is of generic type, its size is not generally known,
157
+ /// so such a copy entails a stack allocation and a copy to that allocation.
158
+ /// That stack traffic gets lowered to calls to
159
+ /// swift_task_alloc/swift_task_deallloc.
160
+ ///
161
+ /// Split the calls _taskLocalValuePush/Pop from the compiler-emitted calls
162
+ /// to swift_task_de/alloc for the copy as follows:
163
+ /// - withValue contains the compiler-emitted calls swift_task_de/alloc.
164
+ /// - withValueImpl contains the calls to _taskLocalValuePush/Pop
165
+ @inlinable
166
+ @discardableResult
167
+ @_unsafeInheritExecutor
168
+ @available ( SwiftStdlib 5 . 1 , * ) // back deploy requires we declare the availability explicitly on this method
169
+ @_backDeploy ( before: SwiftStdlib 5.9 )
170
+ internal func withValueImpl< R> ( _ valueDuringOperation: __owned Value, operation: ( ) async throws -> R ,
171
+ file: String = #fileID, line: UInt = #line) async rethrows -> R {
144
172
// check if we're not trying to bind a value from an illegal context; this may crash
145
173
_checkIllegalTaskLocalBindingWithinWithTaskGroup ( file: file, line: line)
146
174
147
- _taskLocalValuePush ( key: key, value: valueDuringOperation)
148
- do {
149
- let result = try await operation ( )
150
- _taskLocalValuePop ( )
151
- return result
152
- } catch {
153
- _taskLocalValuePop ( )
154
- throw error
155
- }
175
+ _taskLocalValuePush ( key: key, value: consume valueDuringOperation)
176
+ defer { _taskLocalValuePop ( ) }
177
+
178
+ return try await operation ( )
156
179
}
157
180
158
181
/// Binds the task-local to the specific value for the duration of the
0 commit comments