forked from swiftlang/swift
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTask+immediate.swift.gyb
More file actions
420 lines (381 loc) · 14.2 KB
/
Task+immediate.swift.gyb
File metadata and controls
420 lines (381 loc) · 14.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
//===----------------------------------------------------------------------===//
//
// 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.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 = 'Failure'
% THROWS = 'throws(Failure) '
% else:
% FAILURE_TYPE = 'Never'
% THROWS = ''
% end
@available(SwiftStdlib 6.2, *)
% if IS_THROWING:
extension Task { // throwing Failure error type
% else:
extension Task where Failure == ${FAILURE_TYPE} {
% end
% if IS_DETACHED:
/// Create and immediately start running a new detached task in the context of the calling thread/task.
% else:
/// Create and immediately start running a new 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<Success, ${FAILURE_TYPE}> {
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<Success, ${FAILURE_TYPE}>(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<Success, ${FAILURE_TYPE}> {
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<Success, ${FAILURE_TYPE}>(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?)