Skip to content

Commit 163409e

Browse files
Use isolated(any) instead of Sendable for withMainSerialExecutor async operation (#46)
* Use isolated(any) instead of Sendable. * bump ci * bump CI * doc fixes --------- Co-authored-by: Stephen Celis <[email protected]>
1 parent 7a16fa5 commit 163409e

File tree

2 files changed

+76
-51
lines changed

2 files changed

+76
-51
lines changed

.github/workflows/ci.yml

+27-14
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,31 @@ jobs:
4343
run: swift test -c ${{ matrix.config }}
4444

4545
wasm:
46-
name: Wasm
47-
runs-on: ubuntu-latest
48-
steps:
49-
- uses: actions/checkout@v4
50-
- uses: bytecodealliance/actions/wasmtime/setup@v1
51-
- uses: swiftwasm/setup-swiftwasm@v1
52-
with:
53-
swift-version: "wasm-5.9.2-RELEASE"
54-
- name: Build tests
55-
run: swift build --triple wasm32-unknown-wasi --build-tests
56-
- name: Run tests
57-
run: wasmtime --dir . .build/debug/swift-concurrency-extrasPackageTests.wasm
46+
name: SwiftWasm
47+
runs-on: ubuntu-latest
48+
strategy:
49+
matrix:
50+
include:
51+
- toolchain: swift-DEVELOPMENT-SNAPSHOT-2024-09-12-a
52+
swift-sdk: swift-wasm-DEVELOPMENT-SNAPSHOT-2024-09-12-a
53+
checksum: 630ce23114580dfae029f832d8ccc8b1ba5136b7f915e82f8e405650e326b562
54+
steps:
55+
- uses: actions/checkout@v4
56+
- uses: bytecodealliance/actions/wasmtime/setup@v1
57+
- name: Install Swift and Swift SDK for WebAssembly
58+
run: |
59+
PREFIX=/opt/swift
60+
SWIFT_TOOLCHAIN_TAG="${{ matrix.toolchain }}"
61+
SWIFT_SDK_TAG="${{ matrix.swift-sdk }}"
62+
set -ex
63+
curl -f -o /tmp/swift.tar.gz "https://download.swift.org/development/ubuntu2204/$SWIFT_TOOLCHAIN_TAG/$SWIFT_TOOLCHAIN_TAG-ubuntu22.04.tar.gz"
64+
sudo mkdir -p $PREFIX; sudo tar -xzf /tmp/swift.tar.gz -C $PREFIX --strip-component 1
65+
$PREFIX/usr/bin/swift sdk install "https://github.com/swiftwasm/swift/releases/download/$SWIFT_SDK_TAG/$SWIFT_SDK_TAG-wasm32-unknown-wasi.artifactbundle.zip" --checksum ${{ matrix.checksum }}
66+
echo "$PREFIX/usr/bin" >> $GITHUB_PATH
67+
- name: Build tests
68+
run: swift build --swift-sdk wasm32-unknown-wasi --build-tests -Xlinker -z -Xlinker stack-size=$((1024 * 1024))
69+
- name: Run tests
70+
run: wasmtime --dir . .build/debug/swift-concurrency-extrasPackageTests.wasm
5871

5972
windows:
6073
name: Windows
@@ -66,8 +79,8 @@ jobs:
6679
steps:
6780
- uses: compnerd/gha-setup-swift@main
6881
with:
69-
branch: swift-5.10-release
70-
tag: 5.10-RELEASE
82+
branch: swift-6.0-release
83+
tag: 6.0-RELEASE
7184

7285
- uses: actions/checkout@v4
7386
- name: Run tests

Sources/ConcurrencyExtras/MainSerialExecutor.swift

+49-37
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,57 @@
11
#if !os(WASI) && !os(Windows) && !os(Android)
22
import Foundation
33

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
3951

4052
/// Perform an operation on the main serial executor.
4153
///
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
4355
/// `XCTestCase.invokeTest` to ensure all async tests are performed serially:
4456
///
4557
/// ```swift
@@ -65,7 +77,7 @@
6577
/// Overrides Swift's global executor with the main serial executor in an unchecked fashion.
6678
///
6779
/// > 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
6981
/// > scopes this work to the duration of a given operation.
7082
public var uncheckedUseMainSerialExecutor: Bool {
7183
get { swift_task_enqueueGlobal_hook != nil }

0 commit comments

Comments
 (0)