-
Notifications
You must be signed in to change notification settings - Fork 10.5k
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
Resolve race between task creation and concurrent escalation and cancellation. #40606
Conversation
Doh, just hit a conflict with @rjmccall's changes. I'm going to have to take some time to rework this. |
12a4af7
to
32d7a7f
Compare
@swift-ci please test |
There was a problem hiding this 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 */ |
There was a problem hiding this comment.
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 👍
32d7a7f
to
4410226
Compare
@swift-ci please smoke test and merge it |
stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def
Show resolved
Hide resolved
/// | ||
/// Returns false if the task has been cancelled. | ||
SWIFT_EXPORT_FROM(swift_Concurrency) | ||
SWIFT_CC(swift) bool swift_task_removeStatusRecord(TaskStatusRecord *record); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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` | ||
*/ |
There was a problem hiding this comment.
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
).
There was a problem hiding this comment.
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()); | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
stdlib/public/Concurrency/Task.cpp
Outdated
* needs to be idempotent */ | ||
} | ||
return true; | ||
}); |
There was a problem hiding this comment.
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
});
@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` | ||
*/ |
There was a problem hiding this comment.
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
8e5b4e5
to
e35eba0
Compare
@swift-ci please test |
Build failed |
@swift-ci Please test Windows platform |
@swift-ci Please smoke test Linux platform |
@swift-ci Please test Linux platform |
There was a problem hiding this 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.
This change has two parts to it:
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).
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