While migrating to structured concurrency it may be sometimes convenient to temporarily make a blocking call to an async function - e.g.:
let x: Int = try blocking {
try await Task.sleep(nanoseconds: 1)
return 5
}
print(x)
I have this implemented like so at the moment, but I’m not in love with it:
@available(*, deprecated, message: "For use only as a last resort or to allow incremental migration to structured concurrency")
public func blocking<A>(_ ƒ: @escaping () async -> A) -> A {
let semaphore = DispatchSemaphore(value: 0)
let got = Got<A>()
Task(priority: .high) {
got.a = await ƒ()
semaphore.signal()
}
semaphore.wait()
return got.a!
}
And a throwing variant:
@available(*, deprecated, message: "For use only as a last resort or to allow incremental migration to structured concurrency")
public func blocking<A>(_ ƒ: @escaping () async throws -> A) throws -> A {
let semaphore = DispatchSemaphore(value: 0)
let got = Got<Result<A, Error>>()
Task(priority: .high) {
do { got.a = try await .success(ƒ()) }
catch { got.a = .failure(error) }
semaphore.signal()
}
semaphore.wait()
return try got.a!.get()
}
Where Got
is just a simple class to hold a reference to the result:
private class Got<A> {
var a: A?
}
Having reviewed the concurrency module of the Standard Library, I now believe that there is no idiomatic way of expressing, "I'm ok to block my synchronous function while awaiting for this particular async call".