Skip to content
Open
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
# Workaround https://github.com/nektos/act/issues/1875
uses: apple/swift-nio/.github/workflows/wasm_swift_sdk.yml@main
with:
additional_command_arguments: "--target NIOCore"
additional_command_arguments: "--target _NIOWASIPlatformCompilationChecks"

android-sdk:
name: Android Swift SDK
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ jobs:
# Workaround https://github.com/nektos/act/issues/1875
uses: apple/swift-nio/.github/workflows/wasm_swift_sdk.yml@main
with:
additional_command_arguments: "--target NIOCore"
additional_command_arguments: "--target _NIOWASIPlatformCompilationChecks"

android-sdk:
name: Android Swift SDK
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/swift_test_matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ jobs:
done < <(echo "$MATRIX_DOCKER_CAPABILITIES" | jq -r '.[]')
fi

# Add Docker security options if specified
if [[ "$MATRIX_DOCKER_SECURITY_OPTS" != '[]' ]]; then
while IFS= read -r opt; do
docker_args+=("--security-opt" "$opt")
done < <(echo "$MATRIX_DOCKER_SECURITY_OPTS" | jq -r '.[]')
fi

if [[ "$MATRIX_ENV_JSON" != '{}' ]]; then
while IFS="=" read -r key value; do
if [[ -n "$key" && -n "$value" ]]; then
Expand All @@ -118,6 +125,7 @@ jobs:
MATRIX_COMMAND_ARGUMENTS: ${{ matrix.config.command_arguments }}
MATRIX_ENV_JSON: ${{ toJson(matrix.config.env) }}
MATRIX_DOCKER_CAPABILITIES: ${{ toJson(matrix.config.docker_capabilities) }}
MATRIX_DOCKER_SECURITY_OPTS: ${{ toJson(matrix.config.docker_security_opts) }}
GITHUB_WORKSPACE: ${{ github.workspace }}
- name: Run matrix job (Windows)
if: ${{ matrix.config.platform == 'Windows' }}
Expand Down
86 changes: 64 additions & 22 deletions .github/workflows/wasm_swift_sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ permissions:
on:
workflow_call:
inputs:
nightly_main_enabled:
type: boolean
description: "Boolean to enable the nightly-main WASM Swift SDK matrix job. Defaults to true."
default: true
nightly_next_enabled:
type: boolean
description: "Boolean to enable the nightly-next WASM Swift SDK matrix job. Defaults to true."
default: true
release_6_3_enabled:
type: boolean
description: "Boolean to enable the 6.3 release WASM Swift SDK matrix job. Defaults to true."
default: true
env_vars:
type: string
description: "Environment variables for jobs as JSON (e.g., '{\"DEBUG\":\"1\",\"LOG_LEVEL\":\"info\"}')."
Expand All @@ -28,36 +40,66 @@ jobs:
persist-credentials: false
- id: generate-matrix
run: |
# Validate and use JSON environment variables directly
# Validate JSON inputs
if ! echo "$INPUT_ENV_VARS" | jq empty 2>/dev/null; then
echo "Error: env_vars is not valid JSON"
exit 1
fi

env_vars_json="$INPUT_ENV_VARS"
entries=""

# Validate JSON format
if ! echo "$env_vars_json" | jq empty 2>/dev/null; then
echo "Error: env_vars is not valid JSON"
make_entry() {
local swift_version="$1"
local install_env="$2"
jq -n \
--arg name "${swift_version} Jammy" \
--arg swift_version "$swift_version" \
--arg install_env "$install_env" \
--arg command_arguments "$INPUT_ADDITIONAL_COMMAND_ARGUMENTS" \
--argjson env "$env_vars_json" \
'{
"name": $name,
"swift_version": $swift_version,
"platform": "Linux",
"runner": "ubuntu-latest",
"image": "ubuntu:jammy",
"setup_command": ("apt update -q && apt install -y -q curl jq tar && curl -s --retry 3 https://raw.githubusercontent.com/apple/swift-nio/main/scripts/install_swift_prerequisites.sh | bash && curl -s --retry 3 https://raw.githubusercontent.com/apple/swift-nio/main/scripts/install_swift_sdk.sh | " + $install_env + " INSTALL_SWIFT_ARCH=x86_64 INSTALL_SWIFT_SDK=wasm-sdk bash && hash -r"),
"command": "curl -s --retry 3 https://raw.githubusercontent.com/apple/swift-nio/main/scripts/swift-build-with-wasm-sdk.sh | bash -s --",
"command_arguments": $command_arguments,
"env": $env
}'
}

if [[ "$INPUT_NIGHTLY_MAIN_ENABLED" == "true" ]]; then
entry=$(make_entry "nightly-main" "INSTALL_SWIFT_BRANCH=main")
entries="${entries:+${entries},}${entry}"
fi

if [[ "$INPUT_NIGHTLY_NEXT_ENABLED" == "true" ]]; then
entry=$(make_entry "$NIGHTLY_NEXT_VERSION" "INSTALL_SWIFT_BRANCH=$NIGHTLY_NEXT_BRANCH")
entries="${entries:+${entries},}${entry}"
fi

