Skip to content

Commit 27bf2c9

Browse files
ktosorjmccall
authored andcommitted
[Concurrency] Remove Task.current because it prevents task-local alloc #36993
1 parent 1a31fba commit 27bf2c9

File tree

3 files changed

+27
-160
lines changed

3 files changed

+27
-160
lines changed

stdlib/public/Concurrency/Task.swift

Lines changed: 20 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -56,54 +56,11 @@ import Swift
5656
/// and additional reasons can accrue during the cancellation process.
5757
@available(SwiftStdlib 5.5, *)
5858
public struct Task {
59-
internal let _task: Builtin.NativeObject
60-
61-
// May only be created by the standard library.
62-
internal init(_ task: Builtin.NativeObject) {
63-
self._task = task
64-
}
65-
}
66-
67-
// ==== Current Task -----------------------------------------------------------
68-
69-
@available(SwiftStdlib 5.5, *)
70-
extension Task {
71-
72-
/// Returns the task that this code runs on,
73-
/// or `nil` when you access this property outside of any task.
74-
///
75-
/// If you read this property from the context of an asynchronous function or closure,
76-
/// the current task is non-nil.
77-
/// In a synchronous context,
78-
/// this property's value depends on whether the synchronous operation was
79-
/// called from an asynchronous context.
80-
/// For example:
81-
///
82-
/// func hello() {
83-
/// if Task.current == nil { print("Nil") }
84-
/// else { print("Not nil") }
85-
/// }
86-
///
87-
/// func asynchronous() async { hello() }
88-
///
89-
/// In the code above,
90-
/// because `hello()` is called by an asynchronous function,
91-
/// it prints "Not nil".
92-
///
93-
@available(*, deprecated, message: "`Task.current` has been deprecated and will be removed, use static functions on Task instead.")
94-
public static var current: Task? {
95-
guard let _task = _getCurrentAsyncTask() else {
96-
return nil
97-
}
98-
99-
// FIXME: This retain seems pretty wrong, however if we don't we WILL crash
100-
// with "destroying a task that never completed" in the task's destroy.
101-
// How do we solve this properly?
102-
Builtin.retain(_task)
103-
104-
return Task(_task)
105-
}
106-
59+
// Task instances should not be used as they could be stored away,
60+
// and sine some tasks may be task-local allocated such stored away
61+
// references could point at already destroyed task memory (!).
62+
//
63+
// If necessary to obtain a task instance, please use withUnsafeCurrentTask.
10764
}
10865

10966
// ==== Task Priority ----------------------------------------------------------
@@ -132,14 +89,6 @@ extension Task {
13289
}
13390
}
13491

135-
/// The task's priority.
136-
///
137-
/// - SeeAlso: `Task.currentPriority`
138-
@available(*, deprecated, message: "Storing `Task` instances has been deprecated, and as such instance functions on Task are deprecated and will be removed soon. Use the static 'Task.currentPriority' instead.")
139-
public var priority: Priority {
140-
getJobFlags(_task).priority ?? .default
141-
}
142-
14392
/// The priority of a task.
14493
///
14594
/// The executor determines how priority information affects the way tasks are scheduled.
@@ -208,23 +157,26 @@ extension Task {
208157
///
209158
/// You can use a task's handle to wait for its result or cancel the task.
210159
///
211-
/// It isn't a programming error to discard a task's handle without awaiting or canceling the task.
212-
/// A task runs regardless of whether you still have its handle stored somewhere.
213-
/// However, if you discard a task's handle, you give up the ability
214-
/// to wait for that task's result or cancel the task.
160+
/// It is not a programming error to drop a handle without awaiting or cancelling it,
161+
/// i.e. the task will run regardless of the handle still being present or not.
162+
/// Dropping a handle however means losing the ability to await on the task's result
163+
/// and losing the ability to cancel it.
164+
///
165+
// Implementation notes:
166+
// A task handle can ONLY be obtained for a detached task, and as such shares
167+
// no lifetime concerns with regards to holding and storing the `_task` with
168+
// the `Task` type, which would have also be obtainable for any task, including
169+
// a potentially task-local allocated one. I.e. it is always safe to store away
170+
// a Task.Handle, yet the same is not true for the "current task" which may be
171+
// a async-let created task, at risk of getting destroyed while the reference
172+
// lingers around.
215173
public struct Handle<Success, Failure: Error>: Sendable {
216174
internal let _task: Builtin.NativeObject
217175

218176
internal init(_ task: Builtin.NativeObject) {
219177
self._task = task
220178
}
221179

222-
/// The task that this handle refers to.
223-
@available(*, deprecated, message: "Storing `Task` instances has been deprecated and will be removed soon.")
224-
public var task: Task {
225-
Task(_task)
226-
}
227-
228180
/// Wait for the task to complete, returning its result or throw an error.
229181
///
230182
/// If the task hasn't completed, its priority increases to the
@@ -311,23 +263,6 @@ extension Task.Handle: Equatable {
311263
}
312264
}
313265

314-
// ==== Conformances -----------------------------------------------------------
315-
316-
@available(SwiftStdlib 5.5, *)
317-
extension Task: Hashable {
318-
public func hash(into hasher: inout Hasher) {
319-
UnsafeRawPointer(Builtin.bridgeToRawPointer(_task)).hash(into: &hasher)
320-
}
321-
}
322-
323-
@available(SwiftStdlib 5.5, *)
324-
extension Task: Equatable {
325-
public static func ==(lhs: Self, rhs: Self) -> Bool {
326-
UnsafeRawPointer(Builtin.bridgeToRawPointer(lhs._task)) ==
327-
UnsafeRawPointer(Builtin.bridgeToRawPointer(rhs._task))
328-
}
329-
}
330-
331266
// ==== Job Flags --------------------------------------------------------------
332267

333268
@available(SwiftStdlib 5.5, *)
@@ -775,7 +710,7 @@ extension Task {
775710
extension Task {
776711

777712
@available(*, deprecated, message: "`Task.unsafeCurrent` was replaced by `withUnsafeCurrentTask { task in ... }`, and will be removed soon.")
778-
public static var unsafeCurrent: UnsafeCurrentTask? {
713+
public static var unsafeCurrent: UnsafeCurrentTask? { // TODO: remove as soon as possible
779714
guard let _task = _getCurrentAsyncTask() else {
780715
return nil
781716
}
@@ -854,20 +789,7 @@ public struct UnsafeCurrentTask {
854789
self._task = task
855790
}
856791

857-
/// The current task,
858-
/// represented in a way that's safe to store for later use.
859-
///
860-
/// Operations on an instance of `Task` are safe to call from any other task,
861-
/// unlike `UnsafeCurrentTask`.
862-
@available(*, deprecated, message: "Storing `Task` instances has been deprecated and will be removed soon.")
863-
public var task: Task {
864-
Task(_task)
865-
}
866-
867-
/// A Boolean value that indicates whether the current task was canceled.
868-
///
869-
/// After the value of this property is `true`, it remains `true` indefinitely.
870-
/// There is no way to uncancel the operation.
792+
/// Returns `true` if the task is cancelled, and should stop executing.
871793
///
872794
/// - SeeAlso: `checkCancellation()`
873795
public var isCancelled: Bool {

stdlib/public/Concurrency/TaskCancellation.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,13 @@ extension Task {
6666
/// - SeeAlso: `checkCancellation()`
6767
@available(*, deprecated, message: "Storing `Task` instances has been deprecated and will be removed soon. Use the static 'Task.isCancelled' instead.")
6868
public var isCancelled: Bool {
69-
_taskIsCancelled(_task)
69+
withUnsafeCurrentTask { task in
70+
guard let task = task else {
71+
return false
72+
}
73+
74+
return _taskIsCancelled(task._task)
75+
}
7076
}
7177

7278
/// Throws a cancellation error if the current task was canceled.

test/Concurrency/Runtime/async_task_equals_hashCode.swift

Lines changed: 0 additions & 61 deletions
This file was deleted.

0 commit comments

Comments
 (0)