Conflicting actions
Two tasks that change the same balance
Two tasks update the same account balance. When they run at the same time, one update can overwrite the other, and money appears to disappear. Nikaia looks at what each task touches when the program is built. Two tasks that change the same value are refused, and so are two tasks that both write to standard output; standard output and standard error count as one destination, because they often end up in the same place. The message names what the two share. Two tasks that only read the same value are accepted. A lambda of par_iter, which runs on several cores at once, is held to the same rule: one that changes a name outside it is refused.
How to write it instead
The message names the ways out. Give each task a value of its own and combine the results afterwards, keep the balance in a SharedMut and change it through update, which takes the lock for you, or, where the order matters, write the two changes as ordinary statements one after the other.
Where the check stops
The compiler refuses what it can see. Where it cannot tell what a call touches, for example code in a Rust crate or a C library that starts threads of its own, it does not refuse, so the check is not a proof that every race is found. At such a boundary you hand over a copy rather than shared data. Today it also does not tell two writes to the same file from writes to two files: both are accepted.
Today's form and the design being built
The program below uses overlap, which the compiler accepts today. The specification replaces it with async written before an expression, which runs that expression beside the code after it (#983, not built yet). The same check then runs between a task and everything that runs beside it, other tasks included:
let mut balance = 100
let deposit = async fn { balance = balance + 50 }
let charge = async fn { balance = balance - 30 } // refused (NK2104): both change `balance`Show the program and what Nikaia says today
fn main() throws {
let mut balance = 100
overlap {
balance = balance + 50
balance = balance - 30
}
println(f"{balance}")
}
$ nikaia check main.nika
error[NK2104]: These two branches can't run at the same time.
--> main.nika:5:9
|
5 | balance = balance - 30
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: Both change `balance`.
= note: `overlap` runs its branches at once, so a binding one changes can't be touched by the other.
= help: If they should run in order, write them as ordinary statements.
Go deeper
More under Very safe: Tasks that get stuckA memory budget for every taskErrors that go unhandledBroken promises