I have been looking into sending
parameters from SE-0430. By marking a function parameter as sending I can pass it into a Task
class NonSendable {
init() {}
}
func sendToTask(_ t: sending NonSendable) {
Task {
print(t)
}
}
But I cannot do the same for a child task of TaskGroup
func sendToChildTask(_ t: sending NonSendable) async {
await withTaskGroup(of: Void.self) { group in
group.addTask {
print(t)
}
}
}
The above code produces the error Passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure
Is there a reason I can't do this with child tasks?
It appears to be related to the closure provided to withTaskGroup
because I can create an instance of NonSendable
inside that closure and then able to pass it into the child task.
3 Likes
You might find useful information in Sending, inout sending, Mutex in which I recently asked similar questions and got helpful answers!
I think in this case, it's because the compiler doesn't know how many times the closure passed to withTaskGroup
will be called — if withTaskGroup
called it twice, it would alias your NonSendable value.
1 Like
Looks like this was already reported. Wrong error when passing `sending` closure to `TaskGroup` · Issue #76242 · swiftlang/swift · GitHub
I think it is because the closure provided to withTaskGroup
is not tagged sending
.
You can confirm this doesn't help by wrapping up withTaskGroup
:
open class NonSendable {}
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
public func withTaskGroup2<ChildTaskResult, GroupResult>(
of childTaskResultType: ChildTaskResult.Type,
returning returnType: GroupResult.Type = GroupResult.self,
isolation: isolated (any Actor)? = #isolation,
body: sending (inout TaskGroup<ChildTaskResult>) async -> GroupResult
) async -> GroupResult where ChildTaskResult : Sendable {
// no warning:
await withTaskGroup(of: childTaskResultType, returning: returnType, isolation: isolation, body: body)
}
func sendToChildTask(_ t: sending NonSendable) async {
await withTaskGroup2(of: Void.self) { group in
// same warning:
group.addTask {
print(t)
}
}
}