What is actor reentrancy in Swift, and how can it cause bugs?
iOSSenior
In shortAn actor runs one piece of its code at a time, but every await inside an actor method is a point where other calls can run. State read before an await may have changed after it, causing lost updates and duplicate work.
Model answer
Actors prevent data races: only one piece of actor-isolated code runs at a time. But they’re reentrant: when an actor method suspends at an await, the actor is free to run other calls, and when the method resumes, the state may be different.
actor ImageCache {
private var images: [URL: Data] = [:]
func image(for url: URL) async throws -> Data {
if let cached = images[url] { return cached }
let data = try await download(url) // other calls run here
images[url] = data // two callers can both reach this
return data
}
}
Ten rows asking for the same avatar all miss the cache before the first download finishes, so the same file is downloaded ten times. A read-modify-write across an await (let v = count; await …; count = v + 1) loses updates the same way.
Fixes:
- Don’t hold state across
await: do the read-modify-write synchronously, or re-read after resuming. - Record intent before suspending: store the in-flight
Taskin the dictionary before awaiting it, so later callers join it. - Keep critical sections synchronous: if a change must be atomic, do it without suspension points.
Swift made actors reentrant on purpose: non-reentrant actors waiting on each other would deadlock. The trade-off is that you reason about each await as a place where the world can change.
What they're looking for
A precise explanation (serialized, not transactional), a concrete example like a cache that downloads the same URL twice, and fixes (no state held across await, re-check after suspension, storing in-flight tasks). Bonus for explaining why actors are reentrant (to avoid deadlocks).
Common mistakes
- Claiming actors make whole methods atomic.
- Proposing a lock held across an await as the fix.
- Not distinguishing a data race (prevented) from a logical race (still possible).