It turns out, if we just define the closure before passing it to concurrentPerform it works:
let closure = { (row: Int) in
let part1 = someData[row]
let part2 = moreData[row]
buffer[row] = simd_float4(lowHalf: part1, highHalf: part2)
}
DispatchQueue.concurrentPerform(iterations: capacity, execute: closure)
My guess is that closure is not Sendable here, so the compiler won't complain about the buffer access. And concurrentPerform is marked with @preconcurrency, so passing a non-sendable closure is also ok. But it feels wrong...
Is there a better pattern for this kind of work?