@@ -14,6 +14,16 @@ import Swift
14
14
15
15
// ==== TaskGroup --------------------------------------------------------------
16
16
17
+ // In the task-to-thread model we don't enqueue tasks created using addTask
18
+ // to the global pool, but will run them inline instead.
19
+ #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
20
+ @usableFromInline
21
+ let enqueueJobOnCreate = false
22
+ #else
23
+ @usableFromInline
24
+ let enqueueJobOnCreate = true
25
+ #endif
26
+
17
27
/// Starts a new scope that can contain a dynamic number of child tasks.
18
28
///
19
29
/// A group waits for all of its child tasks
@@ -159,9 +169,9 @@ public func _unsafeInheritExecutor_withTaskGroup<ChildTaskResult, GroupResult>(
159
169
/// by calling the `cancelAll()` method on the task group,
160
170
/// or by canceling the task in which the group is running.
161
171
///
162
- /// If you call `addTask(priority:operation:)` to create a new task in a canceled group,
172
+ /// If you call `addTask(name: priority:operation:)` to create a new task in a canceled group,
163
173
/// that task is immediately canceled after creation.
164
- /// Alternatively, you can call `addTaskUnlessCancelled(priority:operation:)`,
174
+ /// Alternatively, you can call `addTaskUnlessCancelled(name: priority:operation:)`,
165
175
/// which doesn't create the task if the group has already been canceled.
166
176
/// Choosing between these two functions
167
177
/// lets you control how to react to cancellation within a group:
@@ -307,8 +317,8 @@ public func _unsafeInheritExecutor_withThrowingTaskGroup<ChildTaskResult, GroupR
307
317
///
308
318
/// A cancelled task group can still keep adding tasks, however they will start
309
319
/// being immediately cancelled, and may act accordingly to this. To avoid adding
310
- /// new tasks to an already cancelled task group, use ``addTaskUnlessCancelled(priority:body:)``
311
- /// rather than the plain ``addTask(priority:body:)`` which adds tasks unconditionally.
320
+ /// new tasks to an already cancelled task group, use ``addTaskUnlessCancelled(name: priority:body:)``
321
+ /// rather than the plain ``addTask(name: priority:body:)`` which adds tasks unconditionally.
312
322
///
313
323
/// For information about the language-level concurrency model that `TaskGroup` is part of,
314
324
/// see [Concurrency][concurrency] in [The Swift Programming Language][tspl].
@@ -344,20 +354,12 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
344
354
priority: TaskPriority ? = nil ,
345
355
operation: sending @escaping @isolated ( any) ( ) async -> ChildTaskResult
346
356
) {
347
- #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
348
357
let flags = taskCreateFlags (
349
358
priority: priority, isChildTask: true , copyTaskLocals: false ,
350
- inheritContext: false , enqueueJob: false ,
359
+ inheritContext: false , enqueueJob: enqueueJobOnCreate ,
351
360
addPendingGroupTaskUnconditionally: true ,
352
361
isDiscardingTask: false
353
362
)
354
- #else
355
- let flags = taskCreateFlags (
356
- priority: priority, isChildTask: true , copyTaskLocals: false ,
357
- inheritContext: false , enqueueJob: true ,
358
- addPendingGroupTaskUnconditionally: true ,
359
- isDiscardingTask: false )
360
- #endif
361
363
362
364
// Create the task in this group.
363
365
let builtinSerialExecutor =
@@ -383,15 +385,9 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
383
385
priority: TaskPriority ? = nil ,
384
386
operation: sending @escaping @isolated ( any) ( ) async -> ChildTaskResult
385
387
) {
386
- #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
387
- let enqueueJob = false
388
- #else
389
- let enqueueJob = true
390
- #endif
391
-
392
388
let flags = taskCreateFlags (
393
389
priority: priority, isChildTask: true , copyTaskLocals: false ,
394
- inheritContext: false , enqueueJob: enqueueJob ,
390
+ inheritContext: false , enqueueJob: enqueueJobOnCreate ,
395
391
addPendingGroupTaskUnconditionally: true ,
396
392
isDiscardingTask: false
397
393
)
@@ -448,19 +444,11 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
448
444
// the group is cancelled and is not accepting any new work
449
445
return false
450
446
}
451
- #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
452
447
let flags = taskCreateFlags (
453
448
priority: priority, isChildTask: true , copyTaskLocals: false ,
454
- inheritContext: false , enqueueJob: false ,
449
+ inheritContext: false , enqueueJob: enqueueJobOnCreate ,
455
450
addPendingGroupTaskUnconditionally: false ,
456
451
isDiscardingTask: false )
457
- #else
458
- let flags = taskCreateFlags (
459
- priority: priority, isChildTask: true , copyTaskLocals: false ,
460
- inheritContext: false , enqueueJob: true ,
461
- addPendingGroupTaskUnconditionally: false ,
462
- isDiscardingTask: false )
463
- #endif
464
452
465
453
// Create the task in this group.
466
454
let builtinSerialExecutor =
@@ -473,6 +461,67 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
473
461
return true
474
462
}
475
463
464
+ /// Adds a child task to the group, unless the group has been canceled.
465
+ ///
466
+ /// - Parameters:
467
+ /// - name: Human readable name of the task.
468
+ /// - priority: The priority of the operation task.
469
+ /// Omit this parameter or pass `.unspecified`
470
+ /// to set the child task's priority to the priority of the group.
471
+ /// - operation: The operation to execute as part of the task group.
472
+ /// - Returns: `true` if the child task was added to the group;
473
+ /// otherwise `false`.
474
+ @_alwaysEmitIntoClient
475
+ public mutating func addTaskUnlessCancelled(
476
+ name: String ? = nil ,
477
+ priority: TaskPriority ? = nil ,
478
+ operation: sending @escaping @isolated ( any) ( ) async -> ChildTaskResult
479
+ ) -> Bool {
480
+ let canAdd = _taskGroupAddPendingTask ( group: _group, unconditionally: false )
481
+
482
+ guard canAdd else {
483
+ // the group is cancelled and is not accepting any new work
484
+ return false
485
+ }
486
+ let flags = taskCreateFlags (
487
+ priority: priority, isChildTask: true , copyTaskLocals: false ,
488
+ inheritContext: false , enqueueJob: enqueueJobOnCreate,
489
+ addPendingGroupTaskUnconditionally: false ,
490
+ isDiscardingTask: false )
491
+
492
+ let builtinSerialExecutor =
493
+ Builtin . extractFunctionIsolation ( operation) ? . unownedExecutor. executor
494
+
495
+ var task : Builtin . NativeObject ?
496
+ #if $BuiltinCreateAsyncTaskName
497
+ if var name {
498
+ task =
499
+ name. withUTF8 { nameBytes in
500
+ Builtin . createTask (
501
+ flags: flags,
502
+ initialSerialExecutor: builtinSerialExecutor,
503
+ taskGroup: _group,
504
+ taskName: nameBytes. baseAddress? . _rawValue,
505
+ operation: operation) . 0
506
+ }
507
+ }
508
+ #endif
509
+
510
+ if task == nil {
511
+ // either no task name was set, or names are unsupported
512
+ task = Builtin . createTask (
513
+ flags: flags,
514
+ // unsupported names, so we drop it.
515
+ initialSerialExecutor: builtinSerialExecutor,
516
+ operation: operation) . 0
517
+ }
518
+
519
+ // task was enqueued to the group, no need to store the 'task' ref itself
520
+ assert ( task != nil , " Expected task to be created! " )
521
+
522
+ return true
523
+ }
524
+
476
525
#else // if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
477
526
@available ( SwiftStdlib 5 . 7 , * )
478
527
@available ( * , unavailable, message: " Unavailable in task-to-thread concurrency model " , renamed: " addTask(operation:) " )
0 commit comments