/*:#localized(key: "SemaphoreAsLock")
 # A semaphore of 1 as a lock (and why to be wary)

 Set the count to **1** and a semaphore becomes a **lock**, a gate that only
 one task can pass at a time. Everything between `wait()` and `signal()` is a
 **critical section**: code that must run without another thread interrupting
 it.

 Why would you need that? Because two tasks touching the same variable at once
 can corrupt it. The code below has 500 tasks each bump a shared tally.
 */
//#-editable-code
import Foundation

let lock = DispatchSemaphore(value: 1)        // one permit = a lock
let counter = DispatchQueue(label: "tally", attributes: .concurrent)
let group = DispatchGroup()
var ordersServed = 0

for _ in 1...500 {
    group.enter()
    counter.async {
        lock.wait()                            // only one task past here
        let seen = ordersServed                // read the tally
        Thread.sleep(forTimeInterval: 0.0001)  // pretend the update takes a moment
        ordersServed = seen + 1                // write it back
        lock.signal()                          // let the next one in
        group.leave()
    }
}

group.wait()
print("Orders served: \(ordersServed)")       // reliably 500
//#-end-editable-code
/*:#localized(key: "SemaphoreAsLock-2")
 **Watch it run:** you get `500`, every time.

 * callout(Try it): Comment out the `lock.wait()` and `lock.signal()` lines and
 run it a few times. You will get a number *less* than 500, and a
 different one each run. That's a **data race**: with the lock gone, several
 tasks read the same `seen` value, each adds one, and each writes the same
 result back, so those increments collapse into a single one. The lock forces
 the read and the write to happen as one unit, so no update is lost.

 This works, but reach for it carefully. A count-1 semaphore has the same
 fragile bookkeeping as `enter`/`leave`: forget a `signal()` on an error path
 and every other task waits forever (a **deadlock**). It also blocks real
 threads while they queue. For simple mutual exclusion there are lighter,
 safer tools (a serial queue, or `NSLock`), and Swift's modern answer,
 **actors**, makes this kind of shared-state protection a language feature you
 can't forget to balance. You'll meet the race itself head-on in the next
 chapter, and actors later in the book. For now, just know: a semaphore *can*
 be a lock, but "lock" is not its best job.

 [Previous](@previous)  |  page 7 of 8  |  [Next: Wrapping Up](@next)
 */
