Tasks that get stuck
- The problem
- Sometimes tasks wait for each other forever.
- An example
- Two account transfers each hold a lock the other needs. Neither can continue.
- What Nikaia checks
- A task that pauses while it holds a lock is rejected, since everyone who needs that value would wait too. For several locks at once,
update_alltakes them in one fixed order, so two transfers cannot hold one each and wait for the other. - The limit
- Nikaia does not yet reject a lock taken inside another lock when you write it by hand, so using
update_allfor several locks is up to you. And not every possible deadlock can be ruled out in every situation; where the analysis cannot decide, it does not claim the problem is solved.
Show the program and what Nikaia says
use std::time
struct Account {
balance: i64,
}
fn main() throws {
let a = SharedMut(Account { balance: 100 })
a.update fn(mut acc) {
acc.balance -= 30
time::sleep(5.millis())
}
}
$ nikaia check main.nika
error[NK2202]: `time::sleep` can pause, but you're holding a lock here.
--> main.nika:11:9
|
11 | time::sleep(5.millis())
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: While the lock is held, every other task that needs the value waits, so a pause here makes all of them wait too.
= help: Call `time::sleep` before taking the lock and pass its value in, or after it with what the block returned.
Moving money between two accounts takes both locks at once, always in the same order:
update_all(a, b) fn(mut from, mut to) {
from.balance -= 30
to.balance += 30
}
Go deeper
More under Very safe: Conflicting actionsA memory budget for every taskErrors that go unhandledBroken promises