Skip to content

Commit b0d44a1

Browse files
authored
Merge pull request #68482 from ktoso/pick-task-lifetime-docs
2 parents 96969a6 + 4ee8020 commit b0d44a1

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

stdlib/public/Concurrency/Task.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,67 @@ import Swift
7676
/// like the reason for cancellation.
7777
/// This reflects the fact that a task can be canceled for many reasons,
7878
/// and additional reasons can accrue during the cancellation process.
79+
///
80+
/// ### Task closure lifetime
81+
/// Tasks are initialized by passing a closure containing the code that will be executed by a given task.
82+
///
83+
/// After this code has run to completion, the task has completed, resulting in either
84+
/// a failure or result value, this closure is eagerly released.
85+
///
86+
/// Retaining a task object doesn't indefinitely retain the closure,
87+
/// because any references that a task holds are released
88+
/// after the task completes.
89+
/// Consequently, tasks rarely need to capture weak references to values.
90+
///
91+
/// For example, in the following snippet of code it is not necessary to capture the actor as `weak`,
92+
/// because as the task completes it'll let go of the actor reference, breaking the
93+
/// reference cycle between the Task and the actor holding it.
94+
///
95+
/// ```
96+
/// struct Work: Sendable {}
97+
///
98+
/// actor Worker {
99+
/// var work: Task<Void, Never>?
100+
/// var result: Work?
101+
///
102+
/// deinit {
103+
/// assert(work != nil)
104+
/// // even though the task is still retained,
105+
/// // once it completes it no longer causes a reference cycle with the actor
106+
///
107+
/// print("deinit actor")
108+
/// }
109+
///
110+
/// func start() {
111+
/// work = Task {
112+
/// print("start task work")
113+
/// try? await Task.sleep(for: .seconds(3))
114+
/// self.result = Work() // we captured self
115+
/// print("completed task work")
116+
/// // but as the task completes, this reference is released
117+
/// }
118+
/// // we keep a strong reference to the task
119+
/// }
120+
/// }
121+
/// ```
122+
///
123+
/// And using it like this:
124+
///
125+
/// ```
126+
/// await Actor().start()
127+
/// ```
128+
///
129+
/// Note that there is nothing, other than the Task's use of `self` retaining the actor,
130+
/// And that the start method immediately returns, without waiting for the unstructured `Task` to finish.
131+
/// So once the task completes and its the closure is destroyed, the strong reference to the "self" of the actor is also released allowing the actor to deinitialize as expected.
132+
///
133+
/// Therefore, the above call will consistently result in the following output:
134+
///
135+
/// ```
136+
/// start task work
137+
/// completed task work
138+
/// deinit actor
139+
/// ```
79140
@available(SwiftStdlib 5.1, *)
80141
@frozen
81142
public struct Task<Success: Sendable, Failure: Error>: Sendable {

0 commit comments

Comments
 (0)