|
1 | 1 | #if !os(WASI) && !os(Windows) && !os(Android)
|
2 | 2 | import Foundation
|
3 | 3 |
|
4 |
| - /// Perform an operation on the main serial executor. |
5 |
| - /// |
6 |
| - /// Some asynchronous code is [notoriously |
7 |
| - /// difficult](https://forums.swift.org/t/reliably-testing-code-that-adopts-swift-concurrency/57304) |
8 |
| - /// to test in Swift due to how suspension points are processed by the runtime. This function |
9 |
| - /// attempts to run all tasks spawned in the given operation serially and deterministically. It |
10 |
| - /// makes asynchronous tests faster and less flakey. |
11 |
| - /// |
12 |
| - /// ```swift |
13 |
| - /// await withMainSerialExecutor { |
14 |
| - /// // Everything performed in this scope is performed serially... |
15 |
| - /// } |
16 |
| - /// ``` |
17 |
| - /// |
18 |
| - /// See <doc:ReliablyTestingAsync> for more information on why this tool is needed to test |
19 |
| - /// async code and how to use it. |
20 |
| - /// |
21 |
| - /// > Warning: This API is only intended to be used from tests to make them more reliable. Please do |
22 |
| - /// > not use it from application code. |
23 |
| - /// > |
24 |
| - /// > We say that it "_attempts_ to run all tasks spawned in an operation serially and |
25 |
| - /// > deterministically" because under the hood it relies on a global, mutable variable in the Swift |
26 |
| - /// > runtime to do its job, and there are no scoping _guarantees_ should this mutable variable change |
27 |
| - /// > during the operation. |
28 |
| - /// |
29 |
| - /// - Parameter operation: An operation to be performed on the main serial executor. |
30 |
| - @MainActor |
31 |
| - public func withMainSerialExecutor( |
32 |
| - @_implicitSelfCapture operation: @Sendable () async throws -> Void |
33 |
| - ) async rethrows { |
34 |
| - let didUseMainSerialExecutor = uncheckedUseMainSerialExecutor |
35 |
| - defer { uncheckedUseMainSerialExecutor = didUseMainSerialExecutor } |
36 |
| - uncheckedUseMainSerialExecutor = true |
37 |
| - try await operation() |
38 |
| - } |
| 4 | + #if compiler(>=6) |
| 5 | + /// Perform an operation on the main serial executor. |
| 6 | + /// |
| 7 | + /// Some asynchronous code is [notoriously |
| 8 | + /// difficult](https://forums.swift.org/t/reliably-testing-code-that-adopts-swift-concurrency/57304) |
| 9 | + /// to test in Swift due to how suspension points are processed by the runtime. This function |
| 10 | + /// attempts to run all tasks spawned in the given operation serially and deterministically. It |
| 11 | + /// makes asynchronous tests faster and less flakey. |
| 12 | + /// |
| 13 | + /// ```swift |
| 14 | + /// await withMainSerialExecutor { |
| 15 | + /// // Everything performed in this scope is performed serially... |
| 16 | + /// } |
| 17 | + /// ``` |
| 18 | + /// |
| 19 | + /// See <doc:ReliablyTestingAsync> for more information on why this tool is needed to test |
| 20 | + /// async code and how to use it. |
| 21 | + /// |
| 22 | + /// > Warning: This API is only intended to be used from tests to make them more reliable. Please do |
| 23 | + /// > not use it from application code. |
| 24 | + /// > |
| 25 | + /// > We say that it "_attempts_ to run all tasks spawned in an operation serially and |
| 26 | + /// > deterministically" because under the hood it relies on a global, mutable variable in the Swift |
| 27 | + /// > runtime to do its job, and there are no scoping _guarantees_ should this mutable variable change |
| 28 | + /// > during the operation. |
| 29 | + /// |
| 30 | + /// - Parameter operation: An operation to be performed on the main serial executor. |
| 31 | + @MainActor |
| 32 | + public func withMainSerialExecutor( |
| 33 | + @_implicitSelfCapture operation: @isolated(any) () async throws -> Void |
| 34 | + ) async rethrows { |
| 35 | + let didUseMainSerialExecutor = uncheckedUseMainSerialExecutor |
| 36 | + defer { uncheckedUseMainSerialExecutor = didUseMainSerialExecutor } |
| 37 | + uncheckedUseMainSerialExecutor = true |
| 38 | + try await operation() |
| 39 | + } |
| 40 | + #else |
| 41 | + @MainActor |
| 42 | + public func withMainSerialExecutor( |
| 43 | + @_implicitSelfCapture operation: @Sendable () async throws -> Void |
| 44 | + ) async rethrows { |
| 45 | + let didUseMainSerialExecutor = uncheckedUseMainSerialExecutor |
| 46 | + defer { uncheckedUseMainSerialExecutor = didUseMainSerialExecutor } |
| 47 | + uncheckedUseMainSerialExecutor = true |
| 48 | + try await operation() |
| 49 | + } |
| 50 | + #endif |
39 | 51 |
|
40 | 52 | /// Perform an operation on the main serial executor.
|
41 | 53 | ///
|
42 |
| - /// A synchronous version of ``withMainSerialExecutor(operation:)-79jpc`` that can be used in |
| 54 | + /// A synchronous version of ``withMainSerialExecutor(operation:)-7fqt1`` that can be used in |
43 | 55 | /// `XCTestCase.invokeTest` to ensure all async tests are performed serially:
|
44 | 56 | ///
|
45 | 57 | /// ```swift
|
|
65 | 77 | /// Overrides Swift's global executor with the main serial executor in an unchecked fashion.
|
66 | 78 | ///
|
67 | 79 | /// > Warning: When set to `true`, all tasks will be enqueued on the main serial executor till set
|
68 |
| - /// > back to `false`. Consider using ``withMainSerialExecutor(operation:)-79jpc``, instead, which |
| 80 | + /// > back to `false`. Consider using ``withMainSerialExecutor(operation:)-7fqt1``, instead, which |
69 | 81 | /// > scopes this work to the duration of a given operation.
|
70 | 82 | public var uncheckedUseMainSerialExecutor: Bool {
|
71 | 83 | get { swift_task_enqueueGlobal_hook != nil }
|
|
0 commit comments