Skip to content

Instantly share code, notes, and snippets.

@MojtabaHs
Created November 25, 2023 12:17
Show Gist options
  • Save MojtabaHs/e074de42efc2038edbde1d9509c91182 to your computer and use it in GitHub Desktop.
Save MojtabaHs/e074de42efc2038edbde1d9509c91182 to your computer and use it in GitHub Desktop.
Performs an async task in a sync context
import Foundation
public extension Task where Failure == Error {
/// Performs an async task in a sync context.
///
/// - Note: This function blocks the thread until the given operation is finished. The caller is responsible for managing multithreading.
@discardableResult
static func synchronous(
priority: TaskPriority? = nil,
@_implicitSelfCapture operation: @escaping @Sendable () async -> Success
) -> Success {
SynchronousTask().perform(priority: priority) { await operation() }
}
/// Performs an async task in a sync context.
///
/// - Note: This function blocks the thread until the given operation is finished. The caller is responsible for managing multithreading.
@discardableResult
static func synchronous(
priority: TaskPriority? = nil,
@_implicitSelfCapture operation: @escaping @Sendable () async throws -> Success
) throws -> Success {
try SynchronousTask().perform(priority: priority) { try await operation() }
}
}
private final class SynchronousTask<Success, Failure: Error> {
private(set) var result: Result<Success, Failure>? = nil
}
private extension SynchronousTask where Failure == Never {
func perform(priority: TaskPriority? = nil, operation: @Sendable @escaping () async -> Success) -> Success {
// Define a semaphore to wait
let semaphore = DispatchSemaphore(value: 0)
Task(priority: priority) {
// Define the exit signal
defer { semaphore.signal() }
// Wait for the result
self.result = await Task(operation: operation).result
}
// Wait for the signal
semaphore.wait()
// Return the result
guard let result = self.result else { fatalError("Result not found") }
switch result {
case let .success(value): return value
}
}
}
private extension SynchronousTask where Failure == Error {
func perform(priority: TaskPriority? = nil, operation: @Sendable @escaping () async throws -> Success) throws -> Success {
// Define a semaphore to wait
let semaphore = DispatchSemaphore(value: 0)
Task(priority: priority) {
// Define the exit signal
defer { semaphore.signal() }
// Wait for the result
self.result = await Task(operation: operation).result
}
// Wait for the signal
semaphore.wait()
// Return the result
guard let result = self.result else { fatalError("Result not found") }
switch result {
case let .success(value): return value
case let .failure(error): throw error
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment