|
| 1 | +//===----------------------------------------------------------------------===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2020 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | + |
| 13 | +import Swift |
| 14 | + |
| 15 | +// ==== Task.init ------------------------------------------------ |
| 16 | + |
| 17 | +% for (METHOD_VARIANTS, ALL_AVAILABILITY, PARAMS) in [ |
| 18 | +% ( |
| 19 | +% [ # METHOD_VARIANT |
| 20 | +% '', |
| 21 | +% 'THROWING', |
| 22 | +% ], |
| 23 | +% [ # ALL_AVAILABILITY |
| 24 | +% '@_alwaysEmitIntoClient', |
| 25 | +% '@available(SwiftStdlib 5.1, *)', |
| 26 | +% ], |
| 27 | +% [ # PARAMS |
| 28 | +% 'name: String?', |
| 29 | +% 'priority: TaskPriority? = nil', |
| 30 | +% '@_inheritActorContext @_implicitSelfCapture operation: sending @escaping @isolated(any) () async throws -> Success', |
| 31 | +% ]), |
| 32 | +% # ==== |
| 33 | +% ( |
| 34 | +% [ # METHOD_VARIANT |
| 35 | +% 'DETACHED', |
| 36 | +% 'DETACHED THROWING', |
| 37 | +% ], |
| 38 | +% [ |
| 39 | +% '@_alwaysEmitIntoClient', |
| 40 | +% '@available(SwiftStdlib 5.1, *)', |
| 41 | +% ], |
| 42 | +% [ # PARAMS |
| 43 | +% 'name: String?', |
| 44 | +% 'priority: TaskPriority? = nil', |
| 45 | +% 'operation: sending @escaping @isolated(any) () async throws -> Success', |
| 46 | +% ]), |
| 47 | +% ]: |
| 48 | +% for METHOD_VARIANT in METHOD_VARIANTS: |
| 49 | + |
| 50 | +% IS_DETACHED = 'DETACHED' in METHOD_VARIANT |
| 51 | +% IS_THROWING = 'THROWING' in METHOD_VARIANT |
| 52 | +% if IS_THROWING: |
| 53 | +% FAILURE_TYPE = 'Error' |
| 54 | +% else: |
| 55 | +% FAILURE_TYPE = 'Never' |
| 56 | +% end |
| 57 | + |
| 58 | +% |
| 59 | +% def adjust_params_for_kind(params): |
| 60 | +% res = [] |
| 61 | +% for p in params: |
| 62 | +% np = p |
| 63 | +% if not IS_THROWING: |
| 64 | +% np = np.replace("throws", "") |
| 65 | +% res.append(np) |
| 66 | +% return res |
| 67 | +% |
| 68 | +% |
| 69 | +% HAS_TASK_PRIORITY = any('priority:' in param for param in PARAMS) |
| 70 | +% HAS_TASK_NAME = any('name:' in param for param in PARAMS) |
| 71 | +% HAS_TASK_EXECUTOR = any('taskExecutor:' in param for param in PARAMS) |
| 72 | + |
| 73 | +% if IS_DETACHED: |
| 74 | +% ARROW_RETURN_TYPE = f'-> Task<Success, {FAILURE_TYPE}>' |
| 75 | +% else: |
| 76 | +% ARROW_RETURN_TYPE = '' # init does not spell out return type |
| 77 | +% end |
| 78 | + |
| 79 | +% if IS_DETACHED: |
| 80 | +% THE_FUNC = 'public static func detached' |
| 81 | +% else: |
| 82 | +% THE_FUNC = 'public init' |
| 83 | +% end |
| 84 | + |
| 85 | +% # ==================================================================================================================== |
| 86 | +@available(SwiftStdlib 5.1, *) |
| 87 | +extension Task where Failure == ${FAILURE_TYPE} { |
| 88 | + |
| 89 | +% # -------------------------------------------------------------------------------------------------------------------- |
| 90 | +#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY |
| 91 | + @discardableResult |
| 92 | + @_alwaysEmitIntoClient |
| 93 | + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model") |
| 94 | + ${THE_FUNC}( |
| 95 | + ${",\n ".join(adjust_params_for_kind(PARAMS))} |
| 96 | + ) ${ARROW_RETURN_TYPE}{ |
| 97 | + fatalError("Unavailable in task-to-thread concurrency model.") |
| 98 | + } |
| 99 | + |
| 100 | +% # -------------------------------------------------------------------------------------------------------------------- |
| 101 | +#elseif $Embedded |
| 102 | + @discardableResult |
| 103 | + @_alwaysEmitIntoClient |
| 104 | + @available(SwiftStdlib 5.1, *) |
| 105 | + ${THE_FUNC}( |
| 106 | + ${",\n ".join(adjust_params_for_kind(PARAMS))} |
| 107 | + ) ${ARROW_RETURN_TYPE}{ |
| 108 | + // Set up the job flags for a new task. |
| 109 | + let flags = taskCreateFlags( |
| 110 | + priority: priority, |
| 111 | + isChildTask: false, |
| 112 | + copyTaskLocals: ${'true' if not IS_DETACHED else 'false /* detached */'}, |
| 113 | + inheritContext: true, |
| 114 | + enqueueJob: true, |
| 115 | + addPendingGroupTaskUnconditionally: false, |
| 116 | + isDiscardingTask: false, |
| 117 | + isSynchronousStart: false) |
| 118 | + |
| 119 | + // Create the asynchronous task. |
| 120 | + let (task, _) = Builtin.createAsyncTask(flags, operation) |
| 121 | + |
| 122 | + self._task = task |
| 123 | + } |
| 124 | + |
| 125 | +% # -------------------------------------------------------------------------------------------------------------------- |
| 126 | +#else |
| 127 | +% if IS_THROWING: |
| 128 | + /// Runs the given throwing operation asynchronously |
| 129 | +% else: |
| 130 | + /// Runs the given nonthrowing operation asynchronously |
| 131 | +% end |
| 132 | +% if IS_DETACHED: |
| 133 | + /// as part of a new _unstructured_ _detached_ top-level task. |
| 134 | +% else: |
| 135 | + /// as part of a new _unstructured_ top-level task. |
| 136 | +% end |
| 137 | + /// |
| 138 | +% if IS_THROWING: |
| 139 | + /// If the `operation` throws an error, it is caught by the `Task` and will be |
| 140 | + /// rethrown only when the task's `value` is awaited. Take care to not accidentally |
| 141 | + /// dismiss errors by not awaiting on the task's resulting value. |
| 142 | +% end |
| 143 | + /// |
| 144 | +% if IS_DETACHED: |
| 145 | + /// Don't use a detached unstructured task if it's possible |
| 146 | + /// to model the operation using structured concurrency features like child tasks. |
| 147 | + /// Child tasks inherit the parent task's priority and task-local storage, |
| 148 | + /// and canceling a parent task automatically cancels all of its child tasks. |
| 149 | + /// You need to handle these considerations manually with a detached task. |
| 150 | +% end |
| 151 | + /// |
| 152 | + /// You need to keep a reference to the task |
| 153 | + /// if you want to cancel it by calling the `Task.cancel()` method. |
| 154 | + /// Discarding your reference to a task |
| 155 | + /// doesn't implicitly cancel that task, |
| 156 | + /// it only makes it impossible for you to explicitly cancel the task. |
| 157 | + /// |
| 158 | + /// - Parameters: |
| 159 | + % if HAS_TASK_NAME: |
| 160 | + /// - name: Human readable name of the task. |
| 161 | + % end |
| 162 | + % if HAS_TASK_NAME: |
| 163 | + /// - taskExecutor: the preferred task executor for this task, |
| 164 | + /// and any child tasks created by it. Explicitly passing `nil` is |
| 165 | + /// interpreted as "no preference". |
| 166 | + % end |
| 167 | + /// - priority: The priority of the task. |
| 168 | + /// - operation: The operation to perform. |
| 169 | + /// |
| 170 | + /// - Returns: A reference to the task. |
| 171 | + ${"\n ".join(ALL_AVAILABILITY)} |
| 172 | + @discardableResult |
| 173 | + ${THE_FUNC}( |
| 174 | + ${",\n ".join(adjust_params_for_kind(PARAMS))} |
| 175 | + ) ${ARROW_RETURN_TYPE}{ |
| 176 | + |
| 177 | + // Set up the job flags for a new task. |
| 178 | + let flags = taskCreateFlags( |
| 179 | + priority: priority, |
| 180 | + isChildTask: false, |
| 181 | + copyTaskLocals: ${'true' if not IS_DETACHED else 'false'}, |
| 182 | + inheritContext: false, |
| 183 | + enqueueJob: true, |
| 184 | + addPendingGroupTaskUnconditionally: false, |
| 185 | + isDiscardingTask: false, |
| 186 | + isSynchronousStart: false) |
| 187 | + |
| 188 | + // Create the asynchronous task. |
| 189 | + let builtinSerialExecutor = |
| 190 | + unsafe Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor |
| 191 | + |
| 192 | + var task: Builtin.NativeObject? |
| 193 | + #if $BuiltinCreateAsyncTaskName |
| 194 | + if let name { |
| 195 | + task = |
| 196 | + unsafe name.utf8CString.withUnsafeBufferPointer { nameBytes in |
| 197 | + Builtin.createTask( |
| 198 | + flags: flags, |
| 199 | + initialSerialExecutor: builtinSerialExecutor, |
| 200 | + taskName: nameBytes.baseAddress!._rawValue, |
| 201 | + operation: operation).0 |
| 202 | + } |
| 203 | + } |
| 204 | + #endif |
| 205 | + if task == nil { |
| 206 | + // either no task name was set, or names are unsupported |
| 207 | + task = Builtin.createTask( |
| 208 | + flags: flags, |
| 209 | + initialSerialExecutor: builtinSerialExecutor, |
| 210 | + operation: operation).0 |
| 211 | + } |
| 212 | + |
| 213 | +% if IS_DETACHED: |
| 214 | + return Task(task!) |
| 215 | +% else: |
| 216 | + self._task = task! |
| 217 | +% end |
| 218 | + } |
| 219 | + |
| 220 | +#endif |
| 221 | +} // extension Task ... |
| 222 | + |
| 223 | +% end |
| 224 | +% end |
0 commit comments