diff --git a/stdlib/public/Concurrency/TaskCancellation.swift b/stdlib/public/Concurrency/TaskCancellation.swift index b4d453e72651c..2991b4dfac3f0 100644 --- a/stdlib/public/Concurrency/TaskCancellation.swift +++ b/stdlib/public/Concurrency/TaskCancellation.swift @@ -74,10 +74,29 @@ import Swift /// Therefore, if a cancellation handler must acquire a lock, other code should /// not cancel tasks or resume continuations while holding that lock. @available(SwiftStdlib 5.1, *) +@export(implementation) +nonisolated(nonsending) +public func withTaskCancellationHandler( + operation: () async throws(E) -> T, + onCancel handler: @Sendable () -> Void +) async throws(E) -> T { + // unconditionally add the cancellation record to the task. + // if the task was already cancelled, it will be executed right away. + let record = unsafe Builtin.taskAddCancellationHandler(handler: handler) + defer { unsafe Builtin.taskRemoveCancellationHandler(record: record) } + + return try await operation() +} + #if !$Embedded -@backDeployed(before: SwiftStdlib 6.0) -#endif -public func withTaskCancellationHandler( +@available(SwiftStdlib 6.0, *) +@usableFromInline +@abi(func withTaskCancellationHandler( + operation: () async throws -> T, + onCancel handler: @Sendable () -> Void, + isolation: isolated (any Actor)?, + ) async rethrows -> T) +func __abi_withTaskCancellationHandler( operation: () async throws -> T, onCancel handler: @Sendable () -> Void, isolation: isolated (any Actor)? = #isolation @@ -89,6 +108,7 @@ public func withTaskCancellationHandler( return try await operation() } +#endif // Note: hack to stage out @_unsafeInheritExecutor forms of various functions // in favor of #isolation. The _unsafeInheritExecutor_ prefix is meaningful diff --git a/stdlib/public/Observation/Sources/Observation/Observations.swift b/stdlib/public/Observation/Sources/Observation/Observations.swift index 3aa6d91ba56c6..8d652f1e13f6b 100644 --- a/stdlib/public/Observation/Sources/Observation/Observations.swift +++ b/stdlib/public/Observation/Sources/Observation/Observations.swift @@ -240,7 +240,7 @@ public struct Observations: AsyncSequence, Se }, onCancel: { // ensure to clean out our continuation uon cancellation State.cancel(state, id: id) - }, isolation: iterationIsolation) + }) return try await trackEmission(isolation: iterationIsolation, state: state, id: id) } } catch { diff --git a/test/Concurrency/async_cancellation.swift b/test/Concurrency/async_cancellation.swift index 47f61fd3523e0..859c3a519297d 100644 --- a/test/Concurrency/async_cancellation.swift +++ b/test/Concurrency/async_cancellation.swift @@ -28,15 +28,23 @@ struct SomeFile: Sendable { func close() {} } +enum HomeworkError: Error { +case dogAteIt +} + @available(SwiftStdlib 5.1, *) func test_cancellation_withTaskCancellationHandler(_ anything: Any) async -> PictureData? { let handle: Task = .init { let file = SomeFile() - return await withTaskCancellationHandler { - await test_cancellation_guard_isCancelled(file) - } onCancel: { - file.close() + do throws(HomeworkError) { + return try await withTaskCancellationHandler { () throws(HomeworkError) in + await test_cancellation_guard_isCancelled(file) + } onCancel: { + file.close() + } + } catch .dogAteIt { + return PictureData.value("...") } } diff --git a/test/api-digester/stability-concurrency-abi.test b/test/api-digester/stability-concurrency-abi.test index 8e8e24505f24f..61a31426a612f 100644 --- a/test/api-digester/stability-concurrency-abi.test +++ b/test/api-digester/stability-concurrency-abi.test @@ -89,8 +89,8 @@ Func withCheckedThrowingContinuation(function:_:) has parameter 0 type change fr Func withCheckedThrowingContinuation(function:_:) has parameter 1 type change from (_Concurrency.CheckedContinuation<τ_0_0, any Swift.Error>) -> () to Swift.String // #isolation adoption for cancellation handlers; old APIs are kept ABI compatible -Func withTaskCancellationHandler(operation:onCancel:) has been renamed to Func withTaskCancellationHandler(operation:onCancel:isolation:) -Func withTaskCancellationHandler(operation:onCancel:) has mangled name changing from '_Concurrency.withTaskCancellationHandler(operation: () async throws -> A, onCancel: @Sendable () -> ()) async throws -> A' to '_Concurrency.withTaskCancellationHandler(operation: () async throws -> A, onCancel: @Sendable () -> (), isolation: isolated Swift.Optional) async throws -> A' +Func withTaskCancellationHandler(operation:onCancel:) has been renamed to Func __abi_withTaskCancellationHandler(operation:onCancel:isolation:) +Func withTaskCancellationHandler(operation:onCancel:) has mangled name changing from '_Concurrency.withTaskCancellationHandler(operation: () async throws -> A, onCancel: @Sendable () -> ()) async throws -> A' to '_Concurrency.__abi_withTaskCancellationHandler(operation: () async throws -> A, onCancel: @Sendable () -> (), isolation: isolated Swift.Optional) async throws -> A' // #isolated was adopted and the old methods kept: $ss31withCheckedThrowingContinuation8function_xSS_yScCyxs5Error_pGXEtYaKlF Func withCheckedContinuation(function:_:) has been renamed to Func withCheckedContinuation(isolation:function:_:)