@@ -29,6 +29,50 @@ import Swift
2929/// Passing `nil` as executor means disabling any preference preference (if it was set) and the task hierarchy
3030/// will execute without any executor preference until a different preference is set.
3131///
32+ /// ### Asynchronous function execution semantics in presence of task executor preferences
33+ /// The following diagram illustrates on which executor an `async` function will
34+ /// execute, in presence (or lack thereof) a task executor preference.
35+ ///
36+ /// ```
37+ /// [ func / closure ] - /* where should it execute? */
38+ /// |
39+ /// +--------------+ +===========================+
40+ /// +-------- | is isolated? | - yes -> | actor has unownedExecutor |
41+ /// | +--------------+ +===========================+
42+ /// | | |
43+ /// | yes no
44+ /// | | |
45+ /// | v v
46+ /// | +=======================+ /* task executor preference? */
47+ /// | | on specified executor | | |
48+ /// | +=======================+ yes no
49+ /// | | |
50+ /// | | v
51+ /// | | +==========================+
52+ /// | | | default (actor) executor |
53+ /// | v +==========================+
54+ /// v +==============================+
55+ /// /* task executor preference? */ ---- yes ----> | on Task's preferred executor |
56+ /// | +==============================+
57+ /// no
58+ /// |
59+ /// v
60+ /// +===============================+
61+ /// | on global concurrent executor |
62+ /// +===============================+
63+ /// ```
64+ ///
65+ /// In short, without a task executor preference, `nonisolated async` functions
66+ /// will execute on the global concurrent executor. If a task executor preference
67+ /// is present, such `nonisolated async` functions will execute on the preferred
68+ /// task executor.
69+ ///
70+ /// Isolated functions semantically execute on the actor they are isolated to,
71+ /// however if such actor does not declare a custom executor (it is a "default
72+ /// actor") in presence of a task executor preference, tasks executing on this
73+ /// actor will use the preferred executor as source of threads to run the task,
74+ /// while isolated on the actor.
75+ ///
3276/// ### Example
3377///
3478/// Task {
@@ -55,7 +99,7 @@ import Swift
5599/// async let x = ...
56100/// await withTaskGroup(of: Int.self) { group in
57101/// group.addTask { 7 } // child task executes on 'specific' executor
58- /// group.addTask(on: .default ) { 13 } // child task executes on default executor
102+ /// group.addTask(executorPreference: globalConcurrentExecutor ) { 13 } // child task executes on default executor
59103/// }
60104///
61105/// // disable the task executor preference:
@@ -75,20 +119,24 @@ import Swift
75119/// }
76120///
77121/// - Parameters:
78- /// - executorPreference : the task executor to use as preferred task executor; if `nil` it is interpreted as "no preference"
122+ /// - preferredTaskExecutor : the task executor to use as preferred task executor; if `nil` it is interpreted as "no preference"
79123/// - operation: the operation to execute on the passed executor; if the executor was `nil`, this will execute on the default global concurrent executor.
80124/// - Returns: the value returned from the `operation` closure
81125/// - Throws: if the operation closure throws
82126/// - SeeAlso: `_TaskExecutor`
83127@_unavailableInEmbedded
84128@available ( SwiftStdlib 9999 , * )
85129@_unsafeInheritExecutor // calling withTaskExecutor MUST NOT perform the "usual" hop to global
86- public func _withTaskExecutor < T: Sendable > (
87- _ taskExecutorPreference : some _TaskExecutor ,
130+ public func _withTaskExecutorPreference < T: Sendable > (
131+ _ taskExecutor : ( any _TaskExecutor ) ? ,
88132 operation: @Sendable ( ) async throws -> T
89133 ) async rethrows -> T {
90134 let taskExecutorBuiltin : Builtin . Executor =
91- taskExecutorPreference. asUnownedTaskExecutor ( ) . executor
135+ if let taskExecutor {
136+ taskExecutor. asUnownedTaskExecutor ( ) . executor
137+ } else {
138+ globalConcurrentExecutor. asUnownedTaskExecutor ( ) . executor
139+ }
92140
93141 let record = _pushTaskExecutorPreference ( taskExecutorBuiltin)
94142 defer {
@@ -101,44 +149,6 @@ public func _withTaskExecutor<T: Sendable>(
101149 return try await operation ( )
102150}
103151
104- /// Default global concurrent pool TaskExecutor --------------------------------
105-
106- @available ( SwiftStdlib 9999 , * )
107- extension _TaskExecutor where Self == DefaultConcurrentExecutor {
108-
109- /// The default executor preference, setting it using ``withTaskExecutor(_:)``
110- /// is equivalent to disabling an existing task executor preference, or
111- /// stating that task now has "no task executor preference".
112- @available ( SwiftStdlib 9999 , * )
113- public static var `default` : DefaultConcurrentExecutor {
114- DefaultConcurrentExecutor . shared
115- }
116-
117- }
118-
119- /// A task executor which enqueues all work on the default global concurrent
120- /// thread pool that is used as the default executor for Swift concurrency
121- /// tasks.
122- @available ( SwiftStdlib 9999 , * )
123- public final class DefaultConcurrentExecutor : _TaskExecutor {
124- public static let shared : DefaultConcurrentExecutor = . init( )
125-
126- private init ( ) { }
127-
128- public func enqueue( _ job: consuming ExecutorJob ) {
129- _enqueueJobGlobal ( job. context)
130- }
131-
132- public func asUnownedTaskExecutor( ) {
133- // The "default global concurrent executor" is simply the "undefined" one.
134- // We represent it as the `(0, 0)` ExecutorRef and it is handled properly
135- // by the runtime, without having to call through to the
136- // `DefaultConcurrentExecutor` declared in Swift.
137- UnownedTaskExecutor ( _getUndefinedTaskExecutor ( ) )
138- }
139-
140- }
141-
142152/// Task with specified executor -----------------------------------------------
143153
144154@available ( SwiftStdlib 9999 , * )
@@ -150,7 +160,7 @@ extension Task where Failure == Never {
150160 /// This overload allows specifying a preferred ``_TaskExecutor`` on which
151161 /// the `operation`, as well as all child tasks created from this task will be
152162 /// executing whenever possible. Refer to ``_TaskExecutor`` for a detailed discussion
153- // of the effect of task executors on execution semantics of asynchronous code.
163+ /// of the effect of task executors on execution semantics of asynchronous code.
154164 ///
155165 /// Use this function when creating asynchronous work
156166 /// that operates on behalf of the synchronous function that calls it.
@@ -169,14 +179,14 @@ extension Task where Failure == Never {
169179 /// it only makes it impossible for you to explicitly cancel the task.
170180 ///
171181 /// - Parameters:
172- /// - executor : the preferred task executor for this task, and any child tasks created by it
182+ /// - taskExecutor : the preferred task executor for this task, and any child tasks created by it
173183 /// - priority: The priority of the task.
174184 /// Pass `nil` to use the priority from `Task.currentPriority`.
175185 /// - operation: The operation to perform.
176186 @discardableResult
177187 @_alwaysEmitIntoClient
178188 public init (
179- _on executor : some _TaskExecutor ,
189+ _executorPreference taskExecutor : ( any _TaskExecutor ) ? ,
180190 priority: TaskPriority ? = nil ,
181191 operation: __owned @Sendable @escaping ( ) async -> Success
182192 ) {
@@ -189,9 +199,15 @@ extension Task where Failure == Never {
189199 isDiscardingTask: false )
190200
191201 // Create the asynchronous task.
192- let taskExecutorRef = executor. asUnownedTaskExecutor ( )
202+ let executorBuiltin : Builtin . Executor =
203+ if let taskExecutor {
204+ taskExecutor. asUnownedTaskExecutor ( ) . executor
205+ } else {
206+ globalConcurrentExecutor. asUnownedTaskExecutor ( ) . executor
207+ }
208+
193209 let ( task, _) = Builtin . createAsyncTaskWithExecutor (
194- flags, taskExecutorRef . executor , operation)
210+ flags, executorBuiltin , operation)
195211 self . _task = task
196212 #else
197213 fatalError ( " Unsupported Swift compiler, missing support for BuiltinCreateAsyncTaskWithExecutor " )
@@ -204,7 +220,7 @@ extension Task where Failure == Error {
204220 @discardableResult
205221 @_alwaysEmitIntoClient
206222 public init (
207- _on executor : some _TaskExecutor ,
223+ _executorPreference taskExecutor : ( any _TaskExecutor ) ? ,
208224 priority: TaskPriority ? = nil ,
209225 operation: __owned @Sendable @escaping ( ) async throws -> Success
210226 ) {
@@ -217,9 +233,14 @@ extension Task where Failure == Error {
217233 isDiscardingTask: false )
218234
219235 // Create the asynchronous task.
220- let taskExecutorRef = executor. asUnownedTaskExecutor ( )
236+ let executorBuiltin : Builtin . Executor =
237+ if let taskExecutor {
238+ taskExecutor. asUnownedTaskExecutor ( ) . executor
239+ } else {
240+ globalConcurrentExecutor. asUnownedTaskExecutor ( ) . executor
241+ }
221242 let ( task, _) = Builtin . createAsyncTaskWithExecutor (
222- flags, taskExecutorRef . executor , operation)
243+ flags, executorBuiltin , operation)
223244 self . _task = task
224245 #else
225246 fatalError ( " Unsupported Swift compiler, missing support for $BuiltinCreateAsyncTaskWithExecutor " )
@@ -236,7 +257,7 @@ extension Task where Failure == Never {
236257 @_alwaysEmitIntoClient
237258 @available ( * , unavailable, message: " Unavailable in task-to-thread concurrency model " )
238259 public static func _detached(
239- on executor : some _TaskExecutor ,
260+ _executorPreference taskExecutor : ( any _TaskExecutor ) ? ,
240261 priority: TaskPriority ? = nil ,
241262 operation: __owned @Sendable @escaping ( ) async -> Success
242263 ) -> Task < Success , Failure > {
@@ -267,7 +288,7 @@ extension Task where Failure == Never {
267288 @discardableResult
268289 @_alwaysEmitIntoClient
269290 public static func _detached(
270- on executor : some _TaskExecutor ,
291+ _executorPreference taskExecutor : ( any _TaskExecutor ) ? ,
271292 priority: TaskPriority ? = nil ,
272293 operation: __owned @Sendable @escaping ( ) async -> Success
273294 ) -> Task < Success , Failure > {
@@ -280,9 +301,14 @@ extension Task where Failure == Never {
280301 isDiscardingTask: false )
281302
282303 // Create the asynchronous task.
283- let taskExecutorRef = executor. asUnownedTaskExecutor ( )
304+ let executorBuiltin : Builtin . Executor =
305+ if let taskExecutor {
306+ taskExecutor. asUnownedTaskExecutor ( ) . executor
307+ } else {
308+ globalConcurrentExecutor. asUnownedTaskExecutor ( ) . executor
309+ }
284310 let ( task, _) = Builtin . createAsyncTaskWithExecutor (
285- flags, taskExecutorRef . executor , operation)
311+ flags, executorBuiltin , operation)
286312
287313 return Task ( task)
288314 #else
@@ -299,7 +325,7 @@ extension Task where Failure == Error {
299325 @_alwaysEmitIntoClient
300326 @available ( * , unavailable, message: " Unavailable in task-to-thread concurrency model " )
301327 public static func _detached(
302- on executor : some _TaskExecutor ,
328+ _executorPreference taskExecutor : ( any _TaskExecutor ) ? ,
303329 priority: TaskPriority ? = nil ,
304330 operation: __owned @Sendable @escaping ( ) async throws -> Success
305331 ) -> Task < Success , Failure > {
@@ -332,7 +358,7 @@ extension Task where Failure == Error {
332358 @discardableResult
333359 @_alwaysEmitIntoClient
334360 public static func _detached(
335- on executor : some _TaskExecutor ,
361+ _executorPreference taskExecutor : ( any _TaskExecutor ) ? ,
336362 priority: TaskPriority ? = nil ,
337363 operation: __owned @Sendable @escaping ( ) async throws -> Success
338364 ) -> Task < Success , Failure > {
@@ -345,10 +371,14 @@ extension Task where Failure == Error {
345371 isDiscardingTask: false )
346372
347373 // Create the asynchronous task.
348- // Create the asynchronous task.
349- let taskExecutorRef = executor. asUnownedTaskExecutor ( )
374+ let executorBuiltin : Builtin . Executor =
375+ if let taskExecutor {
376+ taskExecutor. asUnownedTaskExecutor ( ) . executor
377+ } else {
378+ globalConcurrentExecutor. asUnownedTaskExecutor ( ) . executor
379+ }
350380 let ( task, _) = Builtin . createAsyncTaskWithExecutor (
351- flags, taskExecutorRef . executor , operation)
381+ flags, executorBuiltin , operation)
352382
353383 return Task ( task)
354384 #else
0 commit comments