@@ -16,98 +16,97 @@ import _Concurrency
1616// ==== -----------------------------------------------------------------------
1717// MARK: Precondition APIs
1818
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
3619@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)
4956
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 ( ) ) " ,
5361 file: file, line: line)
5462 }
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)
6363}
6464
6565// ==== -----------------------------------------------------------------------
6666// MARK: Assert APIs
6767
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
8368@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+ }
111110 }
112111}
113112
@@ -116,34 +115,58 @@ public func assertOnExecutor(
116115// MARK: Assume APIs
117116
118117@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+ }
147170 }
148171}
149172
0 commit comments