Created
October 10, 2023 20:31
-
-
Save brennanMKE/ebfc1c834758419ef2e434b2e529df5c to your computer and use it in GitHub Desktop.
Blocking code to integrate Swift Concurrency with legacy code which cannot be async
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Sometimes it is necessary to integrate straight-line code with async code | |
/// and this can be done using a semaphore. Keep in mind that doing this breaks | |
/// the Forward Progress policy that is required by Swift Concurrency. | |
/// | |
/// Warning: This code is not recommended for a production environment. | |
import Foundation | |
struct Blocker<Success, Failure: Error> { | |
private class ResultStore { | |
var result: Result<Success, Failure>? = nil | |
} | |
func run(operation: @escaping () async throws -> Success) -> Result<Success, Failure> { | |
let store = ResultStore() | |
let semaphore = DispatchSemaphore(value: 0) | |
Task { | |
do { | |
let value = try await operation() | |
store.result = .success(value) | |
} catch { | |
if let failure = error as? Failure { | |
store.result = .failure(failure) | |
} else { | |
fatalError("Error type not supported: \(error)") | |
} | |
} | |
semaphore.signal() | |
} | |
semaphore.wait() | |
return store.result! | |
} | |
} | |
struct Worker { | |
enum Failure: Error { | |
case numberTooHigh | |
} | |
func process(number: Int) async throws -> Int { | |
if number < 10 { | |
number * 2 | |
} else { | |
throw Failure.numberTooHigh | |
} | |
} | |
} | |
let doAsync = false | |
let worker = Worker() | |
if doAsync { | |
print("Async:") | |
Task { | |
for number in 1...10 { | |
do { | |
let output = try await worker.process(number: number) | |
print("\(number) -> \(output)") | |
} catch { | |
if let failure = error as? Worker.Failure { | |
switch failure { | |
case .numberTooHigh: | |
print("Error: Number was too high (\(number))") | |
} | |
} | |
} | |
} | |
print("Done") | |
} | |
} else { | |
print("Blocking:") | |
let blocker = Blocker<Int, Worker.Failure>() | |
for number in 1...10 { | |
let result = blocker.run { | |
try await worker.process(number: number) | |
} | |
do { | |
let output = try result.get() | |
print("\(number) -> \(output)") | |
} catch { | |
if let failure = error as? Worker.Failure { | |
switch failure { | |
case .numberTooHigh: | |
print("Error: Number was too high (\(number))") | |
} | |
} | |
} | |
} | |
print("Done") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment