Skip to content

[SE-0304] Align Task API implementation with the fourth revision of the proposal #38306

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 10 commits into from
Jul 9, 2021

Conversation

DougGregor
Copy link
Member

@DougGregor DougGregor commented Jul 8, 2021

Specific changes are:

  • Renaming the task group async operations to addTask
  • Rename TaskPriority.default to TaskPriority.medium
  • Rename Task.yield() to Task.suspend()
  • Add task group waitForAll()
  • Implement cancellable Task.sleep(nanoseconds:)

All changes with the exception of Task.sleep(nanoseconds:) are implemented in an ABI-neutral manner.
Task.sleep(nanoseconds:) is ABI-additive; it's too much code to want to inline.

Implements rdar://80339563

Do this as a staged change to the ABI, introducing an underscored
`@usableFromInline` implementation to the ABI that we can rely on
later, and an `@_alwaysEmitIntoClient` version we can inline now.
Copy link

@benlings benlings left a comment

Choose a reason for hiding this comment

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

Hope an unsolicited review is OK

@DougGregor DougGregor force-pushed the structured-concurrency-review-4 branch from 78e4dad to c6be200 Compare July 8, 2021 14:32
@DougGregor DougGregor force-pushed the structured-concurrency-review-4 branch from c6be200 to 59d1e61 Compare July 8, 2021 16:46
@DougGregor
Copy link
Member Author

@swift-ci please test


@available(SwiftStdlib 5.5, *)
@main struct Main {
static let pause = 500_000_000 // 500ms
Copy link
Contributor

Choose a reason for hiding this comment

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

it might also be good to test the 0 case of a pause

Copy link
Member Author

Choose a reason for hiding this comment

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

Great point, I'm adding it in a subsequent commit.

}
} onCancel: {
let continuationWord = continuationPtr.pointee
if UInt(continuationWord) != 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

is this safe from here on out if another thread cancels just after the checkCancellation at line 84?

Copy link
Member Author

Choose a reason for hiding this comment

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

So, there's an existing race in withTaskCancellationHandler that also gets replicated here. When we address that (@ktoso was looking into it), this will also be fixed.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll get to it this week. I think we'll end up having to "check again" after having stored a cancellation record basically.

try checkCancellation()

// Allocate storage for the flag word and continuation.
let wordPtr = UnsafeMutablePointer<Builtin.Word>.allocate(capacity: 2)
Copy link
Contributor

Choose a reason for hiding this comment

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

this word does not escape the context of this function right? it might be interesting to actually use a stack allocation instead of heap allocating it.

else wise if it does escape then this probably should be ref counted right?

Copy link
Member Author

Choose a reason for hiding this comment

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

It does escape the context of this function: when the task is cancelled, the timed "wake" task has yet to be scheduled, and needs to check whether it finished first or not. In the case where it gets called but didn't finish first, it handles the deallocation.

No need for reference counting; the deallocation points are well-defined.

The prior implementation of `Task.sleep()` effectively had two
different atomic words to capture the state, which could lead to cases
where cancelling before a sleep operation started would fail to
throw `CancellationError`. Reimplement the logic for the cancellable
sleep with a more traditional lock-free approach by
packing all of the state information into a single word, where we
always load, figure out what to do, then compare-and-swap.
@DougGregor
Copy link
Member Author

@swift-ci please test

@DougGregor
Copy link
Member Author

@swift-ci please test Windows

@DougGregor DougGregor merged commit 57e22bb into swiftlang:main Jul 9, 2021
@DougGregor DougGregor deleted the structured-concurrency-review-4 branch July 9, 2021 05:07
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