Skip to content

Commit d4c347c

Browse files
authored
[Concurrency] Adjustments to withTaskExecutorPreference (#71973)
1 parent bae6450 commit d4c347c

File tree

4 files changed

+52
-22
lines changed

4 files changed

+52
-22
lines changed

stdlib/public/Concurrency/Task+TaskExecutor.swift

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,12 @@ import Swift
128128
/// - Throws: if the operation closure throws
129129
/// - SeeAlso: ``TaskExecutor``
130130
@_unavailableInEmbedded
131-
@available(SwiftStdlib 9999, *)
132-
@_unsafeInheritExecutor // calling withTaskExecutor MUST NOT perform the "usual" hop to global
133-
public func withTaskExecutorPreference<T: Sendable>(
131+
@available(SwiftStdlib 6.0, *)
132+
public func withTaskExecutorPreference<T, Failure>(
134133
_ taskExecutor: (any TaskExecutor)?,
135-
operation: @Sendable () async throws -> T
136-
) async rethrows -> T {
134+
isolation: isolated (any Actor)? = #isolation,
135+
operation: () async throws(Failure) -> T
136+
) async throws(Failure) -> T {
137137
guard let taskExecutor else {
138138
// User explicitly passed a "nil" preference, so we invoke the operation
139139
// as is, which will hop to it's expected executor without any change in
@@ -159,9 +159,32 @@ public func withTaskExecutorPreference<T: Sendable>(
159159
return try await operation()
160160
}
161161

162+
@_unavailableInEmbedded
163+
@available(SwiftStdlib 6.0, *)
164+
@_unsafeInheritExecutor // calling withTaskExecutor MUST NOT perform the "usual" hop to global
165+
@_silgen_name("$ss26withTaskExecutorPreference_9operationxSch_pSg_xyYaYbKXEtYaKs8SendableRzlF")
166+
public func __abi__withTaskExecutorPreference<T: Sendable>(
167+
_ taskExecutor: (any TaskExecutor)?,
168+
operation: @Sendable () async throws -> T
169+
) async rethrows -> T {
170+
guard let taskExecutor else {
171+
return try await operation()
172+
}
173+
174+
let taskExecutorBuiltin: Builtin.Executor =
175+
taskExecutor.asUnownedTaskExecutor().executor
176+
177+
let record = _pushTaskExecutorPreference(taskExecutorBuiltin)
178+
defer {
179+
_popTaskExecutorPreference(record: record)
180+
}
181+
182+
return try await operation()
183+
}
184+
162185
/// Task with specified executor -----------------------------------------------
163186

164-
@available(SwiftStdlib 9999, *)
187+
@available(SwiftStdlib 6.0, *)
165188
extension Task where Failure == Never {
166189
/// Runs the given nonthrowing operation asynchronously
167190
/// as part of a new top-level task on behalf of the current actor.
@@ -227,7 +250,7 @@ extension Task where Failure == Never {
227250
}
228251
}
229252

230-
@available(SwiftStdlib 9999, *)
253+
@available(SwiftStdlib 6.0, *)
231254
extension Task where Failure == Error {
232255
/// Runs the given throwing operation asynchronously
233256
/// as part of a new top-level task on behalf of the current actor.
@@ -289,7 +312,7 @@ extension Task where Failure == Error {
289312

290313
// ==== Detached tasks ---------------------------------------------------------
291314

292-
@available(SwiftStdlib 9999, *)
315+
@available(SwiftStdlib 6.0, *)
293316
extension Task where Failure == Never {
294317
/// Runs the given nonthrowing operation asynchronously
295318
/// as part of a new top-level task.
@@ -346,7 +369,7 @@ extension Task where Failure == Never {
346369
}
347370
}
348371

349-
@available(SwiftStdlib 9999, *)
372+
@available(SwiftStdlib 6.0, *)
350373
extension Task where Failure == Error {
351374
/// Runs the given throwing operation asynchronously
352375
/// as part of a new top-level task.
@@ -407,7 +430,7 @@ extension Task where Failure == Error {
407430

408431
// ==== Unsafe Current Task ----------------------------------------------------
409432

410-
@available(SwiftStdlib 9999, *)
433+
@available(SwiftStdlib 6.0, *)
411434
extension UnsafeCurrentTask {
412435

413436
/// The current ``TaskExecutor`` preference, if this task has one configured.
@@ -417,7 +440,7 @@ extension UnsafeCurrentTask {
417440
/// The lifetime of an executor is not guaranteed by an ``UnownedTaskExecutor``,
418441
/// so accessing it must be handled with great case -- and the program must use other
419442
/// means to guarantee the executor remains alive while it is in use.
420-
@available(SwiftStdlib 9999, *)
443+
@available(SwiftStdlib 6.0, *)
421444
public var unownedTaskExecutor: UnownedTaskExecutor? {
422445
let ref = _getPreferredTaskExecutor()
423446
return UnownedTaskExecutor(ref)
@@ -426,18 +449,18 @@ extension UnsafeCurrentTask {
426449

427450
// ==== Runtime ---------------------------------------------------------------
428451

429-
@available(SwiftStdlib 9999, *)
452+
@available(SwiftStdlib 6.0, *)
430453
@_silgen_name("swift_task_getPreferredTaskExecutor")
431454
internal func _getPreferredTaskExecutor() -> Builtin.Executor
432455

433456
typealias TaskExecutorPreferenceStatusRecord = UnsafeRawPointer
434457

435-
@available(SwiftStdlib 9999, *)
458+
@available(SwiftStdlib 6.0, *)
436459
@_silgen_name("swift_task_pushTaskExecutorPreference")
437460
internal func _pushTaskExecutorPreference(_ executor: Builtin.Executor)
438461
-> TaskExecutorPreferenceStatusRecord
439462

440-
@available(SwiftStdlib 9999, *)
463+
@available(SwiftStdlib 6.0, *)
441464
@_silgen_name("swift_task_popTaskExecutorPreference")
442465
internal func _popTaskExecutorPreference(
443466
record: TaskExecutorPreferenceStatusRecord
@@ -447,7 +470,7 @@ internal func _popTaskExecutorPreference(
447470
///
448471
/// It can be used to compare against, and is semantically equivalent to
449472
/// "no preference".
450-
@available(SwiftStdlib 9999, *)
473+
@available(SwiftStdlib 6.0, *)
451474
@usableFromInline
452475
internal func _getUndefinedTaskExecutor() -> Builtin.Executor {
453476
// Similar to the `_getGenericSerialExecutor` this method relies

test/Concurrency/Runtime/async_task_executor_withExecutor.swift

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,17 @@ nonisolated func nonisolatedAsyncMethod(expectedOn executor: MyTaskExecutor) asy
3333
dispatchPrecondition(condition: .onQueue(executor.queue))
3434
}
3535

36-
@MainActor
3736
func testNestingWithExecutorMainActor(_ firstExecutor: MyTaskExecutor,
3837
_ secondExecutor: MyTaskExecutor) async {
39-
MainActor.preconditionIsolated()
40-
dispatchPrecondition(condition: .onQueue(DispatchQueue.main))
38+
dispatchPrecondition(condition: .notOnQueue(firstExecutor.queue))
39+
dispatchPrecondition(condition: .notOnQueue(secondExecutor.queue))
4140

4241
await withTaskExecutorPreference(firstExecutor) {
4342
// the block immediately hops to the expected executor
4443
dispatchPrecondition(condition: .onQueue(firstExecutor.queue))
4544
print("OK: withTaskExecutor body")
4645
await nonisolatedAsyncMethod(expectedOn: firstExecutor)
4746
}
48-
MainActor.preconditionIsolated()
4947

5048
await withTaskExecutorPreference(firstExecutor) {
5149
await withTaskExecutorPreference(secondExecutor) {
@@ -56,7 +54,6 @@ func testNestingWithExecutorMainActor(_ firstExecutor: MyTaskExecutor,
5654
await nonisolatedAsyncMethod(expectedOn: secondExecutor)
5755
}
5856
}
59-
MainActor.preconditionIsolated()
6057

6158
await withTaskExecutorPreference(firstExecutor) {
6259
dispatchPrecondition(condition: .onQueue(firstExecutor.queue))
@@ -78,8 +75,6 @@ func testNestingWithExecutorMainActor(_ firstExecutor: MyTaskExecutor,
7875
dispatchPrecondition(condition: .notOnQueue(secondExecutor.queue))
7976
}
8077

81-
MainActor.preconditionIsolated()
82-
dispatchPrecondition(condition: .onQueue(DispatchQueue.main))
8378
dispatchPrecondition(condition: .notOnQueue(firstExecutor.queue))
8479
dispatchPrecondition(condition: .notOnQueue(secondExecutor.queue))
8580
}

test/abi/macOS/arm64/concurrency.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,9 @@ Added: _swift_job_run_on_task_executor
269269
Added: _swift_task_getPreferredTaskExecutor
270270
Added: _swift_task_popTaskExecutorPreference
271271
Added: _swift_task_pushTaskExecutorPreference
272+
273+
// Updated signature for withTaskExecutorPreference to used typed throws and #isolation
274+
// Swift.withTaskExecutorPreference<A, B where B: Swift.Error>(_: Swift.TaskExecutor?, isolation: isolated Swift.Actor?, operation: () async throws(B) -> A) async throws(B) -> A
275+
Added: _$ss26withTaskExecutorPreference_9isolation9operationxSch_pSg_ScA_pSgYixyYaq_YKXEtYaq_YKs5ErrorR_r0_lF
276+
// async function pointer to Swift.withTaskExecutorPreference<A, B where B: Swift.Error>(_: Swift.TaskExecutor?, isolation: isolated Swift.Actor?, operation: () async throws(B) -> A) async throws(B) -> A
277+
Added: _$ss26withTaskExecutorPreference_9isolation9operationxSch_pSg_ScA_pSgYixyYaq_YKXEtYaq_YKs5ErrorR_r0_lFTu

test/abi/macOS/x86_64/concurrency.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,9 @@ Added: _swift_job_run_on_task_executor
269269
Added: _swift_task_getPreferredTaskExecutor
270270
Added: _swift_task_popTaskExecutorPreference
271271
Added: _swift_task_pushTaskExecutorPreference
272+
273+
// Updated signature for withTaskExecutorPreference to used typed throws and #isolation
274+
// Swift.withTaskExecutorPreference<A, B where B: Swift.Error>(_: Swift.TaskExecutor?, isolation: isolated Swift.Actor?, operation: () async throws(B) -> A) async throws(B) -> A
275+
Added: _$ss26withTaskExecutorPreference_9isolation9operationxSch_pSg_ScA_pSgYixyYaq_YKXEtYaq_YKs5ErrorR_r0_lF
276+
// async function pointer to Swift.withTaskExecutorPreference<A, B where B: Swift.Error>(_: Swift.TaskExecutor?, isolation: isolated Swift.Actor?, operation: () async throws(B) -> A) async throws(B) -> A
277+
Added: _$ss26withTaskExecutorPreference_9isolation9operationxSch_pSg_ScA_pSgYixyYaq_YKXEtYaq_YKs5ErrorR_r0_lFTu

0 commit comments

Comments
 (0)