Post not yet marked as solved
Post marked as unsolved with 2 replies, 145 views
Hi. I've been watching this video that probably many of you watched and noticed something strange during updates to the save functionality.
There is this snippet of code used in the result functionality (I cleaned it up and left only essentials for this question)
public func addDrink(mgCaffeine: Double, onDate date: Date) {
…
currentDrinks = drinks
Task {
await self.drinksUpdated()
}
}
private func drinksUpdated() async {
...
await store.save(currentDrinks)
}
addDrink is always called on the main actor so we are safe with updating instance variable currentDrinks. Everything is fine here. But then afterwards we schedule a task to perform saving of the current state. We await for the store because it is an actor and this means suspension point.
Now imagine that there are several calls happening to addDrink faster then store is able to save. This will mean that there will be several Tasks waiting for the access to the store. And there is no guarantee which task will run first so this means that we can't be sure which state of the currentDrinks will get written to the store last.
The same example in a bit different words.
We have 0 values in currentDrinks
addDrink is getting called
Now we have 1 value in currentDrinks
We fire a Task (lets call it Task1) to save to the store and it starts running
addDrink is getting called again
Now we have 2 values in currentDrinks
We schedule another Task (Task2) to save to the store while currentDrinks has 2 values and it suspends at the await store.save(currentDrinks) (first task is still running)
addDrink is getting called again
Now we have 3 values in currentDrinks
We schedule another Task (Task3) to save to the store while currentDrinks has 3 values and it suspends at the await store.save(currentDrinks) (first task is still running)
Task1 finishes
Now one of the tasks from Task2 and Task3 will get the chance to run but we don't control which one.
Task3 runs first and saves 3 values into store
Task2 runs and saves 2 values into store overwriting what Task3 did
We end up with 3 values in currentDrinks and 2 values in store
If we will relaunch app after this, we will basically loose 3rd drink that we added on the previous run.
Am I correct that this sample code contains a bug or am I missing something about Swift Concurrency?