/*:#localized(key: "BarrierFix")
 # Fix 2: a concurrent queue with a barrier (readers-writers)

 Serializing *everything* is heavier than you sometimes need. Reading the
 balance doesn't corrupt anything: a hundred tellers can *read* the drawer at
 once with no harm. It's only *writing* that needs to be alone. That's the
 **readers-writers pattern**: reads run concurrently; writes get exclusive
 access.

 GCD gives you this with a **barrier**. On a concurrent queue, a block
 submitted with the `.barrier` flag waits until every earlier block finishes,
 then runs completely alone, and only then lets later blocks resume. Reads are
 plain (concurrent); writes are barriers (exclusive).
 */
//#-editable-code
import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

// The pattern packaged up: safe to share because access is coordinated inside.
final class Account: @unchecked Sendable {
    private var balance = 0
    private let queue = DispatchQueue(label: "account", attributes: .concurrent)

    func deposit(_ amount: Int) {
        // Writes are barriers: exclusive access, no overlap.
        queue.async(flags: .barrier) {
            self.balance += amount
        }
    }

    var currentBalance: Int {
        // Reads run concurrently, but a read submitted after the writes
        // won't jump ahead of them.
        queue.sync { balance }
    }
}

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

for _ in 0..<1_000 {
    tellers.async(group: group) {
        account.deposit(1)
    }
}

group.notify(queue: .main) {
    print("Final balance: \(account.currentBalance) (expected 1000)")
    PlaygroundPage.current.finishExecution()
}
//#-end-editable-code
/*:#localized(key: "BarrierFix-2")
 **Watch it run:** `Final balance: 1000 (expected 1000)`, every run. The
 difference from Fix 1 is that reads (`currentBalance`) don't have to wait for
 each other; only writes do. When reads vastly outnumber writes, that's a real
 win. Notice too that the coordination now lives *inside* `Account`: callers
 can't forget to lock, because there's nothing to remember. Hiding the
 synchronization behind a clean interface is the single best habit in this
 chapter.

 [Previous](@previous)  |  page 5 of 9  |  [Next: Deadlock](@next)
 */
