Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions stdlib/public/Concurrency/Task+init.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,14 @@ extension Task where Failure == ${FAILURE_TYPE} {
% end
///
% if IS_DETACHED:
/// Don't use a detached unstructured task if it's possible
/// to model the operation using structured concurrency features like child tasks.
/// Child tasks inherit the parent task's priority and task-local storage,
/// and canceling a parent task automatically cancels all of its child tasks.
/// You need to handle these considerations manually with a detached task.
/// In most cases, an "attached" unstructured task (created via `Task.init`)
Copy link
Contributor

Choose a reason for hiding this comment

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

lazy question since i did not yet search myself, but is this "attached" terminology used anywhere else? i've personally never seen it AFAIK and it seems a bit odd. to what is the Task 'attached'?

Copy link
Author

@jaredsinclair jaredsinclair Dec 5, 2025

Choose a reason for hiding this comment

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

I haven't seen it either. I have put it in quotes here ("attached") to connote the fact that it isn't an accepted term but is being used in a casual manner for rhetorical purposes. I do think, however, that the existence of Task.detached implies that an "attached" alternative must exist. I am also hoping here that by directly stating that Task.init is the alternative, it should help clarify for the reader that there isn't, say, a Task.attached someplace they've yet to stumble upon.

Copy link
Author

Choose a reason for hiding this comment

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

I would be happy to change "attached" to non-detached if that would be preferable.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would be happy to change "attached" to non-detached if that would be preferable.

just my personal opinion, but both of those seem sort of 'overly jargony' and i'd be inclined to just avoid them if possible.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should not be introducing new terms like that. Please don’t call tasks “attached” that’s not a word we ever use anywhere.

these are called “unstructured tasks”.

/// is a more appropriate choice than a detached unstructured task. Attached
/// tasks inherit the priority and task-local storage from the enclosing task
/// during which they are created. You need to handle these considerations
/// manually with a detached task.
///
/// A detached `Task` has the same cancellation mechanism as any other `Task`;
/// it can only become cancelled via an explicit `cancel()` method call.
% end
///
/// You need to keep a reference to the task
Expand Down
55 changes: 47 additions & 8 deletions stdlib/public/Concurrency/Task.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ import Swift
/// you give up the ability
/// to wait for that task's result or cancel the task.
///
/// To support operations on the current task,
/// which can be either a detached task or child task,
/// `Task` also exposes class methods like `yield()`.
/// To support operations on the current task, `Task` also exposes class methods
/// like `yield()`. The current task can be either an unstructured `Task`, a
/// detached unstructured `Task`, or a child task (an `async let` or a task group).
/// Because these methods are asynchronous,
/// they're always invoked as part of an existing task.
///
Expand Down Expand Up @@ -77,6 +77,38 @@ import Swift
/// This reflects the fact that a task can be canceled for many reasons,
/// and additional reasons can accrue during the cancellation process.
///
/// A `Task` becomes cancelled when its `cancel()` method is called on it. There
/// is no other mechanism by which a `Task` can become cancelled. This is true
Copy link
Contributor

Choose a reason for hiding this comment

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

Thats very sneaky wording I’m not comfortable with… its true for specifically an unstructured task but the wording here isn’t clear enough, it may sound like you mean “any task”

/// even of a `Task` instance which is created inside the operation of another
/// `Task`. You can manually propagate cancellation from an enclosing `Task` to
/// another task created during its operation via `withTaskCancellationHandler(operation:onCancel:isolation:)`:
Copy link
Contributor

Choose a reason for hiding this comment

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

These should be double quotes to cause docc links

Copy link
Contributor

Choose a reason for hiding this comment

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

I don’t think we should be giving complete examples here, we should link to the api which then has more examples

///
/// ```swift
/// let outer = Task {
/// let inner = Task {
/// // The next statement will not throw an error unless cancellation of
/// // the `outer` task is manually propagated to the `inner` task via a
/// // cancellation handler:
/// try Task.checkCancellation()
/// return try await work()
/// }
/// return try await withTaskCancellationHandler {
/// try await inner.value
/// } onCancel: {
/// inner.cancel()
/// }
/// }
///
/// // When `outer` becomes cancelled, the `onCancel` handler above will run,
/// // causing `inner` to become cancelled, too.
/// outer.cancel()
/// ```
///
/// Any instance of `Task` that you initialize for yourself is a top-level,
/// _unstructured_ task. For more information about the implications of
/// unstructured tasks, read the ["Unstructured Concurrency"](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/#Unstructured-Concurrency)
/// portion of the Swift Programming Language book.
///
/// ### Task closure lifetime
/// Tasks are initialized by passing a closure containing the code that will be executed by a given task.
///
Expand Down Expand Up @@ -200,9 +232,16 @@ extension Task {
///
/// - It flags the task as canceled.
/// - It causes any active cancellation handlers on the task to run, once.
/// - It cancels any child tasks and task groups of the task, including
/// those created in the future. If those tasks have cancellation handlers,
/// they also are triggered.
/// - It cancels any child tasks (lowercase-t tasks, like `async let` and
Copy link
Contributor

Choose a reason for hiding this comment

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

We should not be calling things “lowercase tasks” that’s wording that’s not used by swift documentation anywhere

/// other forms of structured concurrency, not capital-t `Task` instances)
/// and task groups of the task, including those created in the future. If
/// those tasks have cancellation handlers, they also are triggered.
///
/// Cancelling a `Task` does _not_ cancel other instances of `Task` that were
/// or will be created during its operation. Those other instances will not
/// become cancelled until and unless they are explicitly cancelled via
/// their `cancel()` methods being called on them. Read the "Task Cancellation"
/// section of the `Task` documentation for more information.
///
/// Task cancellation is cooperative and idempotent.
///
Expand Down Expand Up @@ -671,8 +710,8 @@ extension Task where Success == Never, Failure == Never {
/// and save it for long-term use.
/// To query the current task without saving a reference to it,
/// use properties like `currentPriority`.
/// If you need to store a reference to a task,
/// create an unstructured task using `Task.detached(priority:operation:)` instead.
/// If you need to store a reference to a task, create an unstructured task using
/// `Task.init(name:priority:operation:)` or `Task.detached(priority:operation:)` instead.
///
/// - Parameters:
/// - body: A closure that takes an `UnsafeCurrentTask` parameter.
Expand Down