/*:#localized(key: "DataRace")
 # The bug: a data race

 A **data race** is when two threads access the same piece of memory at the same
 time, and at least one of them is writing. A **race condition** is the broader
 family: any bug where the outcome depends on the unpredictable *timing* of
 concurrent work. Every data race is a race condition; not every race condition
 involves memory directly. In this chapter the two travel together.

 Here's the bank. One account, one balance. A thousand tellers each record a
 single deposit of `1`. The balance should end at exactly `1000`.
 */
//#-editable-code
import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

// A shared account. We PROMISE the compiler this is safe to share across
// threads (@unchecked Sendable), then we deliberately break that promise.
final class Account: @unchecked Sendable {
    var balance = 0
}

let account = Account()
let tellers = DispatchQueue(label: "tellers", attributes: .concurrent)
let group = DispatchGroup()
let totalDeposits = 1_000

for _ in 0..<totalDeposits {
    tellers.async(group: group) {
        // A deposit is three steps, not one:
        let current = account.balance   // 1. read the drawer
        let updated = current + 1       // 2. add your dollar
        account.balance = updated       // 3. put it back
    }
}

group.notify(queue: .main) {
    print("Final balance: \(account.balance) (expected \(totalDeposits))")
    PlaygroundPage.current.finishExecution()
}
//#-end-editable-code
/*:#localized(key: "DataRace-2")
 **Watch it run:** run the code a few times. You'll see something like
 `Final balance: 647 (expected 1000)`. Run again: `581`. Again: `621`. Again:
 `658`. **The number changes every run, and it's usually wrong.** No line of that
 code is misspelled. The logic (read, add one, write back) is exactly right for
 *one* teller. It falls apart because a thousand of them do it at once.

 Trace two tellers colliding:

 1. Teller A reads `balance` → `500`.
 2. Teller B reads `balance` → `500` (A hasn't written back yet).
 3. Teller A writes `501`.
 4. Teller B writes `501`.

 Two deposits happened. The balance went up by *one*. Teller B's dollar vanished,
 a **lost update**. That's the whole bug: `balance += 1` looks atomic, but it's a
 read, then a write, with a gap in between. On a concurrent queue, another teller
 can slip into that gap. (Splitting it into three explicit lines above just
 widens the gap so you see it every run; `account.balance += 1` on one line has
 the exact same flaw, it just loses updates slightly less often.)

 * callout(Try it): Drop the loop from `1_000` to `10` tellers and run it a few
 times. Does the balance still come out wrong as often?

 [Previous](@previous)  |  page 2 of 9  |  [Next: Hard to Catch](@next)
 */
