[Concurrency] Cancelled group should only spawn already cancelled tasks

This commit is contained in:
Konrad `ktoso` Malawski
2021-06-24 08:28:05 +09:00
parent 11e3a652df
commit dd10132b12
5 changed files with 21 additions and 10 deletions

View File

@@ -39,6 +39,9 @@ public:
/// Upon a future task's completion, offer it to the task group it belongs to.
void offer(AsyncTask *completed, AsyncContext *context);
/// Checks the cancellation status of the group.
bool isCancelled();
};
} // end namespace swift

View File

@@ -565,7 +565,8 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl(
// In a task group we would not have allowed the `add` to create a child anymore,
// however better safe than sorry and `async let` are not expressed as task groups,
// so they may have been spawned in any case still.
if (swift_task_isCancelled(parent))
if (swift_task_isCancelled(parent) ||
(group && group->isCancelled()))
swift_task_cancel(task);
// Initialize task locals with a link to the parent task.

View File

@@ -508,6 +508,10 @@ void TaskGroup::offer(AsyncTask *completedTask, AsyncContext *context) {
asImpl(this)->offer(completedTask, context);
}
bool TaskGroup::isCancelled() {
return asImpl(this)->isCancelled();
}
static void fillGroupNextResult(TaskFutureWaitAsyncContext *context,
PollResult result) {
/// Fill in the result value

View File

@@ -355,12 +355,12 @@ public struct TaskGroup<ChildTaskResult> {
_taskGroupIsEmpty(_group)
}
/// Cancel all the remaining tasks in the group.
/// Cancel all the remaining, and future, tasks in the group.
///
/// A cancelled group will not will NOT accept new tasks being added into it.
///
/// Any results, including errors thrown by tasks affected by this
/// cancellation, are silently discarded.
/// A cancelled group will not will create new tasks when the `asyncUnlessCancelled`,
/// function is used. It will, however, continue to create tasks when the plain `async`
/// function is used. Such tasks will be created yet immediately cancelled, allowing
/// the tasks to perform some short-cut implementation, if they are responsive to cancellation.
///
/// This function may be called even from within child (or any other) tasks,
/// and will reliably cause the group to become cancelled.

View File

@@ -37,13 +37,16 @@ func test_taskGroup_cancel_then_add() async {
let none = await group.next()
print("next second: \(none)") // CHECK: next second: nil
group.spawn { 3 }
print("added third, unconditionally") // CHECK: added third, unconditionally
print("group isCancelled: \(group.isCancelled)") // CHECK: group isCancelled: true
group.spawn {
print("child task isCancelled: \(Task.isCancelled)") // CHECK: child task isCancelled: true
return 3
}
let three = await group.next()!
print("next third: \(three)") // CHECK: next third: 3
print("added third, unconditionally") // CHECK: added third, unconditionally
print("group isCancelled: \(group.isCancelled)") // CHECK: group isCancelled: true
return one + (none ?? 0)
}