Skip to content

Resolve race between task creation and concurrent escalation and cancellation. #40606

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 12, 2022

Conversation

rokhinip
Copy link
Contributor

@rokhinip rokhinip commented Dec 17, 2021

This change has two parts to it:

  1. Add in a new interface (addStatusRecordWithChecks) for adding task
    status records that also takes in a function ref. This function ref will
    be used to evaluate if current state of the parent task has any changes
    that need to be propagated to the child task that has been created.

This is necessary to prevent the following race between task creation
and concurrent cancellation and escalation:

(a) Parent task create child task. It does lazy relaxed loads on its own
state while doing so and propagates this state to the child.
(b) Child task is created but has not been attached to the parent
task/task group.
(c) Parent task gets cancelled by another thread.
(d) Child task gets linked into the parent’s task status records but no
reevaluation has happened to account for changes that might have happened to
the parent after (a).

  1. Move status record management functions from the
    Runtime/Concurrency.h to TaskPrivate.h. Remove any corresponding
    overrides that are no longer needed. Remove unused tryAddStatusRecord
    method whose functionality is provided by addStatusRecordWithChecks.

Rdar: rdar://86347801

@rokhinip rokhinip requested review from ktoso and rjmccall December 17, 2021 05:16
@rokhinip
Copy link
Contributor Author

rokhinip commented Dec 17, 2021

Doh, just hit a conflict with @rjmccall's changes. I'm going to have to take some time to rework this.

@rokhinip rokhinip force-pushed the rokhinip/86347801-task-creation-escalation-race branch 2 times, most recently from 12a4af7 to 32d7a7f Compare December 17, 2021 07:36
@rokhinip
Copy link
Contributor Author

@swift-ci please test

Copy link
Contributor

@ktoso ktoso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just confirming my understanding in a few places and minor nitpicks.

/*failure*/ std::memory_order_relaxed)) {
return true;
} else {
/* Retry */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx for explicit comments like that; big fan of such style 👍

@rokhinip rokhinip force-pushed the rokhinip/86347801-task-creation-escalation-race branch from 32d7a7f to 4410226 Compare December 18, 2021 02:58
@rokhinip
Copy link
Contributor Author

@swift-ci please smoke test and merge it

///
/// Returns false if the task has been cancelled.
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swift) bool swift_task_removeStatusRecord(TaskStatusRecord *record);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these are no longer public ABI, (1) they shouldn't be exported and (2) please give them names that don't sound like they're in the ABI. For example, this one could just be removeTaskStatusRecord without any prefixes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Okay I didn't realize the swift_ prefix was for exported functions only. I'll adjust them.

Copy link
Contributor Author

@rokhinip rokhinip Dec 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So while I was fixing this, I realized that we do want to be able to export them from the concurrency library to be able to write unit tests against them - see unittests/runtime/TaskStatus.cpp.

So shall I keep to the regular swift_ prefixed names and export the functions or should we do something else since we're talking about exporting for the sake of testing?

SWIFT_CC(swift)
bool swift_task_addStatusRecordWithChecks(
TaskStatusRecord *record,
llvm::function_ref<bool(ActiveTaskStatus)> testAddRecord);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is the only function for adding a status record, it can just be addTaskStatusRecord, and it just takes an update function because that's what it does.

* child task since we know that it won't have any task status records and
* cannot be accessed by anyone else since it hasn't been linked in yet.
* Avoids the extra logic in `swift_task_cancel` and `swift_task_escalate`
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally use // comments unless we're specifically doing aCallWith(/*an inline comment before an*/ argument).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix these comments, please. Doc comments before functions use ///, inline comments use //.

if (parentStatus.isStoredPriorityEscalated()) {
newChildTaskStatus = newChildTaskStatus.withEscalatedPriority(
parentStatus.getStoredPriority());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it's safe to rely on isStoredPriorityEscalated here, since it can be cleared asynchronously. You should just set the priority of the child task to the priority of the parent. You should also update the priority in the Job header.

Copy link
Contributor Author

@rokhinip rokhinip Dec 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can it be cleared? Isn't escalation permanent on a task?

I'd expect a task to be created at say, UT priority, and then if it gets escalated to IN priority, to have the escalated flag be permanently set on the task status.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh okay reading further, I think I misunderstood the semantics of the isEscalated flag. Okay you're right, I need to change this.

* needs to be idempotent */
}
return true;
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a general rule, we format around lambdas as if they were a sub-statement of the call, so this would be more like:

bool fireHandlerNow = false;
swift_task_addStatusRecordWithChecks(record, [&](ActiveTaskStatus parentStatus) {
  fireHandlerNow = parentStatus.isCancelled();
  return true; // Add the status record regardless
});

@rokhinip rokhinip requested a review from rjmccall December 21, 2021 03:51
@rokhinip
Copy link
Contributor Author

@swift-ci please smoke test

* child task since we know that it won't have any task status records and
* cannot be accessed by anyone else since it hasn't been linked in yet.
* Avoids the extra logic in `swift_task_cancel` and `swift_task_escalate`
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix these comments, please. Doc comments before functions use ///, inline comments use //.

This change has two parts to it:

1. Add in a new interface (addStatusRecordWithChecks) for adding task
status records that also takes in a function ref. This function ref will
be used to evaluate if current state of the parent task has any changes
that need to be propagated to the child task that has been created.

This is necessary to prevent the following race between task creation
and concurrent cancellation and escalation:

a. Parent task create child task. It does lazy relaxed loads on its own
   state while doing so and propagates this state to the child.
b. Child task is created but has not been attached to the parent
   task/task group.
c. Parent task gets cancelled by another thread.
d. Child task gets linked into the parent’s task status records but no
   reevaluation has happened to account for changes that might have happened to
   the parent after (a).

2. Move status record management functions from the
Runtime/Concurrency.h to TaskPrivate.h. Remove any corresponding
overrides that are no longer needed. Remove unused tryAddStatusRecord
method whose functionality is provided by addStatusRecordWithChecks.

Radar-Id: rdar://problem/86347801
Radar-Id: rdar://problem/86347801
@rokhinip rokhinip force-pushed the rokhinip/86347801-task-creation-escalation-race branch from 8e5b4e5 to e35eba0 Compare December 31, 2021 11:24
@rokhinip
Copy link
Contributor Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - e35eba0

@rokhinip
Copy link
Contributor Author

@swift-ci Please test Windows platform

@rokhinip
Copy link
Contributor Author

@swift-ci Please smoke test Linux platform

@rokhinip
Copy link
Contributor Author

@swift-ci Please test Linux platform

@rokhinip rokhinip requested review from rjmccall and ktoso January 10, 2022 09:07
Copy link
Contributor

@ktoso ktoso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gave it another skim, looks good afaics.

@rokhinip rokhinip merged commit a4fe57f into main Jan 12, 2022
@rokhinip rokhinip deleted the rokhinip/86347801-task-creation-escalation-race branch January 12, 2022 04:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants