@@ -18,8 +18,6 @@ import Dispatch
1818public final class TaskOperation < R: Sendable > : Operation , AsyncObject ,
1919 @unchecked Sendable
2020{
21- /// The type used to track completion of provided operation and unstructured tasks created in it.
22- private typealias Tracker = TaskTracker
2321 /// The asynchronous action to perform as part of the operation..
2422 private let underlyingAction : @Sendable ( ) async throws -> R
2523 /// The top-level task that executes asynchronous action provided
@@ -29,18 +27,23 @@ public final class TaskOperation<R: Sendable>: Operation, AsyncObject,
2927 /// synchronize data access and modifications.
3028 @usableFromInline
3129 let locker : Locker
30+
31+ /// A type representing a set of behaviors for the executed
32+ /// task type and task completion behavior.
33+ ///
34+ /// ``TaskOperation`` determines the execution behavior of
35+ /// provided action as task based on the provided flags.
36+ public typealias Flags = TaskOperationFlags
3237 /// The priority of top-level task executed.
3338 ///
3439 /// In case of `nil` priority from `Task.currentPriority`
3540 /// of task that starts the operation used.
3641 public let priority : TaskPriority ?
37- /// If completion of unstructured tasks created as part of provided task
38- /// should be tracked.
42+ /// A set of behaviors for the executed task type and task completion behavior.
3943 ///
40- /// If true, operation only completes if the provided asynchronous action
41- /// and all of its created unstructured task completes.
42- /// Otherwise, operation completes if the provided action itself completes.
43- public let shouldTrackUnstructuredTasks : Bool
44+ /// Provided flags determine the execution behavior of
45+ /// the action as task.
46+ public let flags : Flags
4447
4548 /// A Boolean value indicating whether the operation executes its task asynchronously.
4649 ///
@@ -119,14 +122,14 @@ public final class TaskOperation<R: Sendable>: Operation, AsyncObject,
119122 ///
120123 /// - Returns: The newly created asynchronous operation.
121124 public init (
122- trackUnstructuredTasks shouldTrackUnstructuredTasks: Bool = false ,
123125 synchronizedWith locker: Locker = . init( ) ,
124126 priority: TaskPriority ? = nil ,
127+ flags: Flags = [ ] ,
125128 operation: @escaping @Sendable ( ) async throws -> R
126129 ) {
127- self . shouldTrackUnstructuredTasks = shouldTrackUnstructuredTasks
128130 self . locker = locker
129131 self . priority = priority
132+ self . flags = flags
130133 self . underlyingAction = operation
131134 super. init ( )
132135 }
@@ -154,22 +157,12 @@ public final class TaskOperation<R: Sendable>: Operation, AsyncObject,
154157 /// as part of a new top-level task on behalf of the current actor.
155158 public override func main( ) {
156159 guard isExecuting, execTask == nil else { return }
157- execTask = Task ( priority: priority) { [ weak self] in
158- guard
159- let action = self ? . underlyingAction,
160- let trackUnstructuredTasks = self ? . shouldTrackUnstructuredTasks
161- else { throw CancellationError ( ) }
162- let final = { @Sendable [ weak self] in self ? . _finish ( ) ; return }
163- return trackUnstructuredTasks
164- ? try await Tracker . $current. withValue (
165- . init( onComplete: final) ,
166- operation: action
167- )
168- : try await {
169- defer { final ( ) }
170- return try await action ( )
171- } ( )
172- }
160+ let final = { @Sendable [ weak self] in self ? . _finish ( ) ; return }
161+ execTask = flags. createTask (
162+ priority: priority,
163+ operation: underlyingAction,
164+ onComplete: final
165+ )
173166 }
174167
175168 /// Advises the operation object that it should stop executing its task.
@@ -275,3 +268,86 @@ public final class TaskOperation<R: Sendable>: Operation, AsyncObject,
275268/// if the operation hasn't been started yet with either
276269/// ``TaskOperation/start()`` or ``TaskOperation/signal()``.
277270public struct EarlyInvokeError : Error , Sendable { }
271+
272+ /// A set of behaviors for ``TaskOperation``s,
273+ /// such as the task type and task completion behavior.
274+ ///
275+ /// ``TaskOperation`` determines the execution behavior of
276+ /// provided action as task based on the provided flags.
277+ public struct TaskOperationFlags : OptionSet , Sendable {
278+ /// Indicates to ``TaskOperation``, completion of unstructured tasks
279+ /// created as part of provided operation should be tracked.
280+ ///
281+ /// If provided, GCD operation only completes if the provided asynchronous action
282+ /// and all of its created unstructured task completes.
283+ /// Otherwise, operation completes if the provided action itself completes.
284+ public static let trackUnstructuredTasks = Self . init ( rawValue: 1 << 0 )
285+ /// Indicates to ``TaskOperation`` to disassociate action from the current execution context
286+ /// by running as a new detached task.
287+ ///
288+ /// Provided action is executed asynchronously as part of a new top-level task,
289+ /// with the provided task priority and without inheriting actor context that started
290+ /// the GCD operation.
291+ public static let detached = Self . init ( rawValue: 1 << 1 )
292+
293+ /// The type used to track completion of provided operation and unstructured tasks created in it.
294+ private typealias Tracker = TaskTracker
295+
296+ /// Runs the given throwing operation asynchronously as part of a new top-level task
297+ /// based on the current flags indicating whether to on behalf of the current actor
298+ /// and whether to track unstructured tasks created in provided operation.
299+ ///
300+ /// - Parameters:
301+ /// - priority: The priority of the task that operation executes.
302+ /// Pass `nil` to use the priority from `Task.currentPriority`
303+ /// of task that starts the operation.
304+ /// - operation: The asynchronous operation to execute.
305+ /// - completion: The action to invoke when task completes.
306+ ///
307+ /// - Returns: A reference to the task.
308+ fileprivate func createTask< R: Sendable > (
309+ priority: TaskPriority ? = nil ,
310+ operation: @escaping @Sendable ( ) async throws -> R ,
311+ onComplete completion: @escaping @Sendable ( ) -> Void
312+ ) -> Task < R , Error > {
313+ typealias LocalTask = Task < R , Error >
314+ typealias ThrowingAction = @Sendable ( ) async throws -> R
315+ typealias TaskInitializer = ( TaskPriority ? , ThrowingAction ) -> LocalTask
316+
317+ let initializer =
318+ self . contains ( . detached)
319+ ? LocalTask . detached
320+ : LocalTask . init
321+ return initializer ( priority) {
322+ return self . contains ( . trackUnstructuredTasks)
323+ ? try await Tracker . $current. withValue (
324+ . init( onComplete: completion) ,
325+ operation: operation
326+ )
327+ : try await {
328+ defer { completion ( ) }
329+ return try await operation ( )
330+ } ( )
331+ }
332+ }
333+
334+ /// The corresponding value of the raw type.
335+ ///
336+ /// A new instance initialized with rawValue will be equivalent to this instance.
337+ /// For example:
338+ /// ```swift
339+ /// print(TaskOperationFlags(rawValue: 1 << 1) == TaskOperationFlags.detached)
340+ /// // Prints "true"
341+ /// ```
342+ public let rawValue : UInt8
343+ /// Creates a new flag from the given raw value.
344+ ///
345+ /// - Parameter rawValue: The raw value of the flag set to create.
346+ /// - Returns: The newly created flag set.
347+ ///
348+ /// - Note: Do not use this method to create flag,
349+ /// use the default flags provided instead.
350+ public init ( rawValue: UInt8 ) {
351+ self . rawValue = rawValue
352+ }
353+ }
0 commit comments