@@ -16,98 +16,97 @@ import _Concurrency
16
16
// ==== -----------------------------------------------------------------------
17
17
// MARK: Precondition APIs
18
18
19
- /// Unconditionally if the current task is executing on the serial executor of the passed in `actor`,
20
- /// and if not crash the program offering information about the executor mismatch.
21
- ///
22
- /// This function's effect varies depending on the build flag used:
23
- ///
24
- /// * In playgrounds and `-Onone` builds (the default for Xcode's Debug
25
- /// configuration), stops program execution in a debuggable state after
26
- /// printing `message`.
27
- ///
28
- /// * In `-O` builds (the default for Xcode's Release configuration), stops
29
- /// program execution.
30
- ///
31
- /// * In `-Ounchecked` builds, the optimizer may assume that this function is
32
- /// never called. Failure to satisfy that assumption is a serious
33
- /// programming error.
34
- ///
35
- /// - Parameter actor: the actor whose serial executor we expect to be the current executor
36
19
@available ( SwiftStdlib 5 . 9 , * )
37
- public func preconditionOnExecutor(
38
- of actor : some DistributedActor ,
39
- _ message: @autoclosure ( ) -> String = String ( ) ,
40
- file: StaticString = #fileID, line: UInt = #line
41
- ) {
42
- guard _isDebugAssertConfiguration ( ) || _isReleaseAssertConfiguration ( ) else {
43
- return
44
- }
45
-
46
- guard __isLocalActor ( actor ) else {
47
- return
48
- }
20
+ extension DistributedActor {
21
+ /// Unconditionally if the current task is executing on the serial executor of the passed in `actor`,
22
+ /// and if not crash the program offering information about the executor mismatch.
23
+ ///
24
+ /// This function's effect varies depending on the build flag used:
25
+ ///
26
+ /// * In playgrounds and `-Onone` builds (the default for Xcode's Debug
27
+ /// configuration), stops program execution in a debuggable state after
28
+ /// printing `message`.
29
+ ///
30
+ /// * In `-O` builds (the default for Xcode's Release configuration), stops
31
+ /// program execution.
32
+ ///
33
+ /// * In `-Ounchecked` builds, the optimizer may assume that this function is
34
+ /// never called. Failure to satisfy that assumption is a serious
35
+ /// programming error.
36
+ @available ( SwiftStdlib 5 . 9 , * )
37
+ public nonisolated func preconditionIsolated(
38
+ _ message: @autoclosure ( ) -> String = String ( ) ,
39
+ file: StaticString = #fileID, line: UInt = #line
40
+ ) {
41
+ guard _isDebugAssertConfiguration ( ) || _isReleaseAssertConfiguration ( ) else {
42
+ return
43
+ }
44
+
45
+ guard __isLocalActor ( self ) else {
46
+ return
47
+ }
48
+
49
+ guard let unownedExecutor = self . localUnownedExecutor else {
50
+ preconditionFailure (
51
+ " Incorrect actor executor assumption; Distributed actor \( self ) is 'local' but has no executor! " ,
52
+ file: file, line: line)
53
+ }
54
+
55
+ let expectationCheck = _taskIsCurrentExecutor ( unownedExecutor. _executor)
49
56
50
- guard let unownedExecutor = actor . localUnownedExecutor else {
51
- preconditionFailure (
52
- " Incorrect actor executor assumption; Distributed actor \( actor ) is 'local' but has no executor! " ,
57
+ // TODO: offer information which executor we actually got
58
+ precondition ( expectationCheck,
59
+ // TODO: figure out a way to get the typed repr out of the unowned executor
60
+ " Incorrect actor executor assumption; Expected ' \( unownedExecutor) ' executor. \( message ( ) ) " ,
53
61
file: file, line: line)
54
62
}
55
-
56
- let expectationCheck = _taskIsCurrentExecutor ( unownedExecutor. _executor)
57
-
58
- // TODO: offer information which executor we actually got
59
- precondition ( expectationCheck,
60
- // TODO: figure out a way to get the typed repr out of the unowned executor
61
- " Incorrect actor executor assumption; Expected ' \( unownedExecutor) ' executor. \( message ( ) ) " ,
62
- file: file, line: line)
63
63
}
64
64
65
65
// ==== -----------------------------------------------------------------------
66
66
// MARK: Assert APIs
67
67
68
- /// Performs an executor check in debug builds.
69
- ///
70
- /// * In playgrounds and `-Onone` builds (the default for Xcode's Debug
71
- /// configuration): If `condition` evaluates to `false`, stop program
72
- /// execution in a debuggable state after printing `message`.
73
- ///
74
- /// * In `-O` builds (the default for Xcode's Release configuration),
75
- /// `condition` is not evaluated, and there are no effects.
76
- ///
77
- /// * In `-Ounchecked` builds, `condition` is not evaluated, but the optimizer
78
- /// may assume that it *always* evaluates to `true`. Failure to satisfy that
79
- /// assumption is a serious programming error.
80
- ///
81
- ///
82
- /// - Parameter actor: the actor whose serial executor we expect to be the current executor
83
68
@available ( SwiftStdlib 5 . 9 , * )
84
- @_transparent
85
- public func assertOnExecutor(
86
- of actor : some DistributedActor ,
87
- _ message: @autoclosure ( ) -> String = String ( ) ,
88
- file: StaticString = #fileID, line: UInt = #line
89
- ) {
90
- guard _isDebugAssertConfiguration ( ) else {
91
- return
92
- }
93
-
94
- guard __isLocalActor ( actor ) else {
95
- return
96
- }
97
-
98
- guard let unownedExecutor = actor . localUnownedExecutor else {
99
- preconditionFailure (
100
- " Incorrect actor executor assumption; Distributed actor \( actor ) is 'local' but has no executor! " ,
101
- file: file, line: line)
102
- }
103
-
104
- guard _taskIsCurrentExecutor ( unownedExecutor. _executor) else {
105
- // TODO: offer information which executor we actually got
106
- // TODO: figure out a way to get the typed repr out of the unowned executor
107
- let msg = " Incorrect actor executor assumption; Expected ' \( unownedExecutor) ' executor. \( message ( ) ) "
108
- /// TODO: implement the logic in-place perhaps rather than delegating to precondition()?
109
- assertionFailure ( msg, file: file, line: line) // short-cut so we get the exact same failure reporting semantics
110
- return
69
+ extension DistributedActor {
70
+ /// Performs an executor check in debug builds.
71
+ ///
72
+ /// * In playgrounds and `-Onone` builds (the default for Xcode's Debug
73
+ /// configuration): If `condition` evaluates to `false`, stop program
74
+ /// execution in a debuggable state after printing `message`.
75
+ ///
76
+ /// * In `-O` builds (the default for Xcode's Release configuration),
77
+ /// `condition` is not evaluated, and there are no effects.
78
+ ///
79
+ /// * In `-Ounchecked` builds, `condition` is not evaluated, but the optimizer
80
+ /// may assume that it *always* evaluates to `true`. Failure to satisfy that
81
+ /// assumption is a serious programming error.
82
+ @available ( SwiftStdlib 5 . 9 , * )
83
+ @_transparent
84
+ public nonisolated func assertIsolated(
85
+ _ message: @autoclosure ( ) -> String = String ( ) ,
86
+ file: StaticString = #fileID, line: UInt = #line
87
+ ) {
88
+ guard _isDebugAssertConfiguration ( ) else {
89
+ return
90
+ }
91
+
92
+ guard __isLocalActor ( self ) else {
93
+ return
94
+ }
95
+
96
+ guard let unownedExecutor = self . localUnownedExecutor else {
97
+ preconditionFailure (
98
+ " Incorrect actor executor assumption; Distributed actor \( self ) is 'local' but has no executor! " ,
99
+ file: file, line: line)
100
+ }
101
+
102
+ guard _taskIsCurrentExecutor ( unownedExecutor. _executor) else {
103
+ // TODO: offer information which executor we actually got
104
+ // TODO: figure out a way to get the typed repr out of the unowned executor
105
+ let msg = " Incorrect actor executor assumption; Expected ' \( unownedExecutor) ' executor. \( message ( ) ) "
106
+ /// TODO: implement the logic in-place perhaps rather than delegating to precondition()?
107
+ assertionFailure ( msg, file: file, line: line) // short-cut so we get the exact same failure reporting semantics
108
+ return
109
+ }
111
110
}
112
111
}
113
112
@@ -116,34 +115,58 @@ public func assertOnExecutor(
116
115
// MARK: Assume APIs
117
116
118
117
@available ( SwiftStdlib 5 . 9 , * )
119
- @_unavailableFromAsync ( message: " express the closure as an explicit function declared on the specified 'distributed actor' instead " )
120
- public func assumeOnLocalDistributedActorExecutor< Act: DistributedActor , T> (
121
- of actor : Act ,
122
- _ operation: ( isolated Act) throws -> T ,
123
- file: StaticString = #fileID, line: UInt = #line
124
- ) rethrows -> T {
125
- typealias YesActor = ( isolated Act) throws -> T
126
- typealias NoActor = ( Act ) throws -> T
127
-
128
- guard __isLocalActor ( actor ) else {
129
- fatalError ( " Cannot assume to be 'isolated \( Act . self) ' since distributed actor ' \( actor ) ' is remote. " )
130
- }
131
-
132
- /// This is guaranteed to be fatal if the check fails,
133
- /// as this is our "safe" version of this API.
134
- guard let executor = actor . localUnownedExecutor else {
135
- fatalError ( " Distributed local actor MUST have executor, but was nil " )
136
- }
137
- guard _taskIsCurrentExecutor ( executor. _executor) else {
138
- // TODO: offer information which executor we actually got when
139
- fatalError ( " Incorrect actor executor assumption; Expected same executor as \( actor ) . " , file: file, line: line)
140
- }
141
-
142
- // To do the unsafe cast, we have to pretend it's @escaping.
143
- return try withoutActuallyEscaping ( operation) {
144
- ( _ fn: @escaping YesActor ) throws -> T in
145
- let rawFn = unsafeBitCast ( fn, to: NoActor . self)
146
- return try rawFn ( actor )
118
+ extension DistributedActor {
119
+
120
+ /// Assume that the current actor is a local distributed actor and that the currently executing context is the same as that actors
121
+ /// serial executor, or crash.
122
+ ///
123
+ /// This method allows developers to *assume and verify* that the currently executing synchronous function
124
+ /// is actually executing on the serial executor that this distributed (local) actor is using.
125
+ ///
126
+ /// If that is the case, the operation is invoked with an `isolated` version of the actoe,
127
+ /// allowing synchronous access to actor local state without hopping through asynchronous boundaries.
128
+ ///
129
+ /// If the current context is not running on the actor's serial executor, or if the actor is a reference to a remote actor,
130
+ /// this method will crash with a fatalError (similar to ``preconditionIsolated()``).
131
+ ///
132
+ /// This method can only be used from synchronous functions, as asynchronous ones should instead
133
+ /// perform normal method call to the actor.
134
+ ///
135
+ /// - Parameters:
136
+ /// - operation: the operation that will be executed if the current context is executing on the actors serial executor, and the actor is a local reference
137
+ /// - file: source location where the assume call is made
138
+ /// - file: source location where the assume call is made
139
+ /// - Returns: the return value of the `operation`
140
+ /// - Throws: rethrows the `Error` thrown by the operation if it threw
141
+ @available ( SwiftStdlib 5 . 9 , * )
142
+ @_unavailableFromAsync ( message: " express the closure as an explicit function declared on the specified 'distributed actor' instead " )
143
+ public nonisolated func assumeIsolated< T> (
144
+ _ operation: ( isolated Self) throws -> T ,
145
+ file: StaticString = #fileID, line: UInt = #line
146
+ ) rethrows -> T {
147
+ typealias YesActor = ( isolated Self) throws -> T
148
+ typealias NoActor = ( Self ) throws -> T
149
+
150
+ guard __isLocalActor ( self ) else {
151
+ fatalError ( " Cannot assume to be 'isolated \( Self . self) ' since distributed actor ' \( self ) ' is a remote actor reference. " )
152
+ }
153
+
154
+ /// This is guaranteed to be fatal if the check fails,
155
+ /// as this is our "safe" version of this API.
156
+ guard let executor = self . localUnownedExecutor else {
157
+ fatalError ( " Distributed local actor MUST have executor, but was nil " )
158
+ }
159
+ guard _taskIsCurrentExecutor ( executor. _executor) else {
160
+ // TODO: offer information which executor we actually got when
161
+ fatalError ( " Incorrect actor executor assumption; Expected same executor as \( self ) . " , file: file, line: line)
162
+ }
163
+
164
+ // To do the unsafe cast, we have to pretend it's @escaping.
165
+ return try withoutActuallyEscaping ( operation) {
166
+ ( _ fn: @escaping YesActor ) throws -> T in
167
+ let rawFn = unsafeBitCast ( fn, to: NoActor . self)
168
+ return try rawFn ( self )
169
+ }
147
170
}
148
171
}
149
172
0 commit comments