Skip to content

Instantly share code, notes, and snippets.

@below
Created February 26, 2021 09:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save below/9ff99bde03fb9209d540c6ee0b96d3d3 to your computer and use it in GitHub Desktop.
Save below/9ff99bde03fb9209d540c6ee0b96d3d3 to your computer and use it in GitHub Desktop.
Controlling your Future: DispatchSemaphore and Combine
/*:
### Controlling your Future
For my little Combine Project, I need to control acccess to a critical
section. (For those following along it won't be hard to guess: I don't want the accessToken mechanism to be accessed from two places).
So, building on an existing [sample](https://medium.com/swiftcairo/avoiding-race-conditions-in-swift-9ccef0ec0b26), I built
something in combine.
As always, your feedback is appreciated!
*/
import Combine
import PlaygroundSupport
import Foundation
PlaygroundPage.current.needsIndefiniteExecution = true
var balance = 1200
enum ATMError: Error {
case insufficientFunds
case timedOut
}
extension ATMError: LocalizedError {
public var errorDescription: String? {
switch self {
case .insufficientFunds:
return "There are insufficent funds in your account"
case .timedOut:
return "Access to the ATM timed out"
}
}
}
var semaphore = DispatchSemaphore(value: 1)
func withdrawFuture(tag: String, value: Int) -> Future<Int, Error> {
return Future {
promise in
print("\(tag): Waiting for access")
DispatchQueue.global().async {
let timeout = DispatchTime.now() + .seconds(3)
switch semaphore
.wait(timeout: timeout)
{
case .success:
print("\(tag): checking if balance containing sufficent money")
if balance > value {
print("\(tag): Balance is sufficent, please wait while processing withdrawal")
// sleeping for some random time, simulating a long process
Thread.sleep(forTimeInterval: Double.random(in: 0...2))
balance -= value
print("\(tag): Done: \(value) has been withdrawed")
print("\(tag): current balance is \(balance)")
promise(.success(balance))
} else {
print("\(tag): Can't withdraw: insufficent balance")
promise(.failure(ATMError.insufficientFunds))
}
case .timedOut:
promise(.failure(ATMError.timedOut))
}
semaphore.signal()
}
}
}
let atm1 = withdrawFuture(tag: "FutureATM 1", value: 1000)
atm1
.sink {
switch $0 {
case .finished:
print ("ATM1 successfully finished")
case .failure(let error):
print ("ATM1 failed: \(error.localizedDescription)")
}
} receiveValue: { (balance) in
print ("ATM 1: balance is now \(balance)")
}
let atm2 = withdrawFuture(tag: "FutureATM 2", value: 800)
atm2
.sink {
switch $0 {
case .finished:
print ("ATM2 successfully finished")
case .failure(let error):
print ("ATM2 failed: \(error.localizedDescription)")
}
} receiveValue: { (balance) in
print ("ATM 2: balance is now \(balance)")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment