In the following code, method testA in MyActor takes in an NonSendable value that it does not own. Here calling function testB with this NonSendable parameter will get an error since we are trying to send this NonSendable value crossing actor boundaries.
class NonSendable {
var value: Int = 0
func inc() {
value += 1
}
}
@concurrent
func testB(_ value: NonSendable) async {
value.inc()
}
actor MyActor {
let value = NonSendable()
func test() async {
await testA(a: value)
}
func testA(a: NonSendable) async {
await testB(a) // Sending 'a' risks causing data races
}
}
However, if I wrap the expression await testB(a) in a local closure, the error disappears
actor MyActor {
let value = NonSendable()
func test() async {
await testA(a: value)
}
func testA(a: NonSendable) async {
// no error
let c = {
await testB(a)
}
await c()
}
}
I’m not very sure yet but it does not look like a desired behaviour. Actually, if we intensionally change the inc function in NonSendable to something like this:
func inc() {
let newValue = value + 1;
print("something")
value = newValue
}
Then run the test method of an instance of MyActor with lots of tasks concurrently:
let a = MyActor()
await withTaskGroup { group in
for _ in 0 ..< 100 {
group.addTask {
await a.test()
}
}
}
We are getting smaller than 100 in the end, which seems like a data race problem.