Skip to content

Commit bd06afe

Browse files
committed
Use @isolated(any) function types for task functions.
The biggest annoyance here is having to clone all of the task creation functions for Embedded Swift because it can't use `any Actor` right now.
1 parent 398466e commit bd06afe

File tree

6 files changed

+632
-30
lines changed

6 files changed

+632
-30
lines changed

stdlib/public/Concurrency/DiscardingTaskGroup.swift

Lines changed: 245 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,227 @@ public struct DiscardingTaskGroup {
160160
let _: Void? = try await _taskGroupWaitAll(group: _group, bodyError: nil)
161161
}
162162

163+
// Clone the task-creation routines in Embedded Swift so that we don't
164+
// introduce an implicit use of `any Actor`.
165+
#if !$Embedded
166+
167+
/// Adds a child task to the group.
168+
///
169+
/// - Parameters:
170+
/// - priority: The priority of the operation task.
171+
/// Omit this parameter or pass `.unspecified`
172+
/// to set the child task's priority to the priority of the group.
173+
/// - operation: The operation to execute as part of the task group.
174+
@_alwaysEmitIntoClient
175+
@_allowFeatureSuppression(IsolatedAny)
176+
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
177+
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)")
178+
#endif
179+
public mutating func addTask(
180+
priority: TaskPriority? = nil,
181+
operation: __owned @Sendable @escaping @isolated(any) () async -> Void
182+
) {
183+
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
184+
let flags = taskCreateFlags(
185+
priority: priority, isChildTask: true, copyTaskLocals: false,
186+
inheritContext: false, enqueueJob: false,
187+
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true
188+
)
189+
#else
190+
let flags = taskCreateFlags(
191+
priority: priority, isChildTask: true, copyTaskLocals: false,
192+
inheritContext: false, enqueueJob: true,
193+
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true
194+
)
195+
#endif
196+
197+
// Create the task in this group.
198+
#if $BuiltinCreateTask
199+
let builtinSerialExecutor =
200+
Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor
201+
202+
_ = Builtin.createDiscardingTask(flags: flags,
203+
initialSerialExecutor: builtinSerialExecutor,
204+
taskGroup: _group,
205+
operation: operation)
206+
#elseif $BuiltinCreateAsyncDiscardingTaskInGroup
207+
_ = Builtin.createAsyncDiscardingTaskInGroup(flags, _group, operation)
208+
#else
209+
// This builtin happens to work, but the signature of the operation is
210+
// incorrect, as the discarding group uses Void, and therefore has less
211+
// generic parameters than the operation expected to be passed to
212+
// createAsyncTaskInGroup. While this happened to work on some platforms,
213+
// on others this causes issues, e.g. on wasm;
214+
//
215+
// Keep this branch for compatibility with old compilers, but use the
216+
// correct 'createAsyncDiscardingTaskInGroup' when available (and a recent
217+
// enough compiler is used).
218+
_ = Builtin.createAsyncTaskInGroup(flags, _group, operation)
219+
#endif
220+
}
221+
222+
/// Adds a child task to the group, unless the group has been canceled.
223+
///
224+
/// - Parameters:
225+
/// - priority: The priority of the operation task.
226+
/// Omit this parameter or pass `.unspecified`
227+
/// to set the child task's priority to the priority of the group.
228+
/// - operation: The operation to execute as part of the task group.
229+
/// - Returns: `true` if the child task was added to the group;
230+
/// otherwise `false`.
231+
@_alwaysEmitIntoClient
232+
@_allowFeatureSuppression(IsolatedAny)
233+
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
234+
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)")
235+
#endif
236+
public mutating func addTaskUnlessCancelled(
237+
priority: TaskPriority? = nil,
238+
operation: __owned @Sendable @escaping @isolated(any) () async -> Void
239+
) -> Bool {
240+
let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false)
241+
242+
guard canAdd else {
243+
// the group is cancelled and is not accepting any new work
244+
return false
245+
}
246+
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
247+
let flags = taskCreateFlags(
248+
priority: priority, isChildTask: true, copyTaskLocals: false,
249+
inheritContext: false, enqueueJob: false,
250+
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true
251+
)
252+
#else
253+
let flags = taskCreateFlags(
254+
priority: priority, isChildTask: true, copyTaskLocals: false,
255+
inheritContext: false, enqueueJob: true,
256+
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true
257+
)
258+
#endif
259+
260+
// Create the task in this group.
261+
#if $BuiltinCreateTask
262+
let builtinSerialExecutor =
263+
Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor
264+
265+
_ = Builtin.createDiscardingTask(flags: flags,
266+
initialSerialExecutor: builtinSerialExecutor,
267+
taskGroup: _group,
268+
operation: operation)
269+
#elseif $BuiltinCreateAsyncDiscardingTaskInGroup
270+
_ = Builtin.createAsyncDiscardingTaskInGroup(flags, _group, operation)
271+
#else
272+
// This builtin happens to work, but the signature of the operation is
273+
// incorrect, as the discarding group uses Void, and therefore has less
274+
// generic parameters than the operation expected to be passed to
275+
// createAsyncTaskInGroup. While this happened to work on some platforms,
276+
// on others this causes issues, e.g. on wasm;
277+
//
278+
// Keep this branch for compatibility with old compilers, but use the
279+
// correct 'createAsyncDiscardingTaskInGroup' when available (and a recent
280+
// enough compiler is used).
281+
_ = Builtin.createAsyncTaskInGroup(flags, _group, operation)
282+
#endif
283+
284+
return true
285+
}
286+
287+
@_alwaysEmitIntoClient
288+
@_allowFeatureSuppression(IsolatedAny)
289+
public mutating func addTask(
290+
operation: __owned @Sendable @escaping @isolated(any) () async -> Void
291+
) {
292+
let flags = taskCreateFlags(
293+
priority: nil, isChildTask: true, copyTaskLocals: false,
294+
inheritContext: false, enqueueJob: true,
295+
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true
296+
)
297+
298+
// Create the task in this group.
299+
#if $BuiltinCreateTask
300+
let builtinSerialExecutor =
301+
Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor
302+
303+
_ = Builtin.createDiscardingTask(flags: flags,
304+
initialSerialExecutor: builtinSerialExecutor,
305+
taskGroup: _group,
306+
operation: operation)
307+
#elseif $BuiltinCreateAsyncDiscardingTaskInGroup
308+
_ = Builtin.createAsyncDiscardingTaskInGroup(flags, _group, operation)
309+
#else
310+
// This builtin happens to work, but the signature of the operation is
311+
// incorrect, as the discarding group uses Void, and therefore has less
312+
// generic parameters than the operation expected to be passed to
313+
// createAsyncTaskInGroup. While this happened to work on some platforms,
314+
// on others this causes issues, e.g. on wasm;
315+
//
316+
// Keep this branch for compatibility with old compilers, but use the
317+
// correct 'createAsyncDiscardingTaskInGroup' when available (and a recent
318+
// enough compiler is used).
319+
_ = Builtin.createAsyncTaskInGroup(flags, _group, operation)
320+
#endif
321+
}
322+
323+
/// Adds a child task to the group, unless the group has been canceled.
324+
///
325+
/// - Parameters:
326+
/// - operation: The operation to execute as part of the task group.
327+
/// - Returns: `true` if the child task was added to the group;
328+
/// otherwise `false`.
329+
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
330+
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)")
331+
#endif
332+
@_allowFeatureSuppression(IsolatedAny)
333+
@_alwaysEmitIntoClient
334+
public mutating func addTaskUnlessCancelled(
335+
operation: __owned @Sendable @escaping @isolated(any) () async -> Void
336+
) -> Bool {
337+
#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup
338+
let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false)
339+
340+
guard canAdd else {
341+
// the group is cancelled and is not accepting any new work
342+
return false
343+
}
344+
345+
let flags = taskCreateFlags(
346+
priority: nil, isChildTask: true, copyTaskLocals: false,
347+
inheritContext: false, enqueueJob: true,
348+
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true
349+
)
350+
351+
// Create the task in this group.
352+
#if $BuiltinCreateTask
353+
let builtinSerialExecutor =
354+
Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor
355+
356+
_ = Builtin.createDiscardingTask(flags: flags,
357+
initialSerialExecutor: builtinSerialExecutor,
358+
taskGroup: _group,
359+
operation: operation)
360+
#elseif $BuiltinCreateAsyncDiscardingTaskInGroup
361+
_ = Builtin.createAsyncDiscardingTaskInGroup(flags, _group, operation)
362+
#else
363+
// This builtin happens to work, but the signature of the operation is
364+
// incorrect, as the discarding group uses Void, and therefore has less
365+
// generic parameters than the operation expected to be passed to
366+
// createAsyncTaskInGroup. While this happened to work on some platforms,
367+
// on others this causes issues, e.g. on wasm;
368+
//
369+
// Keep this branch for compatibility with old compilers, but use the
370+
// correct 'createAsyncDiscardingTaskInGroup' when available (and a recent
371+
// enough compiler is used).
372+
_ = Builtin.createAsyncTaskInGroup(flags, _group, operation)
373+
#endif
374+
375+
return true
376+
#else
377+
fatalError("Unsupported Swift compiler")
378+
#endif
379+
}
380+
381+
// The Embedded clones of the task-creation routines.
382+
#else
383+
163384
/// Adds a child task to the group.
164385
///
165386
/// - Parameters:
@@ -338,6 +559,8 @@ public struct DiscardingTaskGroup {
338559
#endif
339560
}
340561

562+
#endif // $Embedded
563+
341564
/// A Boolean value that indicates whether the group has any remaining tasks.
342565
///
343566
/// At the start of the body of a `withDiscardingTaskGroup(of:returning:body:)` call,
@@ -604,9 +827,10 @@ public struct ThrowingDiscardingTaskGroup<Failure: Error> {
604827
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)")
605828
#endif
606829
@_alwaysEmitIntoClient
830+
@_allowFeatureSuppression(IsolatedAny)
607831
public mutating func addTask(
608832
priority: TaskPriority? = nil,
609-
operation: __owned @Sendable @escaping () async throws -> Void
833+
operation: __owned @Sendable @escaping @isolated(any) () async throws -> Void
610834
) {
611835
#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup
612836
let flags = taskCreateFlags(
@@ -616,7 +840,15 @@ public struct ThrowingDiscardingTaskGroup<Failure: Error> {
616840
)
617841

618842
// Create the task in this group.
619-
#if $BuiltinCreateAsyncDiscardingTaskInGroup
843+
#if $BuiltinCreateTask
844+
let builtinSerialExecutor =
845+
Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor
846+
847+
_ = Builtin.createDiscardingTask(flags: flags,
848+
initialSerialExecutor: builtinSerialExecutor,
849+
taskGroup: _group,
850+
operation: operation)
851+
#elseif $BuiltinCreateAsyncDiscardingTaskInGroup
620852
_ = Builtin.createAsyncDiscardingTaskInGroup(flags, _group, operation)
621853
#else
622854
// This builtin happens to work, but the signature of the operation is
@@ -639,9 +871,10 @@ public struct ThrowingDiscardingTaskGroup<Failure: Error> {
639871
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)")
640872
#endif
641873
@_alwaysEmitIntoClient
874+
@_allowFeatureSuppression(IsolatedAny)
642875
public mutating func addTaskUnlessCancelled(
643876
priority: TaskPriority? = nil,
644-
operation: __owned @Sendable @escaping () async throws -> Void
877+
operation: __owned @Sendable @escaping @isolated(any) () async throws -> Void
645878
) -> Bool {
646879
#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup
647880
let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false)
@@ -658,7 +891,15 @@ public struct ThrowingDiscardingTaskGroup<Failure: Error> {
658891
)
659892

660893
// Create the task in this group.
661-
#if $BuiltinCreateAsyncDiscardingTaskInGroup
894+
#if $BuiltinCreateTask
895+
let builtinSerialExecutor =
896+
Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor
897+
898+
_ = Builtin.createDiscardingTask(flags: flags,
899+
initialSerialExecutor: builtinSerialExecutor,
900+
taskGroup: _group,
901+
operation: operation)
902+
#elseif $BuiltinCreateAsyncDiscardingTaskInGroup
662903
_ = Builtin.createAsyncDiscardingTaskInGroup(flags, _group, operation)
663904
#else
664905
// This builtin happens to work, but the signature of the operation is

0 commit comments

Comments
 (0)