Skip to content

Commit 664be9b

Browse files
committed
[Concurrency] Add missing Task.immediateDetached, which drops task locals
1 parent 358c3e9 commit 664be9b

File tree

4 files changed

+70
-14
lines changed

4 files changed

+70
-14
lines changed

stdlib/public/Concurrency/Task+immediate.swift.gyb

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@
1313
import Swift
1414
@_implementationOnly import SwiftConcurrencyInternalShims
1515

16-
// ==== Task.immediate ---------------------------------------------------------
16+
// ==== Task.startSynchronously ------------------------------------------------
1717

1818
% METHOD_VARIANTS = [
1919
% 'THROWING',
2020
% 'NON_THROWING',
2121
% ]
22-
% for METHOD_VARIANT in METHOD_VARIANTS:
22+
% for THROWING_VARIANT in METHOD_VARIANTS:
2323

24-
% IS_THROWING = METHOD_VARIANT == 'THROWING'
24+
% IS_THROWING = THROWING_VARIANT == 'THROWING'
2525
% if IS_THROWING:
2626
% FAILURE_TYPE = 'Error'
2727
% THROWS = 'throws '
@@ -51,8 +51,38 @@ extension Task where Failure == ${FAILURE_TYPE} {
5151
) -> Task<Success, ${FAILURE_TYPE}> {
5252
immediate(name: name, priority: priority, operation: operation)
5353
}
54+
}
55+
56+
% end
57+
58+
// ==== Task.immediate(Detached) ---------------------------------------------------------
59+
60+
% METHOD_VARIANTS = [
61+
% ('immediate', 'THROWING'),
62+
% ('immediate', 'NON_THROWING'),
63+
% ('immediateDetached', 'THROWING'),
64+
% ('immediateDetached', 'NON_THROWING'),
65+
% ]
66+
% for (METHOD_NAME, THROWING_VARIANT) in METHOD_VARIANTS:
67+
68+
% IS_THROWING = THROWING_VARIANT == 'THROWING'
69+
% IS_DETACHED = 'Detached' in METHOD_NAME
70+
% if IS_THROWING:
71+
% FAILURE_TYPE = 'Error'
72+
% THROWS = 'throws '
73+
% else:
74+
% FAILURE_TYPE = 'Never'
75+
% THROWS = ''
76+
% end
5477

78+
@available(SwiftStdlib 6.2, *)
79+
extension Task where Failure == ${FAILURE_TYPE} {
80+
81+
% if IS_DETACHED:
5582
/// Create and immediately start running a new task in the context of the calling thread/task.
83+
% else:
84+
/// Create and immediately start running a new detached task in the context of the calling thread/task.
85+
% end # IS_DETACHED
5686
///
5787
/// This function _starts_ the created task on the calling context.
5888
/// The task will continue executing on the caller's context until it suspends,
@@ -65,8 +95,12 @@ extension Task where Failure == ${FAILURE_TYPE} {
6595
/// a synchronous manner.
6696
///
6797
/// Other than the execution semantics discussed above, the created task
68-
/// is semantically equivalent to its basic version which can be
69-
/// created using ``Task/init``.
98+
/// is semantically equivalent to a task created using
99+
% if IS_DETACHED:
100+
/// the ``Task/detached`` function.
101+
% else:
102+
/// the ``Task/init`` initializer.
103+
% end
70104
///
71105
/// - Parameters:
72106
/// - name: The high-level human-readable name given for this task
@@ -81,7 +115,7 @@ extension Task where Failure == ${FAILURE_TYPE} {
81115
@available(SwiftStdlib 6.2, *)
82116
@_alwaysEmitIntoClient
83117
@discardableResult
84-
public static func immediate(
118+
public static func ${METHOD_NAME}(
85119
name: String? = nil,
86120
priority: TaskPriority? = nil,
87121
executorPreference taskExecutor: consuming (any TaskExecutor)? = nil,
@@ -98,14 +132,14 @@ extension Task where Failure == ${FAILURE_TYPE} {
98132
if let builtinSerialExecutor {
99133
_taskIsCurrentExecutor(executor: builtinSerialExecutor, flags: flagsMustNotCrash)
100134
} else {
101-
true // if there is not target executor, we can run synchronously
135+
true // if there is no target executor, we can run synchronously
102136
}
103137

104138
let flags = taskCreateFlags(
105139
priority: priority,
106140
isChildTask: false,
107-
copyTaskLocals: true,
108-
inheritContext: true,
141+
copyTaskLocals: ${'true' if not IS_DETACHED else 'false /* detached */'},
142+
inheritContext: ${'true' if not IS_DETACHED else 'false /* detached */'},
109143
enqueueJob: !canRunSynchronously,
110144
addPendingGroupTaskUnconditionally: false,
111145
isDiscardingTask: false,
@@ -369,9 +403,9 @@ extension ${GROUP_TYPE} {
369403
% 'THROWING',
370404
% 'NON_THROWING',
371405
% ]
372-
% for METHOD_VARIANT in METHOD_VARIANTS:
406+
% for THROWING_VARIANT in METHOD_VARIANTS:
373407

374-
% IS_THROWING = METHOD_VARIANT == 'THROWING'
408+
% IS_THROWING = THROWING_VARIANT == 'THROWING'
375409
% if IS_THROWING:
376410
% FAILURE_TYPE = 'Error'
377411
% THROWS = 'throws '

stdlib/public/Concurrency/Task+init.swift.gyb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ extension Task where Failure == ${FAILURE_TYPE} {
212212
priority: priority,
213213
isChildTask: false,
214214
copyTaskLocals: ${'true' if not IS_DETACHED else 'false /* detached */'},
215-
inheritContext: true,
215+
inheritContext: ${'true' if not IS_DETACHED else 'false /* detached */'},
216216
enqueueJob: true,
217217
addPendingGroupTaskUnconditionally: false,
218218
isDiscardingTask: false,
@@ -294,8 +294,8 @@ extension Task where Failure == ${FAILURE_TYPE} {
294294
let flags = taskCreateFlags(
295295
priority: priority,
296296
isChildTask: false,
297-
copyTaskLocals: ${'true' if not IS_DETACHED else 'false'},
298-
inheritContext: ${'true' if not IS_DETACHED else 'false'},
297+
copyTaskLocals: ${'true' if not IS_DETACHED else 'false /* detached */'},
298+
inheritContext: ${'true' if not IS_DETACHED else 'false /* detached */'},
299299
enqueueJob: true,
300300
addPendingGroupTaskUnconditionally: false,
301301
isDiscardingTask: false,

test/Concurrency/Runtime/startImmediately.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,9 @@ await call_startSynchronously_insideActor()
459459
print("\n\n==== ------------------------------------------------------------------")
460460
print("call_taskImmediate_taskExecutor()")
461461

462+
@TaskLocal
463+
nonisolated(unsafe) var niceTaskLocalValueYouGotThere: String = ""
464+
462465
func call_taskImmediate_taskExecutor(taskExecutor: NaiveQueueExecutor) async {
463466
await Task.immediate(executorPreference: taskExecutor) {
464467
print("Task.immediate(executorPreference:)")
@@ -474,6 +477,20 @@ func call_taskImmediate_taskExecutor(taskExecutor: NaiveQueueExecutor) async {
474477
dispatchPrecondition(condition: .notOnQueue(taskExecutor.queue)) // since @MainActor requirement > preference
475478
}.value
476479

480+
await $niceTaskLocalValueYouGotThere.withValue("value") {
481+
assert(niceTaskLocalValueYouGotThere == "value")
482+
483+
// Task.immediate copies task locals
484+
await Task.immediate(executorPreference: taskExecutor) {
485+
assert(niceTaskLocalValueYouGotThere == "value")
486+
}.value
487+
488+
// Task.immediateDetached does not copy task locals
489+
await Task.immediateDetached(executorPreference: taskExecutor) {
490+
assert(niceTaskLocalValueYouGotThere == "")
491+
}.value
492+
}
493+
477494
await withTaskGroup { group in
478495
print("withTaskGroup { group.addTask(executorPreference:) { ... } }")
479496
group.addImmediateTask(executorPreference: taskExecutor) {

test/Concurrency/startImmediatelyIsolation.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ func async() async throws {
1414
return ""
1515
}
1616
let _: String = await t1.value
17+
18+
let td1 = Task.immediateDetached {
19+
return ""
20+
}
21+
let _: String = await td1.value
1722

1823
let t2: Task<String, Error> = Task.immediate {
1924
throw CancellationError()

0 commit comments

Comments
 (0)