Skip to content

Commit 5e9d4d8

Browse files
authored
Merge pull request #36032 from ktoso/wip-cancellation-handler
2 parents 8b64ab2 + 4e6e159 commit 5e9d4d8

File tree

5 files changed

+56
-8
lines changed

5 files changed

+56
-8
lines changed

include/swift/Runtime/Concurrency.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,17 @@ size_t swift_task_getJobFlags(AsyncTask* task);
225225
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
226226
bool swift_task_isCancelled(AsyncTask* task);
227227

228+
/// Create and add an cancellation record to the task.
229+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
230+
CancellationNotificationStatusRecord*
231+
swift_task_addCancellationHandler(
232+
AsyncTask *task, CancellationNotificationStatusRecord::FunctionType handler);
233+
234+
/// Remove the passed cancellation record from the task.
235+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
236+
void swift_task_removeCancellationHandler(
237+
AsyncTask *task, CancellationNotificationStatusRecord *record);
238+
228239
using TaskLocalValuesFragment = AsyncTask::TaskLocalValuesFragment;
229240

230241
/// Get a task local value from the passed in task. Its Swift signature is

stdlib/public/Concurrency/Task.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,25 @@ bool swift::swift_task_isCancelled(AsyncTask *task) {
569569
return task->isCancelled();
570570
}
571571

572+
CancellationNotificationStatusRecord*
573+
swift::swift_task_addCancellationHandler(
574+
AsyncTask *task, CancellationNotificationStatusRecord::FunctionType handler) {
575+
void *allocation =
576+
swift_task_alloc(task, sizeof(CancellationNotificationStatusRecord));
577+
auto *record =
578+
new (allocation) CancellationNotificationStatusRecord(
579+
handler, /*arg=*/nullptr);
580+
581+
swift_task_addStatusRecord(task, record);
582+
return record;
583+
}
584+
585+
void swift::swift_task_removeCancellationHandler(
586+
AsyncTask *task, CancellationNotificationStatusRecord *record) {
587+
swift_task_removeStatusRecord(task, record);
588+
swift_task_dealloc(task, record);
589+
}
590+
572591
SWIFT_CC(swift)
573592
void swift::swift_continuation_logFailedCheck(const char *message) {
574593
swift_reportError(0, message);

stdlib/public/Concurrency/Task.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -585,9 +585,6 @@ func getJobFlags(_ task: Builtin.NativeObject) -> Task.JobFlags
585585
@usableFromInline
586586
func _enqueueJobGlobal(_ task: Builtin.Job)
587587

588-
@_silgen_name("swift_task_isCancelled")
589-
func isTaskCancelled(_ task: Builtin.NativeObject) -> Bool
590-
591588
@available(*, deprecated)
592589
@_silgen_name("swift_task_runAndBlockThread")
593590
public func runAsyncAndBlock(_ asyncFun: @escaping () async -> ())

stdlib/public/Concurrency/TaskCancellation.swift

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,23 @@ extension Task {
6565
///
6666
/// Does not check for cancellation, and always executes the passed `operation`.
6767
///
68-
/// ### Suspension
6968
/// This function returns instantly and will never suspend.
70-
/* @instantaneous */
7169
public static func withCancellationHandler<T>(
7270
handler: @concurrent () -> (),
7371
operation: () async throws -> T
74-
) async throws -> T {
75-
fatalError("\(#function) not implemented yet.")
72+
) async rethrows -> T {
73+
let task = Builtin.getCurrentAsyncTask()
74+
75+
guard !_taskIsCancelled(task) else {
76+
// If the current task is already cancelled, run the handler immediately.
77+
handler()
78+
return try await operation()
79+
}
80+
81+
let record = _taskAddCancellationHandler(task: task, handler: handler)
82+
defer { _taskRemoveCancellationHandler(task: task, record: record) }
83+
84+
return try await operation()
7685
}
7786

7887
/// The default cancellation thrown when a task is cancelled.
@@ -85,3 +94,15 @@ extension Task {
8594
}
8695

8796
}
97+
98+
@_silgen_name("swift_task_addCancellationHandler")
99+
func _taskAddCancellationHandler(
100+
task: Builtin.NativeObject,
101+
handler: @concurrent () -> ()
102+
) -> UnsafeRawPointer /*CancellationNotificationStatusRecord*/
103+
104+
@_silgen_name("swift_task_removeCancellationHandler")
105+
func _taskRemoveCancellationHandler(
106+
task: Builtin.NativeObject,
107+
record: UnsafeRawPointer /*CancellationNotificationStatusRecord*/
108+
)

test/Concurrency/async_cancellation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func test_cancellation_withCancellationHandler(_ anything: Any) async -> Picture
2626
let handle: Task.Handle<PictureData, Error> = Task.runDetached {
2727
let file = SomeFile()
2828

29-
return try await Task.withCancellationHandler(
29+
return await Task.withCancellationHandler(
3030
handler: { file.close() },
3131
operation: {
3232
await test_cancellation_guard_isCancelled(file)

0 commit comments

Comments
 (0)