if [[ "$INPUT_RELEASE_6_3_ENABLED" == "true" ]]; then
entry=$(make_entry "6.3" "INSTALL_SWIFT_VERSION=6.3")
entries="${entries:+${entries},}${entry}"
fi

if [[ -z "$entries" ]]; then
echo "Error: No WASM Swift SDK matrix jobs enabled"
exit 1
fi

# Generate matrix with parsed environment variables
cat >> "$GITHUB_OUTPUT" << EOM
wasm-swift-sdk-matrix=$(echo '{
"config":[
{
"name":"main Jammy",
"swift_version":"main",
"platform":"Linux",
"runner":"ubuntu-latest",
"image":"ubuntu:jammy",
"setup_command":"apt update -q && apt install -y -q curl jq tar && curl -s --retry 3 https://raw.githubusercontent.com/apple/swift-nio/main/scripts/install_swift_prerequisites.sh | bash && curl -s --retry 3 https://raw.githubusercontent.com/apple/swift-nio/main/scripts/install_swift_sdk.sh | INSTALL_SWIFT_BRANCH=main INSTALL_SWIFT_ARCH=x86_64 INSTALL_SWIFT_SDK=wasm-sdk bash && hash -r",
"command":"curl -s --retry 3 https://raw.githubusercontent.com/apple/swift-nio/main/scripts/swift-build-with-wasm-sdk.sh | bash -s --",
"command_arguments":"'"$INPUT_ADDITIONAL_COMMAND_ARGUMENTS"'",
"env":'"$env_vars_json"'
}
]
}' | jq -c)
EOM
echo "wasm-swift-sdk-matrix=$(echo "{\"config\":[${entries}]}" | jq -c)" >> "$GITHUB_OUTPUT"
env:
INPUT_NIGHTLY_MAIN_ENABLED: ${{ inputs.nightly_main_enabled }}
INPUT_NIGHTLY_NEXT_ENABLED: ${{ inputs.nightly_next_enabled }}
INPUT_RELEASE_6_3_ENABLED: ${{ inputs.release_6_3_enabled }}
INPUT_ENV_VARS: ${{ inputs.env_vars }}
INPUT_ADDITIONAL_COMMAND_ARGUMENTS: ${{ inputs.additional_command_arguments }}
NIGHTLY_NEXT_VERSION: nightly-6.3
NIGHTLY_NEXT_BRANCH: swift-6.3-branch

wasm-swift-sdk:
name: WebAssembly Swift SDK
Expand Down
127 changes: 127 additions & 0 deletions Benchmarks/Benchmarks/NIOAsyncRuntimeBenchmarks/Benchmarks.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2026 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

// NOTE: By and large the benchmarks here were ported from swift-nio
// to allow side-by-side performance comparison
//
// See https://github.com/apple/swift-nio/blob/main/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift

import Benchmark
import NIOAsyncRuntime
import NIOCore

let benchmarks: @Sendable () -> Void = {
if #available(macOS 15, iOS 18, tvOS 18, watchOS 11, *) {
let eventLoop = AsyncEventLoopGroup.singleton.next()

let defaultMetrics: [BenchmarkMetric] = [
.mallocCountTotal,
.contextSwitches,
.wallClock,
]

Benchmark(
"MTELG.immediateTasksThroughput",
configuration: Benchmark.Configuration(
metrics: defaultMetrics,
scalingFactor: .mega,
maxDuration: .seconds(10_000_000),
maxIterations: 5
)
) { benchmark in
@Sendable func noOp() {}
for _ in benchmark.scaledIterations {
eventLoop.execute { noOp() }
}
}

Benchmark(
"MTELG.scheduleTask(in:_:)",
configuration: Benchmark.Configuration(
metrics: defaultMetrics,
scalingFactor: .kilo,
maxDuration: .seconds(10_000_000),
maxIterations: 5
)
) { benchmark in
for _ in benchmark.scaledIterations {
eventLoop.scheduleTask(in: .hours(1), {})
}
}

Benchmark(
"MTELG.scheduleCallback(in:_:)",
configuration: Benchmark.Configuration(
metrics: defaultMetrics,
scalingFactor: .mega,
maxDuration: .seconds(10_000_000),
maxIterations: 5,
// NOTE: Jan 29 2026. This test crashes in CI, but not locally, making a fix difficult.
// Skipping the benchmark for now.
skip: true
)
) { benchmark in
final class Timer: NIOScheduledCallbackHandler, @unchecked Sendable {
func handleScheduledCallback(eventLoop: some EventLoop) {}
}
let timer = Timer()

benchmark.startMeasurement()
for _ in benchmark.scaledIterations {
_ = try! eventLoop.scheduleCallback(in: .hours(1), handler: timer)
}
}

Benchmark(
"Jump to EL and back using execute and unsafecontinuation",
configuration: .init(
metrics: defaultMetrics,
scalingFactor: .kilo
)
) { benchmark in
for _ in benchmark.scaledIterations {
await withUnsafeContinuation { (continuation: UnsafeContinuation<Void, Never>) in
eventLoop.execute {
continuation.resume()
}
}
}
}

final actor Foo {
nonisolated public let unownedExecutor: UnownedSerialExecutor

init(eventLoop: any EventLoop) {
self.unownedExecutor = eventLoop.executor.asUnownedSerialExecutor()
}

func foo() {
blackHole(Void())
}
}

Benchmark(
"Jump to EL and back using actor with EL executor",
configuration: .init(
metrics: defaultMetrics,
scalingFactor: .kilo
)
) { benchmark in
let actor = Foo(eventLoop: eventLoop)
for _ in benchmark.scaledIterations {
await actor.foo()
}
}
}
}
12 changes: 12 additions & 0 deletions Benchmarks/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,17 @@ let package = Package(
.plugin(name: "BenchmarkPlugin", package: "package-benchmark")
]
),
.executableTarget(
name: "NIOAsyncRuntimeBenchmarks",
dependencies: [
.product(name: "Benchmark", package: "package-benchmark"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOAsyncRuntime", package: "swift-nio"),
],
path: "Benchmarks/NIOAsyncRuntimeBenchmarks",
plugins: [
.plugin(name: "BenchmarkPlugin", package: "package-benchmark")
]
),
]
)
47 changes: 44 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@ let package = Package(
.library(name: "NIO", targets: ["NIO"]),
.library(name: "NIOEmbedded", targets: ["NIOEmbedded"]),
.library(name: "NIOPosix", targets: ["NIOPosix"]),
.library(name: "NIOAsyncRuntime", targets: ["NIOAsyncRuntime"]),
.library(name: "_NIOConcurrency", targets: ["_NIOConcurrency"]),
.library(name: "NIOTLS", targets: ["NIOTLS"]),
.library(name: "NIOHTTP1", targets: ["NIOHTTP1"]),
.library(name: "NIOConcurrencyHelpers", targets: ["NIOConcurrencyHelpers"]),
.library(name: "NIOFoundationEssentialsCompat", targets: ["NIOFoundationEssentialsCompat"]),
.library(name: "NIOFoundationCompat", targets: ["NIOFoundationCompat"]),
.library(name: "NIOWebSocket", targets: ["NIOWebSocket"]),
.library(name: "NIOTestUtils", targets: ["NIOTestUtils"]),
Expand Down Expand Up @@ -112,6 +114,14 @@ let package = Package(
resources: includePrivacyManifest ? [.copy("PrivacyInfo.xcprivacy")] : [],
swiftSettings: swiftSettings
),
.target(
name: "NIOAsyncRuntime",
dependencies: [
"NIOCore"
],
exclude: ["README.md"],
swiftSettings: swiftSettings
),
.target(
name: "NIO",
dependencies: [
Expand All @@ -129,11 +139,31 @@ let package = Package(
],
swiftSettings: swiftSettings
),
// Sole purpose of this target is to check all modules
// currently expected to pass compilation for WASI platforms
.target(
name: "_NIOWASIPlatformCompilationChecks",
dependencies: [
.target(name: "NIOAsyncRuntime", condition: .when(platforms: [.wasi])),
"NIOCore",
"NIOEmbedded", // This should be properly elided in source files for WASI platforms
"NIOPosix", // This should be properly elided in source files for WASI platforms
swiftAtomics,
],
swiftSettings: swiftSettings
),
.target(
name: "NIOFoundationEssentialsCompat",
dependencies: [
"NIOCore"
],
swiftSettings: swiftSettings
),
.target(
name: "NIOFoundationCompat",
dependencies: [
.target(name: "NIO", condition: .when(platforms: historicalNIOPosixDependencyRequired)),
"NIOCore",
"NIOFoundationEssentialsCompat",
],
swiftSettings: swiftSettings
),
Expand Down Expand Up @@ -259,7 +289,7 @@ let package = Package(
name: "NIOFSFoundationCompat",
dependencies: [
"NIOFS",
"NIOFoundationCompat",
"NIOFoundationEssentialsCompat",
],
path: "Sources/NIOFSFoundationCompat",
swiftSettings: swiftSettings
Expand Down Expand Up @@ -287,7 +317,7 @@ let package = Package(
name: "_NIOFileSystemFoundationCompat",
dependencies: [
"_NIOFileSystem",
"NIOFoundationCompat",
"NIOFoundationEssentialsCompat",
],
path: "Sources/_NIOFileSystemFoundationCompat",
swiftSettings: swiftSettings
Expand Down Expand Up @@ -501,6 +531,17 @@ let package = Package(
],
swiftSettings: swiftSettings
),
.testTarget(
name: "NIOAsyncRuntimeTests",
dependencies: [
"NIOAsyncRuntime",
"NIOCore",
"NIOConcurrencyHelpers",
"NIOFoundationCompat",
"NIOTestUtils",
],
swiftSettings: swiftSettings
),
.testTarget(
name: "NIOConcurrencyHelpersTests",
dependencies: [
Expand Down
Loading
Loading