-
Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathMainActor.swift
167 lines (151 loc) · 6.12 KB
/
MainActor.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import Swift
#if !$Embedded
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@available(SwiftStdlib 5.1, *)
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
@globalActor public final actor MainActor: GlobalActor {
public static let shared = MainActor()
@inlinable
public nonisolated var unownedExecutor: UnownedSerialExecutor {
return unsafe UnownedSerialExecutor(Builtin.buildMainActorExecutorRef())
}
@inlinable
public static var sharedUnownedExecutor: UnownedSerialExecutor {
return unsafe UnownedSerialExecutor(Builtin.buildMainActorExecutorRef())
}
@inlinable
public nonisolated func enqueue(_ job: UnownedJob) {
_enqueueOnMain(job)
}
}
#else
/// A singleton actor whose executor is equivalent to the main
/// dispatch queue.
@available(SwiftStdlib 5.1, *)
@globalActor public final actor MainActor: GlobalActor {
public static let shared = MainActor()
@inlinable
public nonisolated var unownedExecutor: UnownedSerialExecutor {
return unsafe UnownedSerialExecutor(Builtin.buildMainActorExecutorRef())
}
@inlinable
public static var sharedUnownedExecutor: UnownedSerialExecutor {
return unsafe UnownedSerialExecutor(Builtin.buildMainActorExecutorRef())
}
@inlinable
public nonisolated func enqueue(_ job: UnownedJob) {
_enqueueOnMain(job)
}
}
#endif
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@available(SwiftStdlib 5.1, *)
extension MainActor {
/// Execute the given body closure on the main actor.
///
/// Historical ABI entry point, superseded by the Sendable version that is
/// also inlined to back-deploy a semantic fix where this operation would
/// not hop back at the end.
@usableFromInline
static func run<T>(
resultType: T.Type = T.self,
body: @MainActor @Sendable () throws -> T
) async rethrows -> T {
return try await body()
}
/// Execute the given body closure on the main actor.
@_alwaysEmitIntoClient
public static func run<T: Sendable>(
resultType: T.Type = T.self,
body: @MainActor @Sendable () throws -> T
) async rethrows -> T {
return try await body()
}
}
@available(SwiftStdlib 5.1, *)
extension MainActor {
/// Assume that the current task is executing on the main actor's
/// serial executor, or stop program execution.
///
/// This method allows to *assume and verify* that the currently
/// executing synchronous function is actually executing on the serial
/// executor of the MainActor.
///
/// If that is the case, the operation is invoked with an `isolated` version
/// of the actor, / allowing synchronous access to actor local state without
/// hopping through asynchronous boundaries.
///
/// If the current context is not running on the actor's serial executor, or
/// if the actor is a reference to a remote actor, this method will crash
/// with a fatal error (similar to ``preconditionIsolated()``).
///
/// This method can only be used from synchronous functions, as asynchronous
/// functions should instead perform a normal method call to the actor, which
/// will hop task execution to the target actor if necessary.
///
/// - Note: This check is performed against the MainActor's serial executor,
/// meaning that / if another actor uses the same serial executor--by using
/// ``MainActor/sharedUnownedExecutor`` as its own
/// ``Actor/unownedExecutor``--this check will succeed , as from a concurrency
/// safety perspective, the serial executor guarantees mutual exclusion of
/// those two actors.
///
/// - Parameters:
/// - operation: the operation that will be executed if the current context
/// is executing on the MainActor's serial executor.
/// - file: The file name to print if the assertion fails. The default is
/// where this method was called.
/// - line: The line number to print if the assertion fails The default is
/// where this method was called.
/// - Returns: the return value of the `operation`
/// - Throws: rethrows the `Error` thrown by the operation if it threw
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
@_unavailableFromAsync(message: "await the call to the @MainActor closure directly")
public static func assumeIsolated<T : Sendable>(
_ operation: @MainActor () throws -> T,
file: StaticString = #fileID, line: UInt = #line
) rethrows -> T {
typealias YesActor = @MainActor () throws -> T
typealias NoActor = () throws -> T
/// This is guaranteed to be fatal if the check fails,
/// as this is our "safe" version of this API.
let executor: Builtin.Executor = unsafe Self.shared.unownedExecutor.executor
guard _taskIsCurrentExecutor(executor) else {
// TODO: offer information which executor we actually got
#if !$Embedded
fatalError("Incorrect actor executor assumption; Expected same executor as \(self).", file: file, line: line)
#else
Builtin.int_trap()
#endif
}
// To do the unsafe cast, we have to pretend it's @escaping.
return try withoutActuallyEscaping(operation) {
(_ fn: @escaping YesActor) throws -> T in
let rawFn = unsafe unsafeBitCast(fn, to: NoActor.self)
return try rawFn()
}
}
@available(SwiftStdlib 5.9, *)
@usableFromInline
@_silgen_name("$sScM14assumeIsolated_4file4linexxyKScMYcXE_s12StaticStringVSutKlFZ")
internal static func __abi__assumeIsolated<T : Sendable>(
_ operation: @MainActor () throws -> T,
_ file: StaticString, _ line: UInt
) rethrows -> T {
try assumeIsolated(operation, file: file, line: line)
}
}
#endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
#endif // !$Embedded