Skip to content

Commit e0eb940

Browse files
committed
[Concurrency] Remove Task.current because it prevents task-local alloc swiftlang#36993
1 parent 5cb9227 commit e0eb940

File tree

4 files changed

+45
-148
lines changed

4 files changed

+45
-148
lines changed

stdlib/public/Concurrency/Task.swift

Lines changed: 20 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -33,36 +33,11 @@ import Swift
3333
/// unless implementing a scheduler.
3434
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
3535
public struct Task {
36-
internal let _task: Builtin.NativeObject
37-
38-
// May only be created by the standard library.
39-
internal init(_ task: Builtin.NativeObject) {
40-
self._task = task
41-
}
42-
}
43-
44-
// ==== Current Task -----------------------------------------------------------
45-
46-
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
47-
extension Task {
48-
49-
/// Returns 'current' `Task` instance, representing the task from within which
50-
/// this function was called.
51-
///
52-
/// All functions available on the Task
53-
public static var current: Task? {
54-
guard let _task = _getCurrentAsyncTask() else {
55-
return nil
56-
}
57-
58-
// FIXME: This retain seems pretty wrong, however if we don't we WILL crash
59-
// with "destroying a task that never completed" in the task's destroy.
60-
// How do we solve this properly?
61-
Builtin.retain(_task)
62-
63-
return Task(_task)
64-
}
65-
36+
// Task instances should not be used as they could be stored away,
37+
// and sine some tasks may be task-local allocated such stored away
38+
// references could point at already destroyed task memory (!).
39+
//
40+
// If necessary to obtain a task instance, please use withUnsafeCurrentTask.
6641
}
6742

6843
// ==== Task Priority ----------------------------------------------------------
@@ -78,18 +53,12 @@ extension Task {
7853
/// - SeeAlso: `Task.priority`
7954
public static var currentPriority: Priority {
8055
withUnsafeCurrentTask { task in
81-
task?.priority ?? Priority.default
82-
}
83-
}
56+
guard let task = task else {
57+
return Priority.default
58+
}
8459

85-
/// Returns the `current` task's priority.
86-
///
87-
/// If no current `Task` is available, returns `Priority.default`.
88-
///
89-
/// - SeeAlso: `Task.Priority`
90-
/// - SeeAlso: `Task.currentPriority`
91-
public var priority: Priority {
92-
getJobFlags(_task).priority
60+
return getJobFlags(task._task).priority
61+
}
9362
}
9463

9564
/// Task priority may inform decisions an `Executor` makes about how and when
@@ -150,18 +119,22 @@ extension Task {
150119
/// i.e. the task will run regardless of the handle still being present or not.
151120
/// Dropping a handle however means losing the ability to await on the task's result
152121
/// and losing the ability to cancel it.
122+
///
123+
// Implementation notes:
124+
// A task handle can ONLY be obtained for a detached task, and as such shares
125+
// no lifetime concerns with regards to holding and storing the `_task` with
126+
// the `Task` type, which would have also be obtainable for any task, including
127+
// a potentially task-local allocated one. I.e. it is always safe to store away
128+
// a Task.Handle, yet the same is not true for the "current task" which may be
129+
// a async-let created task, at risk of getting destroyed while the reference
130+
// lingers around.
153131
public struct Handle<Success, Failure: Error>: Sendable {
154132
internal let _task: Builtin.NativeObject
155133

156134
internal init(_ task: Builtin.NativeObject) {
157135
self._task = task
158136
}
159137

160-
/// Returns the `Task` that this handle refers to.
161-
public var task: Task {
162-
Task(_task)
163-
}
164-
165138
/// Wait for the task to complete, returning (or throwing) its result.
166139
///
167140
/// ### Priority
@@ -253,23 +226,6 @@ extension Task.Handle: Equatable {
253226
}
254227
}
255228

256-
// ==== Conformances -----------------------------------------------------------
257-
258-
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
259-
extension Task: Hashable {
260-
public func hash(into hasher: inout Hasher) {
261-
UnsafeRawPointer(Builtin.bridgeToRawPointer(_task)).hash(into: &hasher)
262-
}
263-
}
264-
265-
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
266-
extension Task: Equatable {
267-
public static func ==(lhs: Self, rhs: Self) -> Bool {
268-
UnsafeRawPointer(Builtin.bridgeToRawPointer(lhs._task)) ==
269-
UnsafeRawPointer(Builtin.bridgeToRawPointer(rhs._task))
270-
}
271-
}
272-
273229
// ==== Job Flags --------------------------------------------------------------
274230

275231
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
@@ -603,7 +559,7 @@ extension Task {
603559
extension Task {
604560

605561
@available(*, deprecated, message: "`Task.unsafeCurrent` was replaced by `withUnsafeCurrentTask { task in ... }`, and will be removed soon.")
606-
public static var unsafeCurrent: UnsafeCurrentTask? {
562+
public static var unsafeCurrent: UnsafeCurrentTask? { // TODO: remove as soon as possible
607563
guard let _task = _getCurrentAsyncTask() else {
608564
return nil
609565
}
@@ -667,14 +623,6 @@ public struct UnsafeCurrentTask {
667623
self._task = task
668624
}
669625

670-
/// Returns `Task` representing the same asynchronous context as this 'UnsafeCurrentTask'.
671-
///
672-
/// Operations on `Task` (unlike `UnsafeCurrentTask`) are safe to be called
673-
/// from any other task (or thread).
674-
public var task: Task {
675-
Task(_task)
676-
}
677-
678626
/// Returns `true` if the task is cancelled, and should stop executing.
679627
///
680628
/// - SeeAlso: `checkCancellation()`

stdlib/public/Concurrency/TaskCancellation.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,13 @@ extension Task {
6565
///
6666
/// - SeeAlso: `checkCancellation()`
6767
public var isCancelled: Bool {
68-
_taskIsCancelled(_task)
68+
withUnsafeCurrentTask { task in
69+
guard let task = task else {
70+
return false
71+
}
72+
73+
return _taskIsCancelled(task._task)
74+
}
6975
}
7076

7177
/// Check if the task is cancelled and throw an `CancellationError` if it was.

stdlib/public/Concurrency/TaskLocal.swift

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,22 @@ extension Task {
7777
public static func local<Key>(
7878
_ keyPath: KeyPath<TaskLocalValues, Key>
7979
) -> Key.Value where Key: TaskLocalKey {
80-
guard let task = Task.current else {
81-
return Key.defaultValue
80+
withUnsafeCurrentTask { current in
81+
guard let task = current else {
82+
return Key.defaultValue
83+
}
84+
85+
let value = _taskLocalValueGet(
86+
task._task, keyType: Key.self, inheritance: Key.inherit.rawValue)
87+
guard let rawValue = value else {
88+
return Key.defaultValue
89+
}
90+
91+
// Take the value; The type should be correct by construction
92+
let storagePtr =
93+
rawValue.bindMemory(to: Key.Value.self, capacity: 1)
94+
return UnsafeMutablePointer<Key.Value>(mutating: storagePtr).pointee
8295
}
83-
84-
let value = _taskLocalValueGet(
85-
task._task, keyType: Key.self, inheritance: Key.inherit.rawValue)
86-
guard let rawValue = value else {
87-
return Key.defaultValue
88-
}
89-
90-
// Take the value; The type should be correct by construction
91-
let storagePtr =
92-
rawValue.bindMemory(to: Key.Value.self, capacity: 1)
93-
return UnsafeMutablePointer<Key.Value>(mutating: storagePtr).pointee
9496
}
9597

9698
/// Bind the task local key to the given value for the scope of the `body` function.
@@ -106,7 +108,9 @@ extension Task {
106108
boundTo value: Key.Value,
107109
operation: () async throws -> BodyResult
108110
) async rethrows -> BodyResult where Key: TaskLocalKey {
109-
let _task = Task.current!._task // !-safe, guaranteed to have task available inside async function
111+
let _task = withUnsafeCurrentTask { current in
112+
current!._task // !-safe, guaranteed to have task available inside async function
113+
}
110114

111115
_taskLocalValuePush(_task, keyType: Key.self, value: value)
112116
defer { _taskLocalValuePop(_task) }

test/Concurrency/Runtime/async_task_equals_hashCode.swift

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

0 commit comments

Comments
 (0)