Skip to content

Instantly share code, notes, and snippets.

@kabiroberai
Last active April 23, 2024 16:47
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kabiroberai/89b370dcaee54a2f075fba7989b0b799 to your computer and use it in GitHub Desktop.
Save kabiroberai/89b370dcaee54a2f075fba7989b0b799 to your computer and use it in GitHub Desktop.
func sync() {
MainActor.runAsap {
print("hello")
try? await Task.sleep(for: .seconds(1))
print("goodbye")
}
print("after hello")
}
sync()
RunLoop.main.run()
// OUTPUT:
// hello
// after hello
// goodbye
import Foundation
extension MainActor {
/// Invoke `body`, running synchronously if possible.
///
/// This method is equivalent to `Task { @MainActor in <body> }`, except that
/// the first thread hop is elided if the caller is already on the main thread.
/// Thus if `<foo>` has no subsequent thread hops, it can run fully synchronously.
@discardableResult
public static func runAsap<Success>(
priority: TaskPriority? = nil,
body: @MainActor @Sendable @escaping () async -> Success
) -> Task<Success, Never> {
if Thread.isMainThread, #available(iOS 17, *) {
MainActor.unsafeAssumeIsolated {
Task.startOnMainActor {
await body()
}
}
} else {
Task(priority: priority) { await body() }
}
}
/// Invoke `body`, running synchronously if possible.
///
/// This method is equivalent to `Task { @MainActor in <body> }`, except that
/// the first thread hop is elided if the caller is already on the main thread.
/// Thus if `<foo>` has no subsequent thread hops, it can run fully synchronously.
@discardableResult
public static func runAsap<Success>(
priority: TaskPriority? = nil,
body: @MainActor @Sendable @escaping () async throws -> Success
) -> Task<Success, Error> {
if Thread.isMainThread, #available(iOS 17, *) {
MainActor.unsafeAssumeIsolated {
Task.startOnMainActor {
try await body()
}
}
} else {
Task(priority: priority) { try await body() }
}
}
/// `assumeIsolated` backported to pre-Swift 5.9 runtimes.
///
/// See [stdlib/public/Concurrency/MainActor.swift][1]
///
/// [1]: https://github.com/apple/swift/blob/7afa4cfdc69317559ca6a1b5e0e52cb557ec959b/stdlib/public/Concurrency/MainActor.swift#L119
@_unavailableFromAsync(message: "await the call to the @MainActor closure directly")
public static func unsafeAssumeIsolated<T>(
_ operation: @MainActor () throws -> T,
file: StaticString = #fileID, line: UInt = #line
) rethrows -> T {
typealias YesActor = @MainActor () throws -> T
typealias NoActor = () throws -> T
// To do the unsafe cast, we have to pretend it's @escaping.
return try withoutActuallyEscaping(operation) { (_ fn: @escaping YesActor) throws -> T in
let rawFn = unsafeBitCast(fn, to: NoActor.self)
return try rawFn()
}
}
}
// https://github.com/apple/swift/blob/98e65d015979c7b5a58a6ecf2d8598a6f7c85794/stdlib/public/Concurrency/Task.swift#L250-L294
extension Task where Failure == Never {
@_silgen_name("$sScTss5NeverORs_rlE16startOnMainActor8priority_ScTyxABGScPSg_xyYaYbScMYccntFZ")
@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *)
@MainActor
@discardableResult
fileprivate static func startOnMainActor(
priority: TaskPriority? = nil,
@_inheritActorContext @_implicitSelfCapture _ work: consuming @Sendable @escaping @MainActor() async -> Success
) -> Task<Success, Never>
}
extension Task where Failure == Error {
@_silgen_name("$sScTss5Error_pRs_rlE16startOnMainActor8priority_ScTyxsAA_pGScPSg_xyYaYbKScMYccntFZ")
@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *)
@MainActor
@discardableResult
fileprivate static func startOnMainActor(
priority: TaskPriority? = nil,
@_inheritActorContext @_implicitSelfCapture _ work: consuming @Sendable @escaping @MainActor() async throws -> Success
) -> Task<Success, Error>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment