I can demonstrate a data race in your approach. I think it's a bug.
struct Box<Value> {
var value: Value?
mutating func take() -> sending Value {
if let value {
self.value = nil
return value
} else {
preconditionFailure("Consumed twice")
}
}
}
func foo(_ closure: @escaping () async -> Void) async {
var bar1 = Box(value: closure)
var bar2 = Box(value: closure)
await withTaskGroup { taskGroup in
let closure1 = bar1.take()
let closure2 = bar2.take()
taskGroup.addTask {
await closure1()
}
taskGroup.addTask {
await closure2()
}
}
}
EDIT: I think there are at least two separate issues. One is what you mentioned: closure parameter should be marked as sending
. Another is that, even if it's a sending
closure, it's still possible to create data race by using your Box
struct. I've filed #83121 for the second issue.