Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,31 @@ jobs:
run: swift test -c ${{ matrix.config }}

wasm:
name: Wasm
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: bytecodealliance/actions/wasmtime/setup@v1
- uses: swiftwasm/setup-swiftwasm@v1
with:
swift-version: "wasm-5.9.2-RELEASE"
- name: Build tests
run: swift build --triple wasm32-unknown-wasi --build-tests
- name: Run tests
run: wasmtime --dir . .build/debug/swift-concurrency-extrasPackageTests.wasm
name: SwiftWasm
runs-on: ubuntu-latest
strategy:
matrix:
include:
- toolchain: swift-DEVELOPMENT-SNAPSHOT-2024-09-12-a
swift-sdk: swift-wasm-DEVELOPMENT-SNAPSHOT-2024-09-12-a
checksum: 630ce23114580dfae029f832d8ccc8b1ba5136b7f915e82f8e405650e326b562
steps:
- uses: actions/checkout@v4
- uses: bytecodealliance/actions/wasmtime/setup@v1
- name: Install Swift and Swift SDK for WebAssembly
run: |
PREFIX=/opt/swift
SWIFT_TOOLCHAIN_TAG="${{ matrix.toolchain }}"
SWIFT_SDK_TAG="${{ matrix.swift-sdk }}"
set -ex
curl -f -o /tmp/swift.tar.gz "https://download.swift.org/development/ubuntu2204/$SWIFT_TOOLCHAIN_TAG/$SWIFT_TOOLCHAIN_TAG-ubuntu22.04.tar.gz"
sudo mkdir -p $PREFIX; sudo tar -xzf /tmp/swift.tar.gz -C $PREFIX --strip-component 1
$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 }}
echo "$PREFIX/usr/bin" >> $GITHUB_PATH
- name: Build tests
run: swift build --swift-sdk wasm32-unknown-wasi --build-tests -Xlinker -z -Xlinker stack-size=$((1024 * 1024))
- name: Run tests
run: wasmtime --dir . .build/debug/swift-concurrency-extrasPackageTests.wasm

windows:
name: Windows
Expand All @@ -66,8 +79,8 @@ jobs:
steps:
- uses: compnerd/gha-setup-swift@main
with:
branch: swift-5.10-release
tag: 5.10-RELEASE
branch: swift-6.0-release
tag: 6.0-RELEASE

- uses: actions/checkout@v4
- name: Run tests
Expand Down
86 changes: 49 additions & 37 deletions Sources/ConcurrencyExtras/MainSerialExecutor.swift
Original file line number Diff line number Diff line change
@@ -1,45 +1,57 @@
#if !os(WASI) && !os(Windows) && !os(Android)
import Foundation

/// Perform an operation on the main serial executor.
///
/// Some asynchronous code is [notoriously
/// difficult](https://forums.swift.org/t/reliably-testing-code-that-adopts-swift-concurrency/57304)
/// to test in Swift due to how suspension points are processed by the runtime. This function
/// attempts to run all tasks spawned in the given operation serially and deterministically. It
/// makes asynchronous tests faster and less flakey.
///
/// ```swift
/// await withMainSerialExecutor {
/// // Everything performed in this scope is performed serially...
/// }
/// ```
///
/// See <doc:ReliablyTestingAsync> for more information on why this tool is needed to test
/// async code and how to use it.
///
/// > Warning: This API is only intended to be used from tests to make them more reliable. Please do
/// > not use it from application code.
/// >
/// > We say that it "_attempts_ to run all tasks spawned in an operation serially and
/// > deterministically" because under the hood it relies on a global, mutable variable in the Swift
/// > runtime to do its job, and there are no scoping _guarantees_ should this mutable variable change
/// > during the operation.
///
/// - Parameter operation: An operation to be performed on the main serial executor.
@MainActor
public func withMainSerialExecutor(
@_implicitSelfCapture operation: @Sendable () async throws -> Void
) async rethrows {
let didUseMainSerialExecutor = uncheckedUseMainSerialExecutor
defer { uncheckedUseMainSerialExecutor = didUseMainSerialExecutor }
uncheckedUseMainSerialExecutor = true
try await operation()
}
#if compiler(>=6)
/// Perform an operation on the main serial executor.
///
/// Some asynchronous code is [notoriously
/// difficult](https://forums.swift.org/t/reliably-testing-code-that-adopts-swift-concurrency/57304)
/// to test in Swift due to how suspension points are processed by the runtime. This function
/// attempts to run all tasks spawned in the given operation serially and deterministically. It
/// makes asynchronous tests faster and less flakey.
///
/// ```swift
/// await withMainSerialExecutor {
/// // Everything performed in this scope is performed serially...
/// }
/// ```
///
/// See <doc:ReliablyTestingAsync> for more information on why this tool is needed to test
/// async code and how to use it.
///
/// > Warning: This API is only intended to be used from tests to make them more reliable. Please do
/// > not use it from application code.
/// >
/// > We say that it "_attempts_ to run all tasks spawned in an operation serially and
/// > deterministically" because under the hood it relies on a global, mutable variable in the Swift
/// > runtime to do its job, and there are no scoping _guarantees_ should this mutable variable change
/// > during the operation.
///
/// - Parameter operation: An operation to be performed on the main serial executor.
@MainActor
public func withMainSerialExecutor(
@_implicitSelfCapture operation: @isolated(any) () async throws -> Void
) async rethrows {
let didUseMainSerialExecutor = uncheckedUseMainSerialExecutor
defer { uncheckedUseMainSerialExecutor = didUseMainSerialExecutor }
uncheckedUseMainSerialExecutor = true
try await operation()
}
#else
@MainActor
public func withMainSerialExecutor(
@_implicitSelfCapture operation: @Sendable () async throws -> Void
) async rethrows {
let didUseMainSerialExecutor = uncheckedUseMainSerialExecutor
defer { uncheckedUseMainSerialExecutor = didUseMainSerialExecutor }
uncheckedUseMainSerialExecutor = true
try await operation()
}
#endif

/// Perform an operation on the main serial executor.
///
/// A synchronous version of ``withMainSerialExecutor(operation:)-79jpc`` that can be used in
/// A synchronous version of ``withMainSerialExecutor(operation:)-7fqt1`` that can be used in
/// `XCTestCase.invokeTest` to ensure all async tests are performed serially:
///
/// ```swift
Expand All @@ -65,7 +77,7 @@
/// Overrides Swift's global executor with the main serial executor in an unchecked fashion.
///
/// > Warning: When set to `true`, all tasks will be enqueued on the main serial executor till set
/// > back to `false`. Consider using ``withMainSerialExecutor(operation:)-79jpc``, instead, which
/// > back to `false`. Consider using ``withMainSerialExecutor(operation:)-7fqt1``, instead, which
/// > scopes this work to the duration of a given operation.
public var uncheckedUseMainSerialExecutor: Bool {
get { swift_task_enqueueGlobal_hook != nil }
Expand Down