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.