Skip to content

Commit 70b1c0e

Browse files
ktosoamartini51
andauthored
[Concurrency] Major cleanup of assert/precondition/assume isolated docs (#70800)
Co-authored-by: Alex Martini <[email protected]>
1 parent ef80d77 commit 70b1c0e

File tree

3 files changed

+229
-81
lines changed

3 files changed

+229
-81
lines changed

stdlib/public/Concurrency/ExecutorAssertions.swift

Lines changed: 137 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import SwiftShims
2020

2121
@available(SwiftStdlib 5.1, *)
2222
extension SerialExecutor {
23-
/// Unconditionally if the current task is executing on the expected serial executor,
24-
/// and if not crash the program offering information about the executor mismatch.
23+
/// Stops program execution if the current task is not executing on this
24+
/// serial executor.
2525
///
2626
/// This function's effect varies depending on the build flag used:
2727
///
@@ -32,9 +32,18 @@ extension SerialExecutor {
3232
/// * In `-O` builds (the default for Xcode's Release configuration), stops
3333
/// program execution.
3434
///
35-
/// * In `-Ounchecked` builds, the optimizer may assume that this function is
36-
/// never called. Failure to satisfy that assumption is a serious
37-
/// programming error.
35+
/// - Note: Because this check is performed against the actor's serial executor,
36+
/// if another actor uses the same serial executor--by using
37+
/// that actor's serial executor as its own ``Actor/unownedExecutor``--this
38+
/// check will succeed. From a concurrency safety perspective, the
39+
/// serial executor guarantees mutual exclusion of those two actors.
40+
///
41+
/// - Parameters:
42+
/// - message: The message to print if the assertion fails.
43+
/// - file: The file name to print if the assertion fails. The default value is
44+
/// the file where this method was called.
45+
/// - line: The line number to print if the assertion fails The default value is
46+
/// the line where this method was called.
3847
@available(SwiftStdlib 5.1, *)
3948
#if !$Embedded
4049
@backDeployed(before: SwiftStdlib 5.9)
@@ -60,8 +69,8 @@ extension SerialExecutor {
6069

6170
@available(SwiftStdlib 5.1, *)
6271
extension Actor {
63-
/// Unconditionally if the current task is executing on the serial executor of the passed in `actor`,
64-
/// and if not crash the program offering information about the executor mismatch.
72+
/// Stops program execution if the current task is not executing on this
73+
/// actor's serial executor.
6574
///
6675
/// This function's effect varies depending on the build flag used:
6776
///
@@ -72,9 +81,18 @@ extension Actor {
7281
/// * In `-O` builds (the default for Xcode's Release configuration), stops
7382
/// program execution.
7483
///
75-
/// * In `-Ounchecked` builds, the optimizer may assume that this function is
76-
/// never called. Failure to satisfy that assumption is a serious
77-
/// programming error.
84+
/// - Note: This check is performed against the actor's serial executor,
85+
/// meaning that / if another actor uses the same serial executor--by using
86+
/// that actor's serial executor as its own ``Actor/unownedExecutor``--this
87+
/// check will succeed , as from a concurrency safety perspective, the
88+
/// serial executor guarantees mutual exclusion of those two actors.
89+
///
90+
/// - Parameters:
91+
/// - message: The message to print if the assertion fails.
92+
/// - file: The file name to print if the assertion fails. The default is
93+
/// where this method was called.
94+
/// - line: The line number to print if the assertion fails The default is
95+
/// where this method was called.
7896
@available(SwiftStdlib 5.1, *)
7997
#if !$Embedded
8098
@backDeployed(before: SwiftStdlib 5.9)
@@ -100,8 +118,8 @@ extension Actor {
100118

101119
@available(SwiftStdlib 5.1, *)
102120
extension GlobalActor {
103-
/// Unconditionally if the current task is executing on the serial executor of the passed in `actor`,
104-
/// and if not crash the program offering information about the executor mismatch.
121+
/// Stops program execution if the current task is not executing on this
122+
/// actor's serial executor.
105123
///
106124
/// This function's effect varies depending on the build flag used:
107125
///
@@ -112,9 +130,18 @@ extension GlobalActor {
112130
/// * In `-O` builds (the default for Xcode's Release configuration), stops
113131
/// program execution.
114132
///
115-
/// * In `-Ounchecked` builds, the optimizer may assume that this function is
116-
/// never called. Failure to satisfy that assumption is a serious
117-
/// programming error.
133+
/// - Note: This check is performed against the actor's serial executor,
134+
/// meaning that / if another actor uses the same serial executor--by using
135+
/// that actor's serial executor as its own ``Actor/unownedExecutor``--this
136+
/// check will succeed , as from a concurrency safety perspective, the
137+
/// serial executor guarantees mutual exclusion of those two actors.
138+
///
139+
/// - Parameters:
140+
/// - message: The message to print if the assertion fails.
141+
/// - file: The file name to print if the assertion fails. The default is
142+
/// where this method was called.
143+
/// - line: The line number to print if the assertion fails The default is
144+
/// where this method was called.
118145
@available(SwiftStdlib 5.1, *)
119146
#if !$Embedded
120147
@backDeployed(before: SwiftStdlib 5.9)
@@ -133,18 +160,30 @@ extension GlobalActor {
133160

134161
@available(SwiftStdlib 5.1, *)
135162
extension SerialExecutor {
136-
/// Performs an executor check in debug builds.
163+
/// Stops program execution if the current task is not executing on this
164+
/// serial executor.
165+
///
166+
/// This function's effect varies depending on the build flag used:
137167
///
138168
/// * In playgrounds and `-Onone` builds (the default for Xcode's Debug
139-
/// configuration): If `condition` evaluates to `false`, stop program
140-
/// execution in a debuggable state after printing `message`.
169+
/// configuration), stops program execution in a debuggable state after
170+
/// printing `message`.
141171
///
142172
/// * In `-O` builds (the default for Xcode's Release configuration),
143-
/// `condition` is not evaluated, and there are no effects.
173+
/// the isolation check is not performed and there are no effects.
174+
///
175+
/// - Note: This check is performed against the actor's serial executor,
176+
/// meaning that / if another actor uses the same serial executor--by using
177+
/// that actor's serial executor as its own ``Actor/unownedExecutor``--this
178+
/// check will succeed , as from a concurrency safety perspective, the
179+
/// serial executor guarantees mutual exclusion of those two actors.
144180
///
145-
/// * In `-Ounchecked` builds, `condition` is not evaluated, but the optimizer
146-
/// may assume that it *always* evaluates to `true`. Failure to satisfy that
147-
/// assumption is a serious programming error.
181+
/// - Parameters:
182+
/// - message: The message to print if the assertion fails.
183+
/// - file: The file name to print if the assertion fails. The default is
184+
/// where this method was called.
185+
/// - line: The line number to print if the assertion fails The default is
186+
/// where this method was called.
148187
@available(SwiftStdlib 5.1, *)
149188
#if !$Embedded
150189
@backDeployed(before: SwiftStdlib 5.9)
@@ -170,18 +209,30 @@ extension SerialExecutor {
170209

171210
@available(SwiftStdlib 5.1, *)
172211
extension Actor {
173-
/// Performs an executor check in debug builds.
212+
/// Stops program execution if the current task is not executing on this
213+
/// actor's serial executor.
214+
///
215+
/// This function's effect varies depending on the build flag used:
174216
///
175217
/// * In playgrounds and `-Onone` builds (the default for Xcode's Debug
176-
/// configuration): If `condition` evaluates to `false`, stop program
177-
/// execution in a debuggable state after printing `message`.
218+
/// configuration), stops program execution in a debuggable state after
219+
/// printing `message`.
178220
///
179221
/// * In `-O` builds (the default for Xcode's Release configuration),
180-
/// `condition` is not evaluated, and there are no effects.
222+
/// the isolation check is not performed and there are no effects.
223+
///
224+
/// - Note: This check is performed against the actor's serial executor,
225+
/// meaning that / if another actor uses the same serial executor--by using
226+
/// that actor's serial executor as its own ``Actor/unownedExecutor``--this
227+
/// check will succeed , as from a concurrency safety perspective, the
228+
/// serial executor guarantees mutual exclusion of those two actors.
181229
///
182-
/// * In `-Ounchecked` builds, `condition` is not evaluated, but the optimizer
183-
/// may assume that it *always* evaluates to `true`. Failure to satisfy that
184-
/// assumption is a serious programming error.
230+
/// - Parameters:
231+
/// - message: The message to print if the assertion fails.
232+
/// - file: The file name to print if the assertion fails. The default is
233+
/// where this method was called.
234+
/// - line: The line number to print if the assertion fails The default is
235+
/// where this method was called.
185236
@available(SwiftStdlib 5.1, *)
186237
#if !$Embedded
187238
@backDeployed(before: SwiftStdlib 5.9)
@@ -208,18 +259,30 @@ extension Actor {
208259

209260
@available(SwiftStdlib 5.1, *)
210261
extension GlobalActor {
211-
/// Performs an executor check in debug builds.
262+
/// Stops program execution if the current task is not executing on this
263+
/// actor's serial executor.
264+
///
265+
/// This function's effect varies depending on the build flag used:
212266
///
213267
/// * In playgrounds and `-Onone` builds (the default for Xcode's Debug
214-
/// configuration): If `condition` evaluates to `false`, stop program
215-
/// execution in a debuggable state after printing `message`.
268+
/// configuration), stops program execution in a debuggable state after
269+
/// printing `message`.
216270
///
217271
/// * In `-O` builds (the default for Xcode's Release configuration),
218-
/// `condition` is not evaluated, and there are no effects.
272+
/// the isolation check is not performed and there are no effects.
219273
///
220-
/// * In `-Ounchecked` builds, `condition` is not evaluated, but the optimizer
221-
/// may assume that it *always* evaluates to `true`. Failure to satisfy that
222-
/// assumption is a serious programming error.
274+
/// - Note: This check is performed against the actor's serial executor,
275+
/// meaning that / if another actor uses the same serial executor--by using
276+
/// that actor's serial executor as its own ``Actor/unownedExecutor``--this
277+
/// check will succeed , as from a concurrency safety perspective, the
278+
/// serial executor guarantees mutual exclusion of those two actors.
279+
///
280+
/// - Parameters:
281+
/// - message: The message to print if the assertion fails.
282+
/// - file: The file name to print if the assertion fails. The default is
283+
/// where this method was called.
284+
/// - line: The line number to print if the assertion fails The default is
285+
/// where this method was called.
223286
@available(SwiftStdlib 5.1, *)
224287
#if !$Embedded
225288
@backDeployed(before: SwiftStdlib 5.9)
@@ -238,19 +301,47 @@ extension GlobalActor {
238301

239302
@available(SwiftStdlib 5.1, *)
240303
extension Actor {
241-
/// A safe way to synchronously assume that the current execution context belongs to the passed in actor.
304+
/// Assume that the current task is executing on this actor's serial executor,
305+
/// or stop program execution otherwise.
306+
///
307+
/// You call this method to *assume and verify* that the currently
308+
/// executing synchronous function is actually executing on the serial
309+
/// executor of this actor.
310+
///
311+
/// If that is the case, the operation is invoked with an `isolated` version
312+
/// of the actor, allowing synchronous access to actor local state without
313+
/// hopping through asynchronous boundaries.
314+
///
315+
/// If the current context is not running on the actor's serial executor, or
316+
/// if the actor is a reference to a remote actor, this method will crash
317+
/// with a fatal error (similar to ``preconditionIsolated()``).
318+
///
319+
/// Note that this check is performed against the passed in actor's serial
320+
/// executor, meaning that if another actor uses the same serial executor--by
321+
/// using that actor's ``Actor/unownedExecutor`` as its own
322+
/// ``Actor/unownedExecutor``--this check will succeed, as from a concurrency
323+
/// safety perspective, the serial executor guarantees mutual exclusion of
324+
/// those two actors.
242325
///
243-
/// This API should only be used as last resort, when it is not possible to express the current
244-
/// execution context definitely belongs to the specified actor in other ways. E.g. one may need to use
245-
/// this in a delegate style API, where a synchronous method is guaranteed to be called by the
246-
/// specified actor, however it is not possible to move this method as being declared on the specified actor.
326+
/// This method can only be used from synchronous functions, as asynchronous
327+
/// functions should instead perform a normal method call to the actor, which
328+
/// will hop task execution to the target actor if necessary.
247329
///
248-
/// - Warning: If the current executor is *not* the expected serial executor, this function will crash.
330+
/// - Note: This check is performed against the actor's serial executor,
331+
/// meaning that / if another actor uses the same serial executor--by using
332+
/// another actor's executor as its own ``Actor/unownedExecutor``
333+
/// --this check will succeed , as from a concurrency safety perspective,
334+
/// the serial executor guarantees mutual exclusion of those two actors.
249335
///
250-
/// Note that this check is performed against the passed in actor's serial executor, meaning that
251-
/// if another actor uses the same serial executor--by using that actor's ``Actor/unownedExecutor``
252-
/// as its own ``Actor/unownedExecutor``--this check will succeed, as from a concurrency safety
253-
/// perspective, the serial executor guarantees mutual exclusion of those two actors.
336+
/// - Parameters:
337+
/// - operation: the operation that will be executed if the current context
338+
/// is executing on the actors serial executor.
339+
/// - file: The file name to print if the assertion fails. The default is
340+
/// where this method was called.
341+
/// - line: The line number to print if the assertion fails The default is
342+
/// where this method was called.
343+
/// - Returns: the return value of the `operation`
344+
/// - Throws: rethrows the `Error` thrown by the operation if it threw
254345
@available(SwiftStdlib 5.1, *)
255346
#if !$Embedded
256347
@backDeployed(before: SwiftStdlib 5.9)

stdlib/public/Concurrency/MainActor.swift

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,19 +101,41 @@ extension MainActor {
101101

102102
@available(SwiftStdlib 5.1, *)
103103
extension MainActor {
104-
/// A safe way to synchronously assume that the current execution context belongs to the MainActor.
104+
/// Assume that the current task is executing on the main actor's
105+
/// serial executor, or stop program execution.
105106
///
106-
/// This API should only be used as last resort, when it is not possible to express the current
107-
/// execution context definitely belongs to the main actor in other ways. E.g. one may need to use
108-
/// this in a delegate style API, where a synchronous method is guaranteed to be called by the
109-
/// main actor, however it is not possible to annotate this legacy API with `@MainActor`.
107+
/// This method allows to *assume and verify* that the currently
108+
/// executing synchronous function is actually executing on the serial
109+
/// executor of the MainActor.
110110
///
111-
/// - Warning: If the current executor is *not* the MainActor's serial executor, this function will crash.
111+
/// If that is the case, the operation is invoked with an `isolated` version
112+
/// of the actor, / allowing synchronous access to actor local state without
113+
/// hopping through asynchronous boundaries.
112114
///
113-
/// Note that this check is performed against the MainActor's serial executor, meaning that
114-
/// if another actor uses the same serial executor--by using ``MainActor/sharedUnownedExecutor``
115-
/// as its own ``Actor/unownedExecutor``--this check will succeed, as from a concurrency safety
116-
/// perspective, the serial executor guarantees mutual exclusion of those two actors.
115+
/// If the current context is not running on the actor's serial executor, or
116+
/// if the actor is a reference to a remote actor, this method will crash
117+
/// with a fatal error (similar to ``preconditionIsolated()``).
118+
///
119+
/// This method can only be used from synchronous functions, as asynchronous
120+
/// functions should instead perform a normal method call to the actor, which
121+
/// will hop task execution to the target actor if necessary.
122+
///
123+
/// - Note: This check is performed against the MainActor's serial executor,
124+
/// meaning that / if another actor uses the same serial executor--by using
125+
/// ``MainActor/sharedUnownedExecutor`` as its own
126+
/// ``Actor/unownedExecutor``--this check will succeed , as from a concurrency
127+
/// safety perspective, the serial executor guarantees mutual exclusion of
128+
/// those two actors.
129+
///
130+
/// - Parameters:
131+
/// - operation: the operation that will be executed if the current context
132+
/// is executing on the MainActor's serial executor.
133+
/// - file: The file name to print if the assertion fails. The default is
134+
/// where this method was called.
135+
/// - line: The line number to print if the assertion fails The default is
136+
/// where this method was called.
137+
/// - Returns: the return value of the `operation`
138+
/// - Throws: rethrows the `Error` thrown by the operation if it threw
117139
@available(SwiftStdlib 5.1, *)
118140
@backDeployed(before: SwiftStdlib 5.9)
119141
@_unavailableFromAsync(message: "await the call to the @MainActor closure directly")

0 commit comments

Comments
 (0)