-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Concurrency] Add API stubs for Task #34391
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
Conversation
@swift-ci please smoke test |
Will be unblocked by #34370 |
f030e3e
to
89763f6
Compare
@swift-ci please test |
@swift-ci please smoke test |
And we're green; I also added some small tests... nothing very exciting but better doc the expected look of the API via such anyway. Please review and merge at will @DougGregor (I don't have merge powers btw, not sure if that's expected I think Dario from my team did have those powers hm 🤔) |
@swift-ci please test |
3f68495
to
3a4ead6
Compare
3b87b8f
to
579c89c
Compare
Also added runDetached, priority and task handles. This mostly reuses existing wording, but puts it into API docs. Please have a look @DougGregor More things coming soon -- was thinking in a separate PR tbh, but I can keep adding here if we want all the Task core primitives in one PR. |
@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. My few comments can be addressed in a follow-up.
/// These partial periods towards the task's completion are `PartialAsyncTask`. | ||
/// Partial tasks are generally not interacted with by end-users directly, | ||
/// unless implementing a scheduler. | ||
public struct Task { |
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 should probably put an OpaquePointer
or perhaps an AnyObject
in here and make it frozen, because Task
is a thin wrapper around a pointer to a task, which is a ref-counted entity.
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 true, will address in next PR 👍
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.
It's swift ref-counted, you should use Builtin.NativeObject
.
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.
In the proposal this was just a namespace. If we're going to make it a type that's a generic reference to a task, I think that's useful, but we need to think about how this interacts with Handle
. Maybe it's a class.
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.
The proposal indeed at first declared enum Task
but also has a few struct Task
but I guess those were always meant to be extension Task
-- I guess it boils down to the primary question if one should be able to "touch" someone else's task ever at all.
We mentioned that if we allow Task.current
one could potentially store or send it around; This interacts with things like task local storage and the deadline/timeout APIs...
Or should we stick to "task is a concept" and all interaction with it is via static functions? It is more correct I guess, i.e. we can then easily disallow any other task being able to set/get task local storage (if it was an API on the task object it could be abused).
So... back to namespace or there's reason to have it a class?
stdlib/public/Concurrency/Task.swift
Outdated
/// If the task has not completed yet, its priority will be elevated to the | ||
/// priority of the current task. Note that this may not be as effective as | ||
/// creating the task with the "right" priority to in the first place. | ||
public func get() async -> Success { |
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.
Should this go away?
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 damn, forgot cancelation!
Thanks Doug, will remove
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.
Actually... that's not clear yet!
The more I dug into this while writing up the proposal the more second thoughts I was having.
Pasing example semantics comparison for reference:
let neverChecks: Task.Handle<String, Never> = Task.runDetached {
// never checks cancellation
// Task.sleep(.seconds(60)) // pretend being slow
"ok"
}
// ----
// Semantics A) Cancellation truly is only co-operative
// These semantics would argue FOR the existence of the non-throwing get()
neverChecks.cancel() // we cancel immediately
// get ALWAYS throws, i.e. we're expecting this to throw a CancellationError
// but under Semantics A won't -- we're truly cooperative, and the task never
// checked cancellation so in reality it is as if it wasn't cancelled.
await try neverChecks.get()
// because the task never checked cancellation, we get "ok here"
// Semantics A conclusion: we should offer a non throwing get()
// ----
// Semantics B) Cancellation is eager on handles, hard and does NOT guarantee emitting the same error
// These semantics would argue AGAINST the existence of the non throwing get()
neverChecks.cancel() // we cancel immediately
await try neverChecks.get() // under "eager" B cancellation semantics this will immediately throw
// even if the task still executed and completed successfully (because it never checked for cancellation!)
// this would throw a CancellationError under Semantics B
I would argue semantics A is what people are meaningfully after, and it allows building more predictably on the tasks / handles. This would imply that we do need the non throwing get.
Ended up addressing in here some of the comments @DougGregor mentioned. |
@swift-ci please test |
Introduce minimal placeholders for
Task
andUnsafeContinuation
to enable prototyping usage of the concurrency APIs.edit: crasher resolved 👍
This currently crashes with:Assertion failed: (empty() && "explosion had values remaining when destroyed!"), function ~Explosion, file /Users/ktoso/code/swift-project/swift/lib/IRGen/Explosion.h, line 65.
, partial stack:@DougGregor suggests the crash may be related to missing functionality that @nate-chandler is working on?