Actor Reentrancy

That's actually very easy:

actor Foo {
  var state = 0

  func work() async {
    // assume it's still 0 at this time
    print(state)

    // suspension point of `work`
    await doSomeLongWork() 

    // this is no longer guaranteed to be `0` at this point
    print(state) 
  }
  
  func setState(to newValue: Int) {
    state = newValue
  }
}

As far as I can tell an actor only guarantees that it executes one Task at a time, but if the task gets suspended, it's an opportunity for other tasks to run, regardless if the previous task was finished or not. The actor only needs to guaranteed that no task run in parallel. If we would always wait until the active but suspended task would eventually finish we would have a great chance eventually hitting deadlocks. That said when doSomeLongWork is suspended, another task can access the actor and potentially use setState(to:) to mutate the value. Hence when doSomeLongWork resumes, state is no longer guaranteed to have the same value as before the previous suspension point.

Feel free to correct me or the terminology I used in case there is something wrong with that. ;)

12 Likes