Unfortunately, actors are not a silver bullet for resolving high-level data races either :-(
In a way they could give "an illusion of correctness" because they eliminated all low-level data races (the presence of which (e.g. in a form of runtime traps) would otherwise bring developer's attention to potential issues).
Illustrating example
// Edit: simplified a bit
actor State {
private var state = 0
func get() async -> Int {
await someLogStatement()
return state
}
func set(_ newValue: Int) async {
await someLogStatement()
state = newValue
}
}
let state = State()
Task {
// data race
await state.set(state.get() + 1)
}
Task {
// data race
await state.set(state.get() + 1)
}
Task {
try await Task.sleep(nanoseconds: 100_000_000)
let result = await state.get()
print("resulting value is \(result)")
}
func someLogStatement() async {
try! await Task.sleep(nanoseconds: .random(in: 0 ..< 1000_000))
}
RunLoop.main.run(until: .distantFuture)
Concurrency is hard.