-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
Revise doc comments for structured concurrency #36905
Revise doc comments for structured concurrency #36905
Conversation
Marking as a draft until the placeholders (marked with ◊) get filled in. |
dd74560
to
e7aff66
Compare
stdlib/public/Concurrency/Task.swift
Outdated
/// | ||
/// Only code that's running as part of the task can interact with that task, | ||
/// by invoking the appropriate context-sensitive static functions which operate | ||
/// on the current task. Because all such functions are asynchronous, they can only | ||
/// be invoked as part of an existing task, and therefore are guaranteed to be | ||
/// effective. |
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.
Sorry, this is outdated wording here;
We have both static (non async) functions and instance functions now. The wording about async functions and properties is pretty ancient sorry.
I think we can remove the entire sentence from "Because all such functions are asynchronous ... " since those dont exist anymore and they're handled by the static and instance ones.
stdlib/public/Concurrency/Task.swift
Outdated
/// be invoked as part of an existing task, and therefore are guaranteed to be | ||
/// effective. | ||
/// ◊TR: What do we mean by "effective" above? |
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 think we can remove the entire sentence so no need to dwell here; but what I meant that "they will work and not crash utterly" :)
stdlib/public/Concurrency/Task.swift
Outdated
/// Partial tasks are generally not interacted with by end-users directly, | ||
/// unless implementing a scheduler. | ||
/// Unless you're implementing a scheduler, | ||
/// you don't generally interacted with partial tasks directly. |
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.
/// you don't generally interacted with partial tasks directly. | |
/// you don't generally interact with jobs directly. |
I believe John is removing the notion of partial tasks and they'll be named "Jobs": https://github.com/apple/swift/pull/36878/files#diff-92484f8fd5d76f2bd84b8509dc599fde13def3c55f342c8a64ffbba2cc9d7c97L7
stdlib/public/Concurrency/Task.swift
Outdated
/// | ||
/// ◊ a task checks its own cancellation status | ||
/// ◊ you might need to check cancellation repeatedly for long-running work | ||
/// ◊ the canonical response to cancelation is to throw `Task.CancellationError` |
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.
Yes, all these are correct.
The shortcut to "check and throw the cancellation error" is try Task.checkCancellation()
but one might implement it just with Task.isCancelled
and throwing
stdlib/public/Concurrency/Task.swift
Outdated
@@ -46,10 +63,9 @@ public struct Task { | |||
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) | |||
extension Task { | |||
|
|||
/// Returns 'current' `Task` instance, representing the task from within which | |||
/// this function was called. | |||
/// Returns the task that this code is being run on. |
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 feel we should expand here, how about something like:
/// Returns the task that this code is being run on. | |
/// Returns the task that this code is being run on. | |
/// | |
/// If invoked from the context of an asynchronous function or closure, | |
/// this is guaranteed to return a non-nil task. When called from a synchronous | |
/// context the returned value depends on wether the synchronous operation was | |
/// itself called from an asynchronous context or not. For example: | |
/// | |
/// func hello() { assert(Task.current != nil) } | |
/// func asynchronous() async { hello() } | |
/// | |
/// is correct and will not trigger the assertion, because an asynchronous function exists in the call-stack leading to the `Task.current` invocation. |
or something like that?
stdlib/public/Concurrency/Task.swift
Outdated
/// | ||
/// If no current `Task` is available, returns `Priority.default`. | ||
/// If you access this property outside of any task, | ||
/// its value is `Priority.default`. |
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.
FIXME: I thought we fixed this -- this should be .unspecified
(and in implementation as well)
stdlib/public/Concurrency/Task.swift
Outdated
/// | ||
/// TODO: Define the details of task priority; It is likely to be a concept | ||
/// ◊TODO: Define the details of task priority; It is likely to be a concept | ||
/// similar to Darwin Dispatch's QoS; bearing in mind that priority is not as | ||
/// much of a thing on other platforms (i.e. server side Linux systems). |
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.
for better or worse nothing seems to have been done here and we're likely to stick to the ones we have -- the todo will be likely just removed.
stdlib/public/Concurrency/Task.swift
Outdated
/// - this DOES affect `Task.currentPriority()`. | ||
/// In both cases, priority elevation prevents a low-priority task | ||
/// blocking the execution of a high priority task, | ||
/// also known as *priority inversion*. |
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.
Hmm... I'm not sure about "prevents" it definitely "attempts to lessen the impact of" but we don't really fully prevent it... we still need to complete running the low priority task, even though now it became boosted to high priority... I guess maybe that counts as "prevents"? a bit iffy on the wording hm. @rjmccall maybe has a stronger opinion?
stdlib/public/Concurrency/Task.swift
Outdated
/// i.e. the task will run regardless of the handle still being present or not. | ||
/// Dropping a handle however means losing the ability to await on the task's result | ||
/// and losing the ability to cancel it. | ||
/// It's not a programming error to discard a task's handle without awaiting or canceling the 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.
Small side question, just to educate myself; so guidelines are to actually use "it's"? All my life in technical writing i've been avoiding it :) Is it because it's shorter/easier to read?
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 the exact reason, but yes that's the style used here. For documentation, we follow both Apple Style Guide and style guidance from DevPubs that's specific to reference. ASG's entry for contractions begins:
As part of Apple’s informal voice, contractions are used and recommended throughout most documentation, interface text, and marketing copy. Keep localization in mind when deciding how or whether to use contractions.
/// It's not a programming error to discard a task's handle without awaiting or canceling the task. | ||
/// A task runs whether or not you still have its handle stored somewhere. | ||
/// However, if you discard a task's handle, you give up the ability | ||
/// to wait for that task's result or cancel the 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.
👍
stdlib/public/Concurrency/Task.swift
Outdated
/// ### Cancellation | ||
/// If the awaited on task gets cancelled externally the `get()` will throw | ||
/// a cancellation error. | ||
/// If task is canceled, this method throws a cancellation error. |
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 lost the "externally" here.
edit, wait a second, this is some ancient semantics that are not actually implemented nor do I think we'd want them (nor are they in the proposal now).
I think we need to change as:
/// If task is canceled, this method throws a cancellation error. |
stdlib/public/Concurrency/Task.swift
Outdated
/// If the task is canceled internally --- | ||
/// for example, by checking for cancellation | ||
/// and throwing a specific error, or by using `checkCancellation` --- | ||
/// this method rethrows the error that was thrown by the 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.
This wording is slightly off and ancient; most recent source of truth is https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md
I think the wording is rather:
/// If the task was cancelled it may check for it and throw a `Task.CancellationError`.
/// It may also decide to handle cancellation in other ways, including returning a best-effort result or similar.
stdlib/public/Concurrency/Task.swift
Outdated
/// If the task is canceled internally --- | ||
/// for example, by checking for cancellation | ||
/// and throwing a specific error, or by using `checkCancellation` --- | ||
/// this method rethrows the error that was thrown by the 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.
same notes as above; this wording is wrong nowadays I believe
stdlib/public/Concurrency/Task.swift
Outdated
/// The task that this handle refers to might check for cancellation --- | ||
/// however, since this method is nonthrowing, | ||
/// the task can't throw `CancellationError` and needs to use another method | ||
/// like returning `nil` instead. |
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.
sounds good
stdlib/public/Concurrency/Task.swift
Outdated
@@ -277,6 +292,8 @@ extension Task { | |||
/// Flags for schedulable jobs. | |||
/// | |||
/// This is a port of the C++ FlagSet. | |||
/// ◊TR: What is FlagSet? | |||
/// ◊TR: Why does that matter to users that it matches something in C++? |
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.
This is an internal type, so we don't need to document it in depth for end users; it only matters to us, implementors of the lib that this must align with the C++ Type of the same values
stdlib/public/Concurrency/Task.swift
Outdated
/// Avoid using a detached task unless it isn't possible | ||
/// to model the operation using structured concurrency features like child tasks. | ||
/// Child tasks inherit the parent task's priority, | ||
/// task-local storage, and deadlines, |
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.
sadly we're not shipping deadlines this year
/// task-local storage, and deadlines, | |
/// task-local storage, |
they'll come in the future though :)
/// Child tasks inherit the parent task's priority, | ||
/// task-local storage, and deadlines, | ||
/// and canceling a parent task automatically cancels all of its child tasks. | ||
/// You need to handle these considerations manually with a detached 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.
good to explain what people are losing here! 👍
stdlib/public/Concurrency/Task.swift
Outdated
/// A detached task runs to completion | ||
/// unless it is explicitly canceled by calling the `Task.Handle.cancel()` method. | ||
/// Specifically, dropping a detached task's handle | ||
/// doesn't cancel that 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.
This wording is tricky; the task always runs to completion, but the cancellation may cause it to return early or throw etc.
But since cancellation is cooperative, this means that if the task never checks cancellation, it'd finish as if nothing ever cancelled it (this is by design).
So we should not say that cancellation prevents running to completion, we can rather say that it impacts the task and how it will return.
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.
Another note: cancellation does not prevent the task from being scheduled either, so it really is just informational to the task
stdlib/public/Concurrency/Task.swift
Outdated
/// A detached task always runs to completion unless it is explicitly cancelled. | ||
/// Specifically, dropping a detached tasks `Task.Handle` does _not_ automatically | ||
/// cancel given task. | ||
/// If the operation throws an error, this method rethrows that error. |
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.
This is quite not true, detach
is not throwing.
What happens is that if the operation throws, then the returned Task.Handle's try await get()
will throw. This is "kind of" like rethrowing but I don't think we can use that word here since "rethrows" is a specific thing in Swift.
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 changing "rethrows" to "propagates", to match what we call this in TSPL where the error makes its way back through a series of calls to throwing functions.
https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html#ID510
/// A detached task runs to completion | ||
/// unless it is explicitly canceled by calling the `Task.Handle.cancel()` method. | ||
/// Specifically, dropping a detached task's handle | ||
/// doesn't cancel that 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.
same concern on wording as above
stdlib/public/Concurrency/Task.swift
Outdated
/// Suspends the current task for _at least_ the given duration | ||
/// in nanoseconds. | ||
/// Suspends the current task, | ||
/// and wait for at least the given duration before resuming it. |
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.
tenses are mixed up? Suspends + waits
or suspend + wait
?
stdlib/public/Concurrency/Task.swift
Outdated
/// Suspends the current task for _at least_ the given duration | ||
/// in nanoseconds. | ||
/// Suspends the current task, | ||
/// and wait for at least the given duration before resuming it. |
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.
can we bring back the emphasis on "at least"? It's important to highlight that it's not an exact sleep time guarantee
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 need to check editorial guidelines, but I think we avoid emphasis in abstracts because they show up on their own in places like navigation and Xcode quick help.
/// Note that if this task is the highest-priority task in the system, | ||
/// the executor immediately resumes execution of the same task. | ||
/// As such, | ||
/// this method isn't necessarily a way to avoid resource starvation. |
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.
(a bit weird formatting in this comment?)
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.
What looks weird to you? If it's just the placement of line breaks, that's a convention we've used elsewhere in documentation to reduce noise in diffs. Instead of breaking at the 80 column mark and rewrapping, we break lines at clause and sentence boundaries, so they don't need to be rewrapped after revising.
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 see, that's a new technique to me... A bit weird to be honest since the comments are often read in the source too hm
stdlib/public/Concurrency/Task.swift
Outdated
/// the unsafe task handle passed to the closure is always be non-nil | ||
/// because an asynchronous function always runs in the context of a task. | ||
/// However if you call this function from the body of a synchronous function, | ||
/// the unsafe task handle is `nil` |
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 unsafe task handle is `nil` | |
/// the unsafe task handle may be `nil` |
feels more right somehow...? maybe not?
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 I call this method from sync code, and my backtrace was all sync back up the call stack, when would this return a non-nil value?
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 would be nil then -- if the entire callstack is outside of swift concurrency APIs
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.
Thanks. If it's always nil
in that case, I'll avoid writing "may be":
/// However if you call this function from the body of a synchronous function,
/// and that function isn't executing in the context of any task,
/// the unsafe task handle is `nil`.
/// An `UnsafeCurrentTask` should not be stored for "later" access. | ||
/// To get an instance of `UnsafeCurrentTask` for the current task, | ||
/// call the `withUnsafeCurrentTask(body:)` method. | ||
/// Don't try to store an unsafe task handle |
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.
maybe just
/// Don't try to store an unsafe task handle | |
/// Don't store an unsafe task handle |
?
(reading a book on writing style recently and it left an impression on me to cut unnecessary words :-))
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.
Maybe this phrasing doesn't work as well in reference here. In TSPL, we use "don't try to do X" to describe something that won't work. Here, you think you can store an unsafe task handle, but you really can't store it. I think the counterpoint to my argument is that you can store it — just that storing the handle doesn't let you do anything useful.
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.
storing it is actively unsafe and would lead to undefined behavior, so the stronger wording would be good hm
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.
Removed "try to" in commit 72322eb.
stdlib/public/Concurrency/Task.swift
Outdated
/// generally safe to be invoked from any task/thread. | ||
/// ◊TR: Which ones, exactly, is that? "Generally" isn't good enough here. |
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 can remove the "generally" the trick is "if an API exists on Task, it is also safe on UnsafeCurrentTask from other contexts; If it only exists on UnsafeCurrentTask, it is unsafe to call it from other tasks"
Folding the example @ktoso suggested into the existing code, and removing content that was repeated here from next() but isn't relevant.
2f12b74
to
d8e5d30
Compare
@ktoso I rebased these changes on top of the Swift 5.5 release branch, and changed the PR to target that branch. If you want to avoid re-reading the diff, you can look at the commits from |
This was removed in commit f748e8d. Found this error and verified it's the only change outside of doc comments by running: git diff release/5.5 | grep -v '^+ *///\|- *///' | grep -v '^@@' | grep -v '^ '
Oh sorry I missed the updates! Checking now :) Please ping me on chat if I miss something for a few days :) |
For rdar://77330408
@swift-ci please smoke test |
Conflicts in stdlib/public/Concurrency/TaskGroup.swift from async/spawn renaming. Resolved by copying the docs to the new symbols from their old names.
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.
Looks good, thanks @amartini51 !
Conflicts were just @Availability marker changes.
@swift-ci please smoke test |
@swift-ci Please test and merge. |
Technical and non-clean-up corrections:
Task.priority
because it's an instance variable. By definition, youcan't access it without some current task.
detach(priority:operation:)
to note thatit's for non-throwing operations, since it uses
Never
as itsTask.Handle
failure type.TaskGroup
,since its child tasks can't be throwing.
in the declaration. The rendered documentation already provides an
affordance to navigate to a type in that context.
Style changes, per Apple Style Guide and reference style guidelines:
are short enough that they doesn't need headings.
of describing them as if they were methods.
parentheses. For example,
spawn(overridingPriority:operation:)
rather than just
spawn
orspawn()
.Other changes:
signs (=), to match markup elsewhere in the standard library.
and to make it possible to look up the documentation for that method
if the reader doesn't already know what type it's on.
For rdar://76086293