//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2020-2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// import Swift @_implementationOnly import SwiftConcurrencyInternalShims // ==== Task.startSynchronously ------------------------------------------------ % METHOD_VARIANTS = [ % 'THROWING', % 'NON_THROWING', % ] % for THROWING_VARIANT in METHOD_VARIANTS: % IS_THROWING = THROWING_VARIANT == 'THROWING' % if IS_THROWING: % FAILURE_TYPE = 'Error' % THROWS = 'throws ' % else: % FAILURE_TYPE = 'Never' % THROWS = '' % end % end // ==== Task.immediate(Detached) --------------------------------------------------------- % METHOD_VARIANTS = [ % ('immediate', 'THROWING'), % ('immediate', 'NON_THROWING'), % ('immediateDetached', 'THROWING'), % ('immediateDetached', 'NON_THROWING'), % ] % for (METHOD_NAME, THROWING_VARIANT) in METHOD_VARIANTS: % IS_THROWING = THROWING_VARIANT == 'THROWING' % IS_DETACHED = 'Detached' in METHOD_NAME % if IS_THROWING: % FAILURE_TYPE = 'Error' % THROWS = 'throws ' % else: % FAILURE_TYPE = 'Never' % THROWS = '' % end @available(SwiftStdlib 6.2, *) extension Task where Failure == ${FAILURE_TYPE} { % if IS_DETACHED: /// Create and immediately start running a new task in the context of the calling thread/task. % else: /// Create and immediately start running a new detached task in the context of the calling thread/task. % end # IS_DETACHED /// /// This function _starts_ the created task on the calling context. /// The task will continue executing on the caller's context until it suspends, /// and after suspension will resume on the adequate executor. For a nonisolated /// operation this means running on the global concurrent pool, and on an isolated /// operation it means the appropriate executor of that isolation context. /// /// As indicated by the lack of `async` on this method, this method does _not_ /// suspend, and instead takes over the calling task's (thread's) execution in /// a synchronous manner. /// /// Other than the execution semantics discussed above, the created task /// is semantically equivalent to a task created using % if IS_DETACHED: /// the ``Task/detached`` function. % else: /// the ``Task/init`` initializer. % end /// /// - Parameters: /// - name: The high-level human-readable name given for this task /// - priority: The priority of the task. /// Pass `nil` to use the ``Task/basePriority`` of the current task (if there is one). /// - taskExecutor: The task executor that the child task should be started on and keep using. /// Explicitly passing `nil` as the executor preference is equivalent to no preference, /// and effectively means to inherit the outer context's executor preference. /// You can also pass the ``globalConcurrentExecutor`` global executor explicitly. /// - operation: the operation to be run immediately upon entering the task. /// - Returns: A reference to the unstructured task which may be awaited on. @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient @discardableResult public static func ${METHOD_NAME}( name: String? = nil, priority: TaskPriority? = nil, executorPreference taskExecutor: consuming (any TaskExecutor)? = nil, @_implicitSelfCapture @_inheritActorContext(always) operation: sending @isolated(any) @escaping () async ${THROWS} -> Success ) -> Task { let builtinSerialExecutor = unsafe Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor // Determine if we're switching isolation dynamically. // If not, we can run the task synchronously and therefore MUST NOT "enqueue" it. let flagsMustNotCrash: UInt64 = 0 let canRunSynchronously: Bool = if let builtinSerialExecutor { _taskIsCurrentExecutor(executor: builtinSerialExecutor, flags: flagsMustNotCrash) } else { true // if there is no target executor, we can run synchronously } let flags = taskCreateFlags( priority: priority, isChildTask: false, copyTaskLocals: ${'true' if not IS_DETACHED else 'false /* detached */'}, inheritContext: ${'true' if not IS_DETACHED else 'false /* detached */'}, enqueueJob: !canRunSynchronously, addPendingGroupTaskUnconditionally: false, isDiscardingTask: false, isSynchronousStart: true ) var task: Builtin.NativeObject? #if $BuiltinCreateAsyncTaskName if let name { #if $BuiltinCreateAsyncTaskOwnedTaskExecutor task = unsafe name.utf8CString.withUnsafeBufferPointer { nameBytes in Builtin.createTask( flags: flags, initialSerialExecutor: builtinSerialExecutor, initialTaskExecutorConsuming: taskExecutor, taskName: nameBytes.baseAddress!._rawValue, operation: operation).0 } #else // no $BuiltinCreateAsyncTaskOwnedTaskExecutor task = unsafe name.utf8CString.withUnsafeBufferPointer { nameBytes in Builtin.createTask( flags: flags, initialSerialExecutor: builtinSerialExecutor, taskName: nameBytes.baseAddress!._rawValue, operation: operation).0 } #endif // $BuiltinCreateAsyncTaskOwnedTaskExecutor } // let name #endif // $BuiltinCreateAsyncTaskName // Task name was not set, or task name createTask is unavailable if task == nil { assert(name == nil) #if $BuiltinCreateAsyncTaskOwnedTaskExecutor task = Builtin.createTask( flags: flags, initialSerialExecutor: builtinSerialExecutor, initialTaskExecutorConsuming: taskExecutor, operation: operation).0 #else // legacy branch for the non-consuming task executor let executorBuiltin: Builtin.Executor = taskExecutor.asUnownedTaskExecutor().executor task = Builtin.createTask( flags: flags, initialSerialExecutor: builtinSerialExecutor, initialTaskExecutor: executorBuiltin, operation: operation).0 #endif } if task == nil { // either no task name was set, or names are unsupported task = Builtin.createTask( flags: flags, initialSerialExecutor: builtinSerialExecutor, operation: operation).0 } if canRunSynchronously { _startTaskImmediately(task!, targetExecutor: builtinSerialExecutor) } return Task(task!) } } %end %{ GROUP_AND_OP_INFO = [ ( 'TaskGroup', [ 'addImmediateTask', 'addImmediateTaskUnlessCancelled' ], '', 'ChildTaskResult' ), ( 'ThrowingTaskGroup', [ 'addImmediateTask', 'addImmediateTaskUnlessCancelled' ], 'throws ', 'ChildTaskResult' ), ( 'DiscardingTaskGroup', [ 'addImmediateTask', 'addImmediateTaskUnlessCancelled' ], '', 'Void' ), ( 'ThrowingDiscardingTaskGroup', [ 'addImmediateTask', 'addImmediateTaskUnlessCancelled' ], 'throws ', 'Void' ), ] }% % for (GROUP_TYPE, METHOD_NAMES, THROWS, RESULT_TYPE) in GROUP_AND_OP_INFO: % for METHOD_NAME in METHOD_NAMES: % % IS_DISCARDING = 'Discarding' in GROUP_TYPE % IS_ADD_UNLESS_CANCELLED = METHOD_NAME == "addImmediateTaskUnlessCancelled" % % ARROW_RETURN_TYPE = "-> Bool " if IS_ADD_UNLESS_CANCELLED else "" % % if IS_DISCARDING: % TASK_CREATE_FN = 'Builtin.createDiscardingTask' % else: % TASK_CREATE_FN = 'Builtin.createTask' % end @available(SwiftStdlib 6.2, *) extension ${GROUP_TYPE} { /// Add a child task to the group and immediately start running it in the context of the calling thread/task. /// /// This function _starts_ the created task on the calling context. /// The task will continue executing on the caller's context until it suspends, /// and after suspension will resume on the adequate executor. For a nonisolated /// operation this means running on the global concurrent pool, and on an isolated /// operation it means the appropriate executor of that isolation context. /// /// As indicated by the lack of `async` on this method, this method does _not_ /// suspend, and instead takes over the calling task's (thread's) execution in /// a synchronous manner. /// /// Other than the execution semantics discussed above, the created task /// is semantically equivalent to its basic version which can be /// created using ``${GROUP_TYPE}/addTask``. /// /// - Parameters: /// - name: Human readable name of this task. /// - priority: The priority of the operation task. /// Omit this parameter or pass `nil` to inherit the task group's base priority. /// - taskExecutor: The task executor that the child task should be started on and keep using. /// Explicitly passing `nil` as the executor preference is equivalent to /// calling the `${METHOD_NAME}` method without a preference, and effectively /// means to inherit the outer context's executor preference. /// You can also pass the ``globalConcurrentExecutor`` global executor explicitly. /// - operation: The operation to execute as part of the task group. % if IS_ADD_UNLESS_CANCELLED: /// - Returns: `true` if the child task was added to the group; /// otherwise `false`. % end @available(SwiftStdlib 6.2, *) @_alwaysEmitIntoClient public mutating func ${METHOD_NAME}( // in ${GROUP_TYPE} name: String? = nil, priority: TaskPriority? = nil, executorPreference taskExecutor: consuming (any TaskExecutor)? = nil, @_inheritActorContext @_implicitSelfCapture operation: sending @isolated(any) @escaping () async ${THROWS}-> ${RESULT_TYPE} ) ${ARROW_RETURN_TYPE}{ % if IS_ADD_UNLESS_CANCELLED: let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) guard canAdd else { // the group is cancelled and is not accepting any new work return false } % end # IS_ADD_UNLESS_CANCELLED let flags = taskCreateFlags( priority: priority, isChildTask: true, copyTaskLocals: false, inheritContext: false, enqueueJob: false, // don't enqueue, we'll run it manually % if IS_ADD_UNLESS_CANCELLED: % # In this case, we already added the pending task count before we create the task % # so we must not add to the pending counter again. addPendingGroupTaskUnconditionally: false, % else: addPendingGroupTaskUnconditionally: true, % end isDiscardingTask: ${str(IS_DISCARDING).lower()}, isSynchronousStart: true ) let builtinSerialExecutor = unsafe Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor var task: Builtin.NativeObject? #if $BuiltinCreateAsyncTaskName if let name { task = unsafe name.utf8CString.withUnsafeBufferPointer { nameBytes in ${TASK_CREATE_FN}( flags: flags, initialSerialExecutor: builtinSerialExecutor, taskGroup: _group, initialTaskExecutorConsuming: taskExecutor, taskName: nameBytes.baseAddress!._rawValue, operation: operation).0 } } #endif // $BuiltinCreateAsyncTaskName // Task name was not set, or task name createTask is unavailable if task == nil, let taskExecutor { #if $BuiltinCreateAsyncTaskOwnedTaskExecutor task = ${TASK_CREATE_FN}( flags: flags, initialSerialExecutor: builtinSerialExecutor, taskGroup: _group, initialTaskExecutorConsuming: taskExecutor, operation: operation).0 #else // legacy branch for the non-consuming task executor let executorBuiltin: Builtin.Executor = taskExecutor.asUnownedTaskExecutor().executor task = ${TASK_CREATE_FN}( flags: flags, initialSerialExecutor: builtinSerialExecutor, taskGroup: _group, initialTaskExecutor: executorBuiltin, operation: operation).0 #endif } if task == nil { task = ${TASK_CREATE_FN}( flags: flags, initialSerialExecutor: builtinSerialExecutor, taskGroup: _group, operation: operation).0 } // Assert that we did create the task, but there's no need to store it, // as it was added to the group itself. assert(task != nil, "Expected task to be created!") _startTaskImmediately(task!, targetExecutor: builtinSerialExecutor) % if IS_ADD_UNLESS_CANCELLED: return true // task successfully enqueued % end } } % end # METHOD_NAMES %end # GROUP_TYPES // ==== Legacy SPI ------------------------------------------------------------- % METHOD_VARIANTS = [ % 'THROWING', % 'NON_THROWING', % ] % for THROWING_VARIANT in METHOD_VARIANTS: % IS_THROWING = THROWING_VARIANT == 'THROWING' % if IS_THROWING: % FAILURE_TYPE = 'Error' % THROWS = 'throws ' % else: % FAILURE_TYPE = 'Never' % THROWS = '' % end #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY && !SWIFT_CONCURRENCY_EMBEDDED @available(SwiftStdlib 5.9, *) extension Task where Failure == ${FAILURE_TYPE} { @_spi(MainActorUtilities) @MainActor @available(SwiftStdlib 5.9, *) @discardableResult @available(*, deprecated, renamed: "immediate") public static func startOnMainActor( priority: TaskPriority? = nil, @_inheritActorContext @_implicitSelfCapture _ operation: __owned @Sendable @escaping @MainActor () async ${THROWS} -> Success ) -> Task { let flags = taskCreateFlags( priority: priority, isChildTask: false, copyTaskLocals: true, inheritContext: true, enqueueJob: false, addPendingGroupTaskUnconditionally: false, isDiscardingTask: false, isSynchronousStart: false ) let (task, _) = Builtin.createAsyncTask(flags, operation) _startTaskOnMainActor(task) return Task(task) } } #endif % end // Internal Runtime Functions -------------------------------------------------- @_silgen_name("swift_task_startOnMainActor") internal func _startTaskOnMainActor(_ task: Builtin.NativeObject) @available(SwiftStdlib 6.2, *) @_silgen_name("swift_task_immediate") @usableFromInline internal func _startTaskImmediately(_ task: Builtin.NativeObject, targetExecutor: Builtin.Executor?)