Skip to content

Commit b802e5b

Browse files
authored
Merge pull request swiftlang#36126 from ktoso/wip-task-tl
[Concurrency] Taks APIs fully implemented via ActiveTask internal TL
2 parents 288a0db + dd9a9a6 commit b802e5b

14 files changed

+123
-54
lines changed

include/swift/Runtime/Concurrency.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ void swift_task_dealloc(AsyncTask *task, void *ptr);
107107
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
108108
void swift_task_cancel(AsyncTask *task);
109109

110+
/// Get 'active' AsyncTask, depending on platform this may use thread local storage.
111+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
112+
AsyncTask* swift_task_get_active();
113+
110114
/// Escalate the priority of a task and all of its child tasks.
111115
///
112116
/// This can be called from any thread.

stdlib/public/Concurrency/Actor.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(
134134

135135
} // end anonymous namespace
136136

137+
AsyncTask*
138+
swift::swift_task_get_active() {
139+
return ActiveTask::get();
140+
}
141+
137142
void swift::swift_job_run(Job *job, ExecutorRef executor) {
138143
ExecutorTrackingInfo trackingInfo;
139144
trackingInfo.enterAndShadow(executor);

stdlib/public/Concurrency/Task.swift

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,8 @@ extension Task {
4949
///
5050
/// All functions available on the Task
5151
// TODO: once we can have async properties land make this computed property
52-
@available(*, deprecated, message: "Please use Builtin.getCurrentAsyncTask() or Task.__unsafeCurrentAsync() until this function becomes implemented.")
53-
public static func current(file: StaticString = #file, line: UInt = #line) async -> Task {
54-
fatalError("Task.current() is not implemented yet!", file: file, line: line)
55-
Task.unsafeCurrent!.task // !-safe, guaranteed to have a Task available within an async function.
52+
public static func current() async -> Task {
53+
Task.unsafeCurrent!.task // !-safe, we are guaranteed to have a task available within an async function
5654
}
5755

5856
}
@@ -67,7 +65,6 @@ extension Task {
6765
///
6866
/// - SeeAlso: `Task.Priority`
6967
/// - SeeAlso: `Task.priority`
70-
@available(*, deprecated, message: "Not implemented yet, until unsafeCurrent is ready. Please use Task.__unsafeCurrentAsync().priority instead.")
7168
public static var currentPriority: Priority {
7269
Task.unsafeCurrent?.priority ?? Priority.default
7370
}
@@ -140,7 +137,7 @@ extension Task {
140137
/// Dropping a handle however means losing the ability to await on the task's result
141138
/// and losing the ability to cancel it.
142139
public struct Handle<Success, Failure: Error> {
143-
private let _task: Builtin.NativeObject
140+
internal let _task: Builtin.NativeObject
144141

145142
internal init(_ task: Builtin.NativeObject) {
146143
self._task = task
@@ -510,22 +507,17 @@ extension Task {
510507
/// asynchronous function present in this functions call stack.
511508
///
512509
/// The returned value must not be accessed from tasks other than the current one.
513-
@available(*, deprecated, message: "Not implemented yet, use Builtin.getCurrentAsyncTask() or Task.___unsafeCurrentAsync() until this function is implemented.")
514510
public static var unsafeCurrent: UnsafeCurrentTask? {
515-
// FIXME: rdar://70546948 implement this once getCurrentAsyncTask can be called from sync funcs
516-
// guard let _task = Builtin.getCurrentAsyncTask() else {
517-
// return nil
518-
// }
519-
// return UnsafeCurrentTask(_task)
520-
fatalError("\(#function) is not implemented yet")
511+
guard let _task = _getActiveAsyncTask() else {
512+
return nil
513+
}
514+
// FIXME: This retain seems pretty wrong, however if we don't we WILL crash
515+
// with "destroying a task that never completed" in the task's destroy.
516+
// How do we solve this properly?
517+
_swiftRetain(_task)
518+
return UnsafeCurrentTask(_task)
521519
}
522520

523-
@available(*, deprecated, message: "This will be removed, and replaced by unsafeCurrent().", renamed: "unsafeCurrent()")
524-
public static func __unsafeCurrentAsync() async -> UnsafeCurrentTask {
525-
let task = Builtin.getCurrentAsyncTask()
526-
_swiftRetain(task)
527-
return UnsafeCurrentTask(task)
528-
}
529521
}
530522

531523
/// An *unsafe* 'current' task handle.
@@ -542,7 +534,7 @@ extension Task {
542534
/// and most certainly will break invariants in other places of the program
543535
/// actively running on this task.
544536
public struct UnsafeCurrentTask {
545-
private let _task: Builtin.NativeObject
537+
internal let _task: Builtin.NativeObject
546538

547539
// May only be created by the standard library.
548540
internal init(_ task: Builtin.NativeObject) {
@@ -576,8 +568,24 @@ public struct UnsafeCurrentTask {
576568

577569
}
578570

571+
extension UnsafeCurrentTask: Hashable {
572+
public func hash(into hasher: inout Hasher) {
573+
UnsafeRawPointer(Builtin.bridgeToRawPointer(_task)).hash(into: &hasher)
574+
}
575+
}
576+
577+
extension UnsafeCurrentTask: Equatable {
578+
public static func ==(lhs: Self, rhs: Self) -> Bool {
579+
UnsafeRawPointer(Builtin.bridgeToRawPointer(lhs._task)) ==
580+
UnsafeRawPointer(Builtin.bridgeToRawPointer(rhs._task))
581+
}
582+
}
583+
579584
// ==== Internal ---------------------------------------------------------------
580585

586+
@_silgen_name("swift_task_get_active")
587+
func _getActiveAsyncTask() -> Builtin.NativeObject?
588+
581589
@_silgen_name("swift_task_getJobFlags")
582590
func getJobFlags(_ task: Builtin.NativeObject) -> Task.JobFlags
583591

stdlib/public/Concurrency/TaskLocal.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,13 @@ extension Task {
7070
/// - Returns: the value bound to the key, or its default value it if was not
7171
/// bound in the current (or any parent) tasks.
7272
public static func local<Key>(_ keyPath: KeyPath<TaskLocalValues, Key>)
73-
async -> Key.Value where Key: TaskLocalKey {
74-
let task = Builtin.getCurrentAsyncTask()
73+
-> Key.Value where Key: TaskLocalKey {
74+
guard let task = Task.unsafeCurrent else {
75+
return Key.defaultValue
76+
}
7577

76-
let value = _taskLocalValueGet(task, keyType: Key.self, inheritance: Key.inherit.rawValue)
78+
let value = _taskLocalValueGet(
79+
task._task, keyType: Key.self, inheritance: Key.inherit.rawValue)
7780
guard let rawValue = value else {
7881
return Key.defaultValue
7982
}
Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,57 @@
1-
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library) | %FileCheck %s --dump-input=always
1+
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always
2+
23
// REQUIRES: executable_test
34
// REQUIRES: concurrency
4-
// REQUIRES: OS=macosx
5-
// REQUIRES: CPU=x86_64
65

7-
import Dispatch
86
#if canImport(Darwin)
97
import Darwin
108
#elseif canImport(Glibc)
119
import Glibc
1210
#endif
1311

14-
@main struct Main {
15-
static func main() async {
16-
let one = await Task.__unsafeCurrentAsync().task // FIXME: replace with Task.current
17-
let two = await Task.__unsafeCurrentAsync().task // FIXME: replace with Task.current
18-
print("same equal: \(one == two)") // CHECK: same equal: true
19-
print("hashes equal: \(one.hashValue == two.hashValue)") // CHECK: hashes equal: true
12+
func simple() async {
13+
print("\(#function) -----------------------")
14+
let one = await Task.current()
15+
let two = await Task.current()
16+
print("same equal: \(one == two)") // CHECK: same equal: true
17+
print("hashes equal: \(one.hashValue == two.hashValue)") // CHECK: hashes equal: true
18+
19+
async let x = Task.current()
20+
let three = await x
21+
22+
print("parent/child equal: \(three == two)") // CHECK: parent/child equal: false
23+
print("parent/child hashes equal: \(three.hashValue == two.hashValue)") // CHECK: parent/child hashes equal: false
24+
}
25+
26+
func unsafe() async {
27+
print("\(#function) -----------------------")
28+
let one = Task.unsafeCurrent!
29+
let two = Task.unsafeCurrent!
30+
print("unsafe same equal: \(one == two)") // CHECK: same equal: true
31+
print("unsafe hashes equal: \(one.hashValue == two.hashValue)") // CHECK: hashes equal: true
2032

21-
async let x = Task.__unsafeCurrentAsync().task // FIXME: replace with Task.current
33+
async let x = Task.unsafeCurrent!
34+
let three = await x
2235

23-
let three = await x
24-
print("parent/child equal: \(three == two)") // CHECK: parent/child equal: false
25-
print("parent/child hashes equal: \(three.hashValue == two.hashValue)") // CHECK: parent/child hashes equal: false
36+
print("unsafe parent/child equal: \(three == two)") // CHECK: parent/child equal: false
37+
print("unsafe parent/child hashes equal: \(three.hashValue == two.hashValue)") // CHECK: parent/child hashes equal: false
38+
39+
print("unsafe.task parent/child equal: \(three.task == two.task)") // CHECK: parent/child equal: false
40+
print("unsafe.task parent/child hashes equal: \(three.task.hashValue == two.task.hashValue)") // CHECK: parent/child hashes equal: false
41+
}
42+
43+
func unsafeSync() {
44+
print("\(#function) -----------------------")
45+
let one = Task.unsafeCurrent!
46+
let two = Task.unsafeCurrent!
47+
print("unsafe same equal: \(one == two)") // CHECK: same equal: true
48+
print("unsafe hashes equal: \(one.hashValue == two.hashValue)") // CHECK: hashes equal: true
49+
}
50+
51+
@main struct Main {
52+
static func main() async {
53+
await simple()
54+
await unsafe()
55+
unsafeSync()
2656
}
2757
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library) | %FileCheck %s --dump-input=always
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: concurrency
5+
6+
@main struct Main {
7+
static func main() async {
8+
let handle = Task.runDetached {
9+
while (!Task.isCancelled) { // no need for await here, yay
10+
print("waiting")
11+
}
12+
13+
print("done")
14+
}
15+
16+
handle.cancel()
17+
18+
// CHECK: done
19+
await handle.get()
20+
}
21+
}

test/Concurrency/Runtime/async_task_priority_current.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,26 @@ import WinSDK
1313
#error("Unsupported platform")
1414
#endif
1515

16-
// FIXME: use `Task.currentPriority` once unsafeCurrent works in all these
17-
1816
func test_detach() async {
19-
let a1 = await Task.__unsafeCurrentAsync().task.priority
17+
let a1 = Task.currentPriority
2018
print("a1: \(a1)") // CHECK: a1: default
2119

2220
// Note: remember to detach using a higher priority, otherwise a lower one
2321
// might be escalated by the get() and we could see `default` in the detached
2422
// task.
2523
await Task.runDetached(priority: .userInitiated) {
26-
let a2 = await Task.__unsafeCurrentAsync().task.priority
24+
let a2 = Task.currentPriority
2725
print("a2: \(a2)") // CHECK: a2: userInitiated
2826
}.get()
2927

30-
let a3 = await Task.__unsafeCurrentAsync().task.priority
28+
let a3 = Task.currentPriority
3129
print("a3: \(a3)") // CHECK: a3: default
3230
}
3331

3432
func test_multiple_lo_indirectly_escalated() async {
3533
@concurrent
3634
func loopUntil(priority: Task.Priority) async {
37-
while (await Task.__unsafeCurrentAsync().task.priority != priority) {
35+
while (Task.currentPriority != priority) {
3836
#if os(Windows)
3937
Sleep(1)
4038
#else

test/Concurrency/Runtime/async_taskgroup_next_not_invoked_cancelAll.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func test_skipCallingNext_butInvokeCancelAll() async {
1616
await group.add { () async -> Int in
1717
sleep(1)
1818
print(" inside group.add { \(n) }")
19-
let cancelled = await Task.__unsafeCurrentAsync().isCancelled
19+
let cancelled = Task.isCancelled
2020
print(" inside group.add { \(n) } (canceled: \(cancelled))")
2121
return n
2222
}
@@ -25,7 +25,7 @@ func test_skipCallingNext_butInvokeCancelAll() async {
2525
group.cancelAll()
2626

2727
// return immediately; the group should wait on the tasks anyway
28-
let c = await Task.__unsafeCurrentAsync().isCancelled
28+
let c = Task.isCancelled
2929
print("return immediately 0 (canceled: \(c))")
3030
return 0
3131
}

test/Concurrency/Runtime/async_taskgroup_next_not_invoked_without_cancelAll.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ func test_skipCallingNext() async {
1515
print("group.add { \(n) }")
1616
await group.add { () async -> Int in
1717
sleep(1)
18-
let c = await Task.__unsafeCurrentAsync().isCancelled
18+
let c = Task.isCancelled
1919
print(" inside group.add { \(n) } (canceled: \(c))")
2020
return n
2121
}
2222
}
2323

2424
// return immediately; the group should wait on the tasks anyway
25-
let c = await Task.__unsafeCurrentAsync().isCancelled
25+
let c = Task.isCancelled
2626
print("return immediately 0 (canceled: \(c))")
2727
return 0
2828
}

test/Concurrency/Runtime/async_taskgroup_throw_recover.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func test_taskGroup_throws() async {
3131
print("error caught in group: \(error)")
3232

3333
await group.add { () async -> Int in
34-
let c = await Task.__unsafeCurrentAsync().isCancelled
34+
let c = Task.isCancelled
3535
print("task 3 (cancelled: \(c))")
3636
return 3
3737
}

0 commit comments

Comments
 (0)