Commit Graph
2945 Commits
Author SHA1 Message Date
Rick Newton-Rogers af3eee91b9 Benchmark diffs not being output on threshold regression
Motivation

* `check_benchmark_thresholds.sh` was updated to check for exit code 2
from `swift package benchmark thresholds check` to distinguish
threshold regression from build errors.
* It seems SwiftPM's `CommandPlugin` infrastructure always exits with code 1 when
`performCommand` throws, regardless of the error's raw value. So the
exit code is never 2, causing all regressions to fall through to the
`else` branch and be misreported as build errors, with no diff output.

Modifications

* Remove the `rc == 2` check and instead attempt `thresholds update`
for any non-zero `rc` from `thresholds check`.
* Use the result of `thresholds update` to distinguish regression
(success) from build error (failure).
* Remove `--exit-code` from `git diff` to prevent `set -uo pipefail`
from aborting the script before output is fully flushed.

Result

* Benchmark threshold regressions correctly output the `=== BEGIN DIFF
===` section again.
* Actual build errors are still correctly detected and reported.
2026-02-20 15:30:18 +00:00
Rick Newton-Rogers 535c2f8260 switch from main to 0.0.7 for swiftlang workflows (#3514)
Motivation

* `@main` references to `swiftlang/github-workflows` are deprecated.

Modifications

* Replace `@main` with `@0.0.7` in all workflow files.

Result

* Workflow files reference a pinned version of
`swiftlang/github-workflows`.
2026-02-20 13:40:26 +00:00
Si Beaumont e932d3c4d8 Workaround test crashing when compiled in release mode on Swift 6.2 (#3511)
### Motivation:

Our post-merge, pre-release pipelines run tests for our supported Swift
versions in both debug and release mode. The recent PR to add
`withUnsafeTransportIfAvailable` added some tests for various valid and
invalid usages of the API. One of the invalid usages was causing the
tests to crash, when compiled in release mode, in the Swift runtime:

```
% docker run --rm -it -v $PWD:/pwd -w /pwd swift:6.2 swift test --filter testUnderlyingSocketAccessForSocketBasedChannel
[1/1] Planning build
Building for debugging...
[1/1] Write swift-version-24593BA9C3E375BF.txt
Build complete! (2.38s)
Test Suite 'Selected tests' started at 2026-02-18 13:28:18.905
Test Suite 'Selected tests' passed at 2026-02-18 13:28:18.905
         Executed 0 tests, with 0 failures (0 unexpected) in 0.0 (0.0) seconds
◇ Test run started.
↳ Testing Library Version: 6.2.3 (48a471ab313e858)
↳ Target Platform: aarch64-unknown-linux-gnu
◇ Suite NIOTransportAccessibleChannelCoreTests started.
◇ Test testUnderlyingSocketAccessForSocketBasedChannel() started.
✔ Test testUnderlyingSocketAccessForSocketBasedChannel() passed after 0.005 seconds.
✔ Suite NIOTransportAccessibleChannelCoreTests passed after 0.005 seconds.
✔ Test run with 1 test in 1 suite passed after 0.005 seconds.
```

```
% docker run --rm -it -v $PWD:/pwd -w /pwd swift:6.2 swift test -c release --filter testUnderlyingSocketAccessForSocketBasedChannel
[1/1] Planning build
Building for production...
[1/1] Write swift-version-24593BA9C3E375BF.txt
Build complete! (2.12s)
Test Suite 'Selected tests' started at 2026-02-18 13:27:12.102
Test Suite 'Selected tests' passed at 2026-02-18 13:27:12.103
         Executed 0 tests, with 0 failures (0 unexpected) in 0.0 (0.0) seconds
◇ Test run started.
↳ Testing Library Version: 6.2.3 (48a471ab313e858)
↳ Target Platform: aarch64-unknown-linux-gnu
◇ Suite NIOTransportAccessibleChannelCoreTests started.
◇ Test testUnderlyingSocketAccessForSocketBasedChannel() started.

*** Signal 11: Backtracing from 0xffff8854b1ec... done ***

*** Program crashed: Bad pointer dereference at 0x0000000000000000 ***

Platform: arm64 Linux (Ubuntu 24.04.4 LTS)

Thread 0 "NIO-ELT-1-#1" crashed:

  0                0x0000ffff8854b1ec tryCast(swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*, swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetMetadata<swift::InProcess> const*&, swift::TargetMetadata<swift::InProcess> const*&, bool, bool, bool) + 52 in libswiftCore.so
  1 [ra]           0x0000ffff8854b148 swift_dynamicCast + 79 in libswiftCore.so
  2 [ra] [system]  0x0000aaaab8668588 specialized ChannelPipeline.SynchronousOperations.withUnsafeTransportIfAvailable<A, B>(of:_:) + 135 in swift-nioPackageTests.xctest at //<compiler-generated>
  3 [ra]           0x0000aaaab8668148 closure #1 in NIOTransportAccessibleChannelCoreTests.testUnderlyingSocketAccessForSocketBasedChannel() + 3103 in swift-nioPackageTests.xctest at /tmp/swift-generated-sources/@__swiftmacro_13NIOPosixTests0049NIOTransportAccessibleChannelCoreTestsswift_IhHJqfMX55_16_33_309B77F97E02F918EDFF07092681CD49Ll6expectfMf8_.swift:1:40
  4 [ra] [thunk]   0x0000aaaab866be1c partial apply for closure #1 in NIOTransportAccessibleChannelCoreTests.testUnderlyingSocketAccessForSocketBasedChannel() + 15 in swift-nioPackageTests.xctest at //<compiler-generated>
  5 [ra]           0x0000aaaab7f410ec closure #1 in EventLoop.submit<A>(_:) + 95 in swift-nioPackageTests.xctest at /pwd/Sources/NIOCore/EventLoop.swift:1030:69
  6 [ra] [inlined] 0x0000aaaab841d468 closure #1 in SelectableEventLoop.run(_:) in swift-nioPackageTests.xctest at /pwd/Sources/NIOPosix/SelectableEventLoop.swift:618:17
  7 [ra] [inlined] 0x0000aaaab841d468 specialized withAutoReleasePool<A>(_:) in swift-nioPackageTests.xctest at /pwd/Sources/NIOPosix/SelectableEventLoop.swift:49:16
  8 [ra] [inlined] 0x0000aaaab841d468 SelectableEventLoop.run(_:) in swift-nioPackageTests.xctest at /pwd/Sources/NIOPosix/SelectableEventLoop.swift:615:9
  9 [ra]           0x0000aaaab841d468 SelectableEventLoop.runOneLoopTick(selfIdentifier:) + 535 in swift-nioPackageTests.xctest at /pwd/Sources/NIOPosix/SelectableEventLoop.swift:809:22
 10 [ra]           0x0000aaaab8423e5c SelectableEventLoop.run() + 331 in swift-nioPackageTests.xctest at /pwd/Sources/NIOPosix/SelectableEventLoop.swift:923:38
 11 [ra] [inlined] 0x0000aaaab83de614 static MultiThreadedEventLoopGroup.runTheLoop(thread:uniqueID:parentGroup:canEventLoopBeShutdownIndividually:selectorFactory:initializer:metricsDelegate:_:) in swift-nioPackageTests.xctest at /pwd/Sources/NIOPosix/MultiThreadedEventLoopGroup.swift:117:22
 12 [ra]           0x0000aaaab83de614 closure #1 in static MultiThreadedEventLoopGroup.setupThreadAndEventLoop(name:uniqueID:parentGroup:selectorFactory:initializer:metricsDelegate:) + 1255 in swift-nioPackageTests.xctest at /pwd/Sources/NIOPosix/MultiThreadedEventLoopGroup.swift:139:41
 13 [ra] [thunk]   0x0000aaaab83e1eec partial apply for closure #1 in static MultiThreadedEventLoopGroup.setupThreadAndEventLoop(name:uniqueID:parentGroup:selectorFactory:initializer:metricsDelegate:) + 39 in swift-nioPackageTests.xctest at //<compiler-generated>
 14 [ra] [thunk]   0x0000aaaab843f534 thunk for @escaping @callee_guaranteed (@guaranteed NIOThread) -> () + 23 in swift-nioPackageTests.xctest at //<compiler-generated>
 15 [ra]           0x0000aaaab84409dc closure #1 in closure #1 in static ThreadOpsPosix.run(handle:args:) + 319 in swift-nioPackageTests.xctest at /pwd/Sources/NIOPosix/ThreadPosix.swift:186:21
 16 [ra]           0x0000ffff86af595c <unknown> in libc.so.6
...


Registers:

 x0 0x0000ffff62fddfe0  48 60 00 58 ff ff 00 00 e0 e0 fd 62 ff ff 00 00  H`·Xÿÿ··ààýbÿÿ··
 x1 0x0000000000000000  0
 x2 0x0000ffff62fddff0  40 b4 01 5c ff ff 00 00 20 83 d1 b8 aa aa 00 00  @´·\ÿÿ·· ·Ñ¸ªª··
 x3 0x0000ffff58002038  03 03 00 00 00 00 00 00 01 00 00 00 01 00 00 00  ················
 x4 0x0000ffff62fddfa8  00 00 00 00 00 00 00 00 00 e0 fd 62 ff ff 00 00  ·········àýbÿÿ··
 x5 0x0000ffff62fddfa0  38 20 00 58 ff ff 00 00 00 00 00 00 00 00 00 00  8 ·Xÿÿ··········
 x6 0x0000000000000001  1
 x7 0x0000000000000000  0
 x8 0x0000000000000000  0
 x9 0x0000ffff886cf4e8  98 73 54 88 ff ff 00 00 08 be 5b 88 ff ff 00 00  ·sT·ÿÿ···¾[·ÿÿ··
x10 0x000000000000ffff  65535
x11 0x0000000000000000  0
x12 0x0000000000000000  0
x13 0x0000000000000001  1
x14 0x0000aaaab8e3ff2c  05 00 00 00 d4 2e a7 ff 00 00 03 00 51 00 00 00  ····Ô.§ÿ····Q···
x15 0x0000000000000009  9
x16 0x0000aaaab8d70520  f8 b0 54 88 ff ff 00 00 b4 45 55 88 ff ff 00 00  ø°T·ÿÿ··´EU·ÿÿ··
x17 0x0000ffff8854b0f8  ff 43 01 d1 fd 7b 02 a9 f6 57 03 a9 f4 4f 04 a9  ÿC·Ñý{·©öW·©ôO·©
x18 0x0000000000000000  0
x19 0x0000ffff58002038  03 03 00 00 00 00 00 00 01 00 00 00 01 00 00 00  ················
x20 0x0000ffff62fddff0  40 b4 01 5c ff ff 00 00 20 83 d1 b8 aa aa 00 00  @´·\ÿÿ·· ·Ñ¸ªª··
x21 0x0000000000000006  6
x22 0x0000000000000000  0
x23 0x0000aaaab86688b0  09 00 40 f9 09 01 00 f9 c0 03 5f d6 ff c3 00 d1  ··@ù···ùÀ·_Öÿ÷Ñ
x24 0x0000ffff5c01b440  f0 9d d9 b8 aa aa 00 00 03 00 00 00 08 00 00 00  ð·Ù¸ªª··········
x25 0x0000aaaab8d18320  0c 9c e2 b8 aa aa 00 00 ec 3c 3a b8 aa aa 00 00  ··â¸ªª··ì<:¸ªª··
x26 0x0000ffff62fde170  6e 69 6c 00 00 00 00 00 00 00 00 00 00 00 00 e3  nil············ã
x27 0x0000ffff62fde0e0  2d 00 00 00 00 00 00 d0 a0 3b de b8 aa aa 00 80  -······Ð ;Þ¸ªª··
x28 0x0000ffff886c6c28  02 02 00 00 00 00 00 00 30 b0 6f 88 ff ff 00 00  ········0°o·ÿÿ··
 fp 0x0000ffff62fddf30  b0 df fd 62 ff ff 00 00 48 b1 54 88 ff ff 00 00  °ßýbÿÿ··H±T·ÿÿ··
 lr 0x0000ffff8854b148  1f 08 00 71 c0 00 00 54 1f 04 00 71 c1 00 00 54  ···qÀ··T···qÁ··T
 sp 0x0000ffff62fddf00  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ················
 pc 0x0000ffff8854b1ec  68 01 40 f9 69 00 40 f9 1f fd 1f f1 f3 83 88 1a  h·@ùi·@ù·ý·ñó···


Images (20 omitted):

0x0000aaaab7d20000–0x0000aaaab8cd6d00 a17d4d145f2d2621f7b9d43c2b2d2a754b69002b swift-nioPackageTests.xctest /pwd/.build/aarch64-unknown-linux-gnu/release/swift-nioPackageTests.xctest
0x0000ffff86a70000–0x0000ffff86c09ec1 d5ef86dde36cbd3289566cf5098226035d76f2e1 libc.so.6                    /usr/lib/aarch64-linux-gnu/libc.so.6
0x0000ffff88160000–0x0000ffff8869e0c8 f62b9374639c58d0b60814a6fbec69d2b596cfc7 libswiftCore.so              /usr/lib/swift/linux/libswiftCore.so

Backtrace took 0.62s

error: Exited with unexpected signal code 11
```

The test is fine for both debug and release mode on Swift 6.0, 6.1, and
6.3-nightly.

### Modifications:

Since this is a valid construction and the crash is a Swift bug, and
since it only manifests on a specific Swift version, and since it's only
triggered when mis-using the API, we can just not perform this part of
the test on Swift 6.2. The API is fully functional when used correctly.

### Result:

Running NIO tests, compiled in release mode, no longer crashes on Swift
6.2.3.

### Local testings:

With this branch:

```
% docker run --rm -it -v $PWD:/pwd -w /pwd swift:6.2 swift test -c release --filter testUnderlyingSocketAccessForSocketBasedChannel
[1/1] Planning build
Building for production...
[8/8] Linking swift-nioPackageTests.xctest
Build complete! (38.46s)
Test Suite 'Selected tests' started at 2026-02-18 14:23:12.902
Test Suite 'Selected tests' passed at 2026-02-18 14:23:12.902
         Executed 0 tests, with 0 failures (0 unexpected) in 0.0 (0.0) seconds
◇ Test run started.
↳ Testing Library Version: 6.2.3 (48a471ab313e858)
↳ Target Platform: aarch64-unknown-linux-gnu
◇ Suite NIOTransportAccessibleChannelCoreTests started.
◇ Test testUnderlyingSocketAccessForSocketBasedChannel() started.
✔ Test testUnderlyingSocketAccessForSocketBasedChannel() passed after 0.002 seconds.
✔ Suite NIOTransportAccessibleChannelCoreTests passed after 0.003 seconds.
✔ Test run with 1 test in 1 suite passed after 0.003 seconds.
```
2.95.0
2026-02-18 15:11:55 +00:00
Stepan UlyaninandGeorge Barnett be8fdc13c0 Allow to copy files and symlinks while overwriting the destination (#3508)
Adds capability to NIOFS to copy regular files and symlink, allowing to
overwrite the destination.

### Motivation:

Per https://github.com/apple/swift-nio/issues/3403 and
https://github.com/apple/swift-nio/pull/3470 we want to add
`replaceExisting: bool` to `FileSystem.copyItem`.

### Modifications:

1. Adds `replaceExisting: bool` parameter to
`FileSystemProtocol.copyItem`.
2. Adds `replaceExisting: bool` parameter to `FileSystem.copyItem` and
implementation for regular files and symbolic links.
3. Adds tests.

---------

Co-authored-by: George Barnett <gbarnett@apple.com>
2026-02-18 08:57:33 +00:00
Si BeaumontandAgam Dua b0e024792a Add opt-in API for channels to expose their underlying transport (#3509)
## Motivation

NIO channels abstract over an underlying transport mechanisms (sockets,
pipes, etc.), which users typically need not interact with. However,
there are scenarios where users need direct access to the underlying
transport for low-level operations that work outside NIOs abstraction.
One example is performing out out-of-band operations on the underlying
file descriptor for a socket-based channel.

This PR adds a structured way to access the underlying transport of a
channel, for channels that choose to implement it.

## Modifications

- Add a new `public protocol
NIOTransportAccessibleChannelCore<Transport>` which provides a scoped
`withUnsafeTransport(_:)`, used for channel implementations to opt-in.
- Add conformance to
`NIOTransportAccessibleChannel<NIOBSDSocket.Handle>` for
`BaseSocketChannel`, to make this API available for all socket-based
channels, including channels returned from the socket-based bootstrap
public APIs.
- Add public API
`ChannelPipeline.SynchronousOperations.withUnsafeTransportIfAvailable(_:)`

Note that not all channels need to or should their transport, which is
why this was added as an additional protocol that refines `ChannelCore`,
vs. extending `Channel` or `ChannelCore` with a default implementation.

The protocol uses a primary associated type allowing channels to provide
typed access to the transport. E.g. this could be used in NIO Transport
Services to expose the underlying `NWConnection`, if desired.

The method itself is spelled with "unsafe", uses scoped access, and has
clear documentation that users must not violated any of NIOs assumptions
about the state of the underlying transport. It's very much not intended
for every day use. It being on `ChannelCore` should defer users of the
low-level API, since `Channel._channelCore` is marked as for NIO
internal use, and the public `withUnsafeTransportIfAvailable(_:)` will
take care of the runtime checks for channels that have opted into this
API.

## Result

- New opt-in API for channel implementations to expose their underlying
transport
- All socket-based channels from NIOPosix now expose the underlying
socket file descriptor
- New API
`ChannelPipeline.SynchronousOperations.withUnsafeTransportIfAvailable(_:)`
for users

---------

Co-authored-by: Agam Dua <agam_dua@apple.com>
2026-02-17 16:12:58 +00:00
Fabian Fett 2fdda6c78e Fix benchmark succeeding – even though Benchmark build failed (#3502)
### Motivation:

Currently if there is an error in the Benchmark targets, that prevents
the Benchmark to build correctly, we still get a CI success.

### Modifications:

- Check for the threshold changed error

### Result:

- More reliable CI
2026-02-17 10:30:44 +00:00
Si Beaumont c329d1e173 Fix C++ interop CI script to work with changes in SwiftPM (#3510)
### Motivation:

SwiftPM behaviour has changed in recent Swift nightlies.

In most recent released version of Swift, SwiftPM will use the parent
directory name for the source and test target directory too, even when
it contains a period:

```
% docker run -it swift:6.2 bash -c 'mkdir -p /tmp/foo.bar && cd /tmp/foo.bar && swift package init'
Creating library package: foo.bar
Creating Package.swift
Creating .gitignore
Creating Sources
Creating Sources/foo.bar/foo_bar.swift
Creating Tests/
Creating Tests/foo.barTests/
Creating Tests/foo.barTests/foo_barTests.swift
```

Note in the above how it replaces the `.` with a `_` in the source
files, but the directories retain the `.`.

In the recent nightly-main, SwiftPM has also started replacing `.` with
`_` in the target directory names:

```
% docker run -it swiftlang/swift:nightly-main bash -c 'cat /etc/motd && mkdir -p /tmp/foo.bar && cd /tmp/foo.bar && swift package init'
################################################################
#                                                              #
# Swift Nightly Docker Image                                   #
# Tag: swift-DEVELOPMENT-SNAPSHOT-2026-02-06-a                 #
#                                                              #
################################################################
Creating library package: foo.bar
Creating Package.swift
Creating .gitignore
Creating Sources
Creating Sources/foo_bar/foo_bar.swift
Creating Tests/
Creating Tests/foo_barTests/
Creating Tests/foo_barTests/foo_barTests.swift
```

Probably this
https://github.com/swiftlang/swift-package-manager/pull/9252.

This breaks our CI automation, only on nightly-main, because we run
`swift package init` in a directory created by `mktemp -d`, which, by
default, follows the pattern `tmp.XXXXXXX`.

### Modifications:

Create temporary directory for Swift package using explicit pattern
containing no periods.

### Result:

C++ interop CI should now work again on nightly-main Swift, and continue
to work on all supported Swift versions.
2026-02-11 17:13:33 +00:00
Raphael d948192838 Don't send (in)active when channel closes directly (#3497)
### Motivation

Issue #2773 reports a problem where channels can receive
`channelInactive` before `channelActive`. This breaks expectations and
should not happen. Instead of sending them in the wrong order, this PR
suppresses both of them in those cases.

### Modifications

* Add a test to reproduce the problematic behavior.
* Update the state machine to split the active state into two. This
decouples succeeding the promise from sending the signal. Transiting to
closing only sends `channelInactive` when originating from the state
that sent `channelActive`.

### Result

Address #2773.

Edit: Updated modifications to reflect latests commit.
2026-02-11 15:46:19 +00:00
Kushal Pisavadia 9b92dcd5c2 Replace ConditionLock with wake-one signalling NIOThreadPoolWorkAvailable (#3507)
**TL;DR** This change leads to a ~90% reduction in observed system CPU
time for some use cases by waking a single thread, instead of all idle
threads.

# Changes

Inlining the commit messages here.

## Add NIOThreadPool submit throughput benchmarks

### Motivation

`NIOThreadPool` had no benchmarks measuring submit overhead. This makes
it difficult to evaluate the cost of signalling changes or to catch
latency regressions.

### Modifications

Add thread pool submit benchmarks, covering use cases with 4-thread and
16-thread pools.

### Result

`NIOThreadPool` submit throughput and context-switch overhead are now
tracked by benchmarks.

### Benchmark Results

<details>

```
NIOThreadPool.serial_wakeup(16 threads)
╒══════════════════════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╕
│ Metric                   │        p0 │       p25 │       p50 │       p75 │       p90 │       p99 │      p100 │   Samples │
╞══════════════════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ Context switches (K)     │        77 │        78 │        79 │        80 │        80 │        92 │        92 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Syscalls (total) (K) *   │       106 │       107 │       108 │       109 │       110 │       116 │       116 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (system CPU) (ms) * │      1649 │      1752 │      1768 │      1795 │      1826 │      1929 │      1929 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (total CPU) (ms) *  │      1701 │      1805 │      1821 │      1849 │      1879 │      1987 │      1987 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (user CPU) (ms) *   │        51 │        52 │        52 │        53 │        53 │        57 │        57 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (wall clock) (ms) * │       167 │       177 │       178 │       181 │       183 │       200 │       200 │        30 │
╘══════════════════════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╛

NIOThreadPool.serial_wakeup(4 threads)
╒══════════════════════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╕
│ Metric                   │        p0 │       p25 │       p50 │       p75 │       p90 │       p99 │      p100 │   Samples │
╞══════════════════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ Context switches (K)     │        44 │        44 │        44 │        45 │        45 │        45 │        45 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Syscalls (total) (K) *   │        65 │        65 │        65 │        66 │        66 │        67 │        67 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (system CPU) (ms) * │       159 │       162 │       163 │       165 │       166 │       169 │       169 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (total CPU) (ms) *  │       178 │       182 │       183 │       185 │       186 │       190 │       190 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (user CPU) (ms) *   │        19 │        19 │        20 │        20 │        20 │        21 │        21 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (wall clock) (ms) * │        76 │        79 │        79 │        80 │        80 │        82 │        82 │        30 │
╘══════════════════════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╛
```

</details>

## Replace `ConditionLock` with wake-one signalling
`NIOThreadPoolWorkAvailable`

### Motivation

`NIOThreadPool` used `ConditionLock` which calls
`pthread_cond_broadcast` on every state change, waking all threads when
only one work item is enqueued. This causes a thundering-herd problem.

### Modifications

Add `NIOThreadPoolWorkAvailable` in NIOConcurrencyHelpers that uses
`pthread_cond_signal` (wake-one) for work submission and
`pthread_cond_broadcast` only for shutdown. Replace
`ConditionLock<_WorkState>` and the `_WorkState` enum in `NIOThreadPool`
with this new primitive.

### Result

Submitting a work item wakes exactly **one** thread instead of all
threads.

### Benchmark Results

<details>

```
NIOThreadPool.serial_wakeup(16 threads)
╒══════════════════════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╕
│ Metric                   │        p0 │       p25 │       p50 │       p75 │       p90 │       p99 │      p100 │   Samples │
╞══════════════════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ Context switches (K)     │        20 │        20 │        20 │        20 │        20 │        20 │        20 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Syscalls (total) (K) *   │        40 │        40 │        40 │        40 │        40 │        40 │        40 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (system CPU) (ms) * │        47 │        49 │        49 │        50 │        50 │        55 │        55 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (total CPU) (ms) *  │        57 │        58 │        59 │        60 │        61 │        67 │        67 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (user CPU) (ms) *   │        10 │        10 │        10 │        10 │        10 │        12 │        12 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (wall clock) (ms) * │        54 │        55 │        56 │        56 │        57 │        65 │        65 │        30 │
╘══════════════════════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╛

NIOThreadPool.serial_wakeup(4 threads)
╒══════════════════════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╕
│ Metric                   │        p0 │       p25 │       p50 │       p75 │       p90 │       p99 │      p100 │   Samples │
╞══════════════════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ Context switches (K)     │        20 │        20 │        20 │        20 │        20 │        20 │        20 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Syscalls (total) (K) *   │        40 │        40 │        40 │        40 │        40 │        40 │        40 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (system CPU) (ms) * │        45 │        46 │        46 │        47 │        57 │        75 │        75 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (total CPU) (ms) *  │        54 │        55 │        56 │        57 │        68 │        87 │        87 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (user CPU) (μs) *   │      9055 │      9372 │      9478 │      9765 │     10887 │     12585 │     12585 │        30 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (wall clock) (ms) * │        52 │        53 │        53 │        54 │        64 │       124 │       124 │        30 │
╘══════════════════════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╛
```

</details>
2.94.1
2026-02-10 20:01:35 +00:00
Stepan Ulyanin db01d87942 Add symlinkat, renameatx_np, and unlinkat system call wrappers (#3505)
Adds three new system call wrappers for the `symlinkat`, `renameatx_np`,
and `unlinkat` system calls.

### Motivation:

Related to https://github.com/apple/swift-nio/issues/3403 and
https://github.com/apple/swift-nio/pull/3470. This PR adds syscall
wrappers needed to atomically overwrite existing files or symlinks at
the destination during copy operations. On Linux, atomic overwrites
require a "copy to temp file, then rename" strategy. We use the `*at`
family of syscalls (which operate relative to directory file
descriptors) to avoid TOCTOU race conditions.

### Modifications:

1. Adds three system call wrappers for the `symlinkat`, `renameatx_np`,
and `unlinkat` system calls.
2. Adds related tests
3. Updates the `FileSystemError` for `symlink` and `unlink` to take in
the system call name to allow for the `*at` names to be passed.
2026-02-09 08:38:25 +00:00
Si Beaumont edad6d8a9b Clarify state and relationship of NIOFileSystem and _NIOFileSystem in API docs (#3504)
### Motivation:

`NIOFileSystem` currently exposes the same API as `_NIOFileSystem`,
which is not API stable. `NIOFileSystem`
was created in error, and its lack of underscore incorrectly implies API
stability. Users who are currently importing `NIOFileSystem` should
ideally move to `_NIOFileSystem`. However this isn't made clear in the
docs:

1. The README talks about the non-underscored `NIOFileSystem`.
2. There are no hosted API docs `NIOFileSystem`.
3. The hosted API docs for `NIO` point to the docs for `NIOFileSystem`
-- results in 404.

### Modifications:

- Update the README to refer to `_NIOFileSystem`.
- Update the hosted API docs for `NIO` to point to docs for
`_NIOFileSystem`.
- Add hosted API docs for `NIOFileSystem` with a statement explaining
the situation and pointing people to the docs
  for `_NIOFileSystem`.

### Result:

Clearer documentation on the state and relationship of the
`NIOFileSystem` and `_NIOFileSystem` modules.
2026-02-06 18:56:42 +00:00
scottmarchant 90ed88baab test: Port NIOThreadPoolTest to Swift Testing. (#3490)
Ports NIOThreadPoolTest in NIOPosix from XCTest to Swift Testing.

### Motivation:

Tests should use the latest Swift Testing framework when possible.

### Modifications:

- Ported `NIOThreadPoolTest` from XCTest to Swift Testing
- Updated test implementation from Dispatch to Swift Concurrency as
needed to support test functionality

### Result:

All tests continue to pass, NIOThreadPoolTest now has more modern Swift
Testing syntax.
2026-02-06 15:19:18 +00:00
scottmarchant e0476c6072 test: Port EventLoopFutureTest to Swift Testing. (#3491)
Port `EventLoopFutureTest` to Swift Testing. Includes some adjustments
to ensure tests are ran from the event loop, among other minor changes

### Motivation:

Tests should use the latest Swift Testing framework when possible.

### Modifications:

- Ported `EventLoopFutureTest ` from XCTest to Swift Testing
- Consolidated instantiations of `EmbeddedEventLoop()` into a private
function called `makeEventLoop` to allow for easier injection of a
different loop from a single location.
- Migrated some `.wait` usages to `.get` to allow tests to run async.

### Result:

All tests continue to pass, EventLoopFutureTest now has more modern
Swift Testing syntax.
2026-02-05 08:24:43 +00:00
Fabian Fett 6a6f7d7c33 Centralize UnsafeTransfer in NIOCore (#3492)
We have a number of copies of `UnsafeTransfer` and two copies of
`UnsafeMutableTransferBox` in our code base. Before introducing more of
those, lets centralize to just one using a `package` access modifier.
2026-02-03 10:27:30 +00:00
Franz Busch c90e69406b Fix Xcode 26.2 CI copy and paste error (#3499) 2026-01-28 16:32:30 +00:00
Franz Busch d1aaf4371a Add support for Xcode 26.2 (#3498)
Adds the required new inputs to the macOS tests and benchmark reusable
workflows
2026-01-28 16:47:02 +01:00
Franz Busch 5e72fc1029 Move nightly-next CI to 6.3 (#3496)
This PR moves the nightly-next CI to use the 6.3 images. Since those
checks should be non required on any user of this package and the name
of the check doesn't change with moving images this should be a
non-breaking change.
2.94.0
2026-01-27 16:32:59 +01:00
Si Beaumont 37ffc4b8d5 Add API to enable throwing in EmbeddedChannel.getOption and .setOption if channel is closed (#3495)
### Motivation:

Channels based on `BaseSocketChannel` throw in both `getOption` and
`setOption` if the channel has been closed, since the `setsockopt` will
fail. However, the current behavior of `EmbeddedChannel` is options
remain writable and readable on closed channels.

There are situations where we'd like to be able to model the runtime
behaviour of the real channel in tests, e.g. to test this fix:
https://github.com/apple/swift-nio-extras/pull/304.

### Modifications:

- Add API to enable throwing in `EmbeddedChannel.getOption` and
`.setOption` if channel is closed.
- Add a test for this new behavior.

### Result:

- New API to enable throwing in `EmbeddedChannel.getOption` and
`.setOption` if channel is closed.
- No observable change for existing users of `EmbeddedChannel`.
2026-01-27 12:06:55 +00:00
dependabot[bot] b2a14468d9 Bump actions/checkout from 6.0.1 to 6.0.2 (#3493)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.1
to 6.0.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/releases">actions/checkout's
releases</a>.</em></p>
<blockquote>
<h2>v6.0.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Add orchestration_id to git user-agent when ACTIONS_ORCHESTRATION_ID
is set by <a
href="https://github.com/TingluoHuang"><code>@​TingluoHuang</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2355">actions/checkout#2355</a></li>
<li>Fix tag handling: preserve annotations and explicit fetch-tags by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2356">actions/checkout#2356</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v6.0.1...v6.0.2">https://github.com/actions/checkout/compare/v6.0.1...v6.0.2</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v6.0.2</h2>
<ul>
<li>Fix tag handling: preserve annotations and explicit fetch-tags by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2356">actions/checkout#2356</a></li>
</ul>
<h2>v6.0.1</h2>
<ul>
<li>Add worktree support for persist-credentials includeIf by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2327">actions/checkout#2327</a></li>
</ul>
<h2>v6.0.0</h2>
<ul>
<li>Persist creds to a separate file by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li>
<li>Update README to include Node.js 24 support details and requirements
by <a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li>
</ul>
<h2>v5.0.1</h2>
<ul>
<li>Port v6 cleanup to v5 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li>
</ul>
<h2>v5.0.0</h2>
<ul>
<li>Update actions checkout to use node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li>
</ul>
<h2>v4.3.1</h2>
<ul>
<li>Port v6 cleanup to v4 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2305">actions/checkout#2305</a></li>
</ul>
<h2>v4.3.0</h2>
<ul>
<li>docs: update README.md by <a
href="https://github.com/motss"><code>@​motss</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li>
<li>Add internal repos for checking out multiple repositories by <a
href="https://github.com/mouismail"><code>@​mouismail</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li>
<li>Documentation update - add recommended permissions to Readme by <a
href="https://github.com/benwells"><code>@​benwells</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li>
<li>Adjust positioning of user email note and permissions heading by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li>
<li>Update CODEOWNERS for actions by <a
href="https://github.com/TingluoHuang"><code>@​TingluoHuang</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li>
<li>Update package dependencies by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li>
</ul>
<h2>v4.2.2</h2>
<ul>
<li><code>url-helper.ts</code> now leverages well-known environment
variables by <a href="https://github.com/jww3"><code>@​jww3</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
<li>Expand unit test coverage for <code>isGhes</code> by <a
href="https://github.com/jww3"><code>@​jww3</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
</ul>
<h2>v4.2.1</h2>
<ul>
<li>Check out other refs/* by commit if provided, fall back to ref by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li>
</ul>
<h2>v4.2.0</h2>
<ul>
<li>Add Ref and Commit outputs by <a
href="https://github.com/lucacome"><code>@​lucacome</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1180">actions/checkout#1180</a></li>
<li>Dependency updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>- <a
href="https://redirect.github.com/actions/checkout/pull/1777">actions/checkout#1777</a>,
<a
href="https://redirect.github.com/actions/checkout/pull/1872">actions/checkout#1872</a></li>
</ul>
<h2>v4.1.7</h2>
<ul>
<li>Bump the minor-npm-dependencies group across 1 directory with 4
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1739">actions/checkout#1739</a></li>
<li>Bump actions/checkout from 3 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1697">actions/checkout#1697</a></li>
<li>Check out other refs/* by commit by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkout#1774</a></li>
<li>Pin actions/checkout's own workflows to a known, good, stable
version. by <a href="https://github.com/jww3"><code>@​jww3</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1776">actions/checkout#1776</a></li>
</ul>
<h2>v4.1.6</h2>
<ul>
<li>Check platform to set archive extension appropriately by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1732">actions/checkout#1732</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/checkout/commit/de0fac2e4500dabe0009e67214ff5f5447ce83dd"><code>de0fac2</code></a>
Fix tag handling: preserve annotations and explicit fetch-tags (<a
href="https://redirect.github.com/actions/checkout/issues/2356">#2356</a>)</li>
<li><a
href="https://github.com/actions/checkout/commit/064fe7f3312418007dea2b49a19844a9ee378f49"><code>064fe7f</code></a>
Add orchestration_id to git user-agent when ACTIONS_ORCHESTRATION_ID is
set (...</li>
<li>See full diff in <a
href="https://github.com/actions/checkout/compare/8e8c483db84b4bee98b60c0593521ed34d9990e8...de0fac2e4500dabe0009e67214ff5f5447ce83dd">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=6.0.1&new-version=6.0.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-26 09:24:06 +00:00
scottmarchant 5bf0267df4 build: Elide NIOPosix for WASI platforms only (#3485)
### Motivation:

Packages that consume NIOPosix should be able to compile to WASI
platforms without special configurations. This change elides the
NIOPosix source from WASI platforms to simplify configuration.

Without this change:

```swift
dependencies: [
    // Without this PR, downstream packages must maintain exhaustive platform list to exclude `.wasi`:
    .product(
        name: "NIOPosix",
        package: "swift-nio",
        condition: .when(platforms: [
            .macOS,
            .macCatalyst,
            .iOS,
            .tvOS,
            .watchOS,
            .visionOS,
            .driverKit,
            .linux,
            .windows,
            .android,
            .openbsd,
            // .wasi // <-- Need to exclude this, because there is no exclusion list api for SPM conditionals
        ])
    ),
]
```

With this change:

```swift
dependencies: [
    // Without this PR, downstream packages can consume NIOPosix simply:
    .product(name: "NIOPosix", package: "swift-nio"),
]
```

### Modifications:

- Add compiler directives (`#if !os(WASI)`) to all source files in
NIOPosix

### Result:

Downstream packages can compile to wasm without manually excluding the
NIOPosix dependency.

### Testing performed

- Verified `swift build --swift-sdk wasm32-unknown-wasip1 --target
NIOPosix` compiles, which demonstrates proper elision of source files in
NIOPosix that aren't wasm-ready.
- Confirmed GitHub [checks
pass](https://github.com/PassiveLogic/swift-nio/actions/runs/21151341738).
2026-01-23 16:39:32 +00:00
scottmarchant 3f80264185 build: Elide NIOEmbedded for WASI platforms only (#3484)
### Motivation:

Packages that consume NIOEmbedded should be able to compile to WASI
platforms without special configurations. This change elides the
NIOEmbedded source from WASI platforms to simplify configuration.

Without this change:

```swift
dependencies: [
    // Without this PR, downstream packages must maintain exhaustive platform list to exclude `.wasi`:
    .product(
        name: "NIOEmbedded",
        package: "swift-nio",
        condition: .when(platforms: [
            .macOS,
            .macCatalyst,
            .iOS,
            .tvOS,
            .watchOS,
            .visionOS,
            .driverKit,
            .linux,
            .windows,
            .android,
            .openbsd,
            // .wasi // <-- Need to exclude this, because there is no exclusion list api for SPM conditionals
        ])
    ),
]
```

With this change:

```swift
dependencies: [
    // Without this PR, downstream packages can consume NIOEmbedded simply:
    .product(name: "NIOEmbedded", package: "swift-nio"),
]
```

### Modifications:

- Fix compiler directive using in AsyncTestingChannel.swift to include
an extension

### Result:

Packages can compile to wasm without manually excluding the NIOEmbedded
dependency.

### Testing performed

- Verified `swift build --swift-sdk wasm32-unknown-wasip1 --target
NIOEmbedded` compiles, which demonstrates proper elision of source files
in NIOEmbedded that aren't wasm-ready.
- Confirmed GitHub [checks
pass](https://github.com/PassiveLogic/swift-nio/actions/runs/21151287289).
2026-01-22 18:58:24 +00:00
scottmarchant c337170d91 chore: Elide mutex variable from Lock class when it is unused (#3483)
### Motivation:

In reviewing a copy of this file in swift-log, @czechboy0
[noticed](https://github.com/apple/swift-log/pull/398#discussion_r2685605800)
that the `mutex` variable is essentially unused and unnecessarily
allocated for certain conditions. It is possible the compiler elided
usage. But given the unconditional variable reference in the `deinit`,
the compiler may not be able to.

This change ensures the `mutex` property is completely elided except in
the conditions where it is used.

### Modifications:

- Adjusted compiler directive pattern to completely elide the `mutex`
property for conditions where it would be unused.
- Adjusted `deinit` to avoid referencing `mutex` unless it is compiled
into the `Lock` class

### Result:

The `mutex` property is no longer allocated or even compiled into the
`Lock` class for unsupported configurations. All unit tests and checks
pass.

### Research:

The `deinit` used to have a blanket call to `mutex.deallocate()`. This
has been moved into the platform-specific checks within the `deinit`.
This allows complete elision of the `mutex` property altogether. The
`_runtime(_multithreaded)` condition [appears to be rooted to this
implementation](https://github.com/swiftlang/swift/blob/ffc51b914602765c5d680241796dbd3c3711fa6b/lib/Basic/LangOptions.cpp#L490).
Diving deeper, all [operating systems in this
list](https://github.com/swiftlang/swift/blob/ffc51b914602765c5d680241796dbd3c3711fa6b/lib/Basic/LangOptions.cpp#L554)
except for UnknownOS and WASI result in `_runtime(_multithreaded)`
returning true. That means that `OpenBSD` (and many other operating
systems besides windows) were almost certainly using the `#elseif
(compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) &&
_runtime(_multithreaded))` condition in the `deinit`. In conclusion,
moving `mutex.deallocate()` up into the conditional clauses inside
`deinit` should be identical to the previous implementation for all
operating systems except `llvm::Triple::UnknownOS`. And
`llvm::Triple::UnknownOS` is likely not to have every compiled anyways
due to the [check on line
32](https://github.com/apple/swift-nio/blob/main/Sources/NIOConcurrencyHelpers/lock.swift#L32).

So in summary, it is expected that this clean up will compile identical
to the previous implementation for all platforms except WASI platforms
that don't have pthread support. For non-pthread WASI platforms, the
unused `mutex` is properly elided from compilation altogether.

### Testing Done:

- Confirmed that unit tests pass locally using Xcode
- Verified [PR checks
pass](https://github.com/PassiveLogic/swift-nio/actions/runs/21149729751)
2026-01-22 17:57:10 +00:00
Vladimir Kukushkin c534a13dcb Update macOS runners to Tahoe (#3489)
Sequoia runners are no longer available and we need to switch to Tahoe.

### Motivation:

Sequoia runners are no longer available and jobs requesting them hang.

### Modifications:

Request Tahoe runners.

### Result:

Jobs no longer hang, macOS tests and benchmarks are running on Tahoe.
2026-01-22 15:54:50 +01:00
3405691582 233f61bc2c Initialize the mutexattr before use. (#3482)
As observed by @kkebo, this was missing from the implementation.

### Motivation:

Correct pthread API usage. 

### Modifications:

Initialize the pthread_mutexattr_t with pthread_mutexattr_init before
passed to pthread_mutex_init.

### Result:

Even though this lock API appears to be deprecated, we'll correct the
usage anyway. The non-deprecated NIOLock class doesn't have this
problem.
2.93.0
2026-01-19 18:18:38 +00:00
scottmarchant 6ce9173261 fix: Ensure pthread_mutexattr_t is properly cleaned up with pthread_mutexattr_destroy in Lock (#3480)
### Motivation:

In reviewing a copy of this file in swift-log, @czechboy0 noticed that
the call to `pthread_mutexattr_destroy` is missing. Since
pthread_mutexattr_t is like allocated on the stack rather than heap on
most platforms, this is likely not a memory leak. But for correctness
the missing destroy call should be added.

This change is the start of a resolution
[requested](https://github.com/apple/swift-log/pull/398#discussion_r2689459360)
in a corresponding Lock rollout in swift-log

### Modifications:

- Added missing call to `pthread_mutexattr_destroy`
- Duplicated test for `NIOLock` to also test `Lock`, as a sanity test.

### Result:

The new sanity test passes. If there are other ways this change can be
realistically verified as safe and proper, please feel free to provide
feedback in the PR.
2026-01-19 08:13:02 +00:00
Mike DrobandRick Newton-Rogers 192616d47e Disable Xcode-beta builds until one is installed (#3446)
Xcode-latest currently points to the 26.1 release so it gives us a false
sense of version coverage, and should be ignored.

### Motivation:

We do not need to build twice with the same environment.

### Modifications:

Set default value for Xcode latest beta enabled to `false`

### Result:

One fewer job generated for the macOS build matrix.

Co-authored-by: Rick Newton-Rogers <rnro@apple.com>
2026-01-14 13:42:44 +00:00
Adam FowlerandCory Benfield 61053a00bb NIOTypedHTTPClientUpgradeHandler will write Upgrade request on handlerAdded if active (#3473)
`NIOTypedHTTPClientUpgradeHandler.handlerAdded` will write upgrade
request if the channel is already active

### Motivation:

This has been added to make it easier to implement a websocket client
that supports proxies. If the upgrade handler is added after the proxy
connect has been processed previously nothing would happen.

### Modifications:

- Added new function
`NIOTypedHTTPClientUpgradeHandler.writeUpgradeRequest()` function. Call
this from `channelActive` and also from `handlerAdded` if the channel is
already active.
- Added test `testUpgradeHappensAfterHandlerAdded`

### Result:

NIOTypedHTTPClientUpgradeHandler now also works if it is added to a
channel after the channel is active

---------

Co-authored-by: Cory Benfield <lukasa@apple.com>
2026-01-14 11:40:00 +00:00
Joseph HeckandCory Benfield 6b33489e64 Updates to present docs on _NIOFileSystem (#3477)
This re-enables documentation lost during refactoring work, under the
target _NIOFileSystem

resolves #3474 

### Motivation:

During the refactoring of NIOFileSystem and recent Swift updates, the
mechanism to "shadow" symbols using `@_exported import` has stopped
working for documentation and imports for symbols, which means that
_NIOFileSystem is the target that needs to host the documentation for
this (for now)

### Modifications:

Moves DocC catalog into _NIOFileSystem target, and updates
disambiguation hashes on overloaded symbols in order to verify no
warnings are presented while generating documentation.
Updates .spi.yml to present _NIOFileSystem instead of NIOFileSystem

### Result:

Previous documentation should be available again, although at a slightly
different URI structure within Swift Package Index.

Co-authored-by: Cory Benfield <lukasa@apple.com>
2026-01-13 14:22:46 +00:00
Honza Dvorsky d779d6d104 Revert "Address potential template injection issues" (#3478)
Reverts apple/swift-nio#3466 - it broke CI in downstream projects.
2026-01-13 09:45:06 +00:00
Mike Drob 5ada77dafa Address potential template injection issues (#3466)
Instead of evaluating inputs directly in the run step, save them to an
environment variable and evaluate that.

### Motivation:

Evaluating workflow inputs directly can lead to injection attacks, such
as those described at https://docs.zizmor.sh/audits/#template-injection

### Modifications:

Evaluate workflow inputs as environment variables so that they do not
inadvertently execute arbitrary shell injections.

### Result:

The workflows should be more secure against malicious inputs.
2026-01-12 19:52:59 +00:00
Karan LokchandaniandCory Benfield 608e511d7a Add homeDirectory accessor to FileSystem (#3471)
Add homeDirectory accessor to FileSystem

### Motivation:
Addresses #3381 by adding a `homeDirectory` property to FileSystem,
equivalent to `FileManager.default.homeDirectoryForCurrentUser`.

### Modifications:
- Added `homeDirectory` property to `FileSystemProtocol` and implemented
in `FileSystem`
- Implementation checks `HOME` environment variable first, falls back to
`USERPROFILE` on Windows, or uses `getpwuid_r(3)` on POSIX systems
- Added system call wrappers (`system_getuid`, `libc_getpwuid_r`) with
proper platform guards
- Added `FileSystemError.getpwuid_r()` error helper

### Result:
Users can now access the home directory via `FileSystem.homeDirectory`,
returning a `FilePath` asynchronously. Follows the same pattern as
`currentWorkingDirectory` and `temporaryDirectory`.

Co-authored-by: Cory Benfield <lukasa@apple.com>
2026-01-12 15:10:25 +00:00
Karan Lokchandani 4a9a971110 docs: String(buffer:) needs to mention that it will always succeed (#3476)
### Motivation:
The documentation for String(buffer:) initializer does not clearly
indicate
that this operation will always succeed, which may leave developers
uncertain
about error handling requirements.
fixes: https://github.com/apple/swift-nio/issues/3449

### Modifications:
Updated the documentation for String(buffer:) to explicitly mention that
the initialization will always succeed.

### Result:
Developers will have clearer understanding that String(buffer:) is a
safe
operation that does not require error handling.

Signed-off-by: Karan <karanlokchandani@protonmail.com>
2.92.2
2026-01-08 15:58:15 +00:00
aryan-25 5dc3b4ba4a Cancel pending in/outbound consumers in EmbeddedChannelCore upon channel close (#3464)
### Motivation:

Currently, pending consumer closures remain in the
`{in}{out}boundBufferConsumer` queues of `EmbeddedChannelCore` even
after the channel closes. It is also possible to enqueue consumers
*after* the channel closes. In these cases, the consumer closures will
never be invoked and this can lead to unfavourable behaviour, as
observed in `NIOAsyncTestingChannel`'s `waitFor{In}{Out}boundWrite`
methods (the only places these queues are currently used).

`NIOAsyncTestingChannel`'s `waitFor{In}{Out}boundWrite` methods complete
a continuation *inside* the consumer closure. In the cases described
above, the continuation never completes and therefore
`waitFor{In}{Out}boundWrite` never returns.

### Modifications:

- Updated the element type in `EmbeddedChannelCore`'s
`{in}{out}boundBufferConsumer` from `(NIOAny) -> Void` to
`(Result<NIOAny, Error>) -> Void`.
- This is so that the `.failure` case can be used to notify the consumer
closure that the channel has closed.
- Changed the visibility of the `{in}{out}boundBufferConsumer`
properties from `internal` to `private` in order to prevent the queues
from being accessed and being appended to without the call site
considering whether the channel has been closed.
- Added new `internal` methods named
`enqueue{In}{Out}boundBufferConsumer(_:)` which take the consumer
closure as an argument and only append to the corresponding queue if the
channel isn't closed.
- If the channel is closed, the consumer closure is invoked immediately
with `.failure(ChannelError.ioOnClosedChannel)`.
- Updated `EmbeddedChannelCore`'s `close0` method to return a
`.failure(ChannelError.ioOnClosedChannel)` result to each closure in
`{in}{out}boundBufferConsumer` and empty both buffers.
- Updated `NIOAsyncTestingChannel`'s `waitFor{In}{Out}boundWrite` to
throw an error in the continuation upon receiving a `.failure` result.
- Added associated test cases.

### Result:

`EmbeddedChannelCore`'s `{in}{out}boundBufferConsumer` queues can be
used more safely: all pending closures will be invoked upon channel
close. As a result, `NIOAsyncTestingChannel`'s
`waitFor{In}{Out}boundWrite` no longer indefinitely blocks when the
channel closes.
2026-01-06 17:52:06 +00:00
e3d5c560e0 Fix coreCount on Linux when using cgroup v2 with CFS throttling disabled (#3462)
Fix coreCount on Linux when using cgroup v2 with CFS throttling disabled

### Motivation:

When using `swift-nio` on Linux with cgroup v2 enabled, but with CFS
throttling disabled, it falls back to attempting to read the cpuset file
at the cgroup v1 path. This does not exist, which in turns falls back to
returning `_SC_NPROCESSORS_ONLN`, which will return the total number of
cores available (ignoring cgroup assignments).

This has unexpected effects, including the default behaviour of starting
the `MultiThreadedEventLoopGroup.singleton` with significantly more
event loops than cores available to the workload.

### Modifications:

- Adds `SystemCalls.statfs`, and associated constants, to determine the
cgroup version.
- Adds `Linux.cgroupVersion()` API to expose cgroup version.
- Adds `Linux.cgroupV2MountPoint` variable to determine the cgroup v2
mount point.
- Adds `Linux.cpuSetPathV1` & `Linux.cpuSetPathV2` (and
`Linux.cpuSetPath` convenience) variables to determine the correct cpu
set path.
- Alters `System.coreCount` to use the appropriate logic from above to
ensure that `cpuset.cpus` is parsed from the correct location.

### Result:

`Linux.coreCount` should correctly parse and return the core count on
Linux cgroup v2 enabled systems (when CFS throttling is disabled), while
maintaining correctness for other configurations.

---------

Co-authored-by: Johannes Weiss <johannesweiss@apple.com>
Co-authored-by: Cory Benfield <lukasa@apple.com>
2.92.1
2026-01-05 14:26:21 +00:00
Mike Drob 92a262f376 ci: only run self-hosted ci on apple runners (#3468)
Limit Darwin Job execution to apple org.

### Motivation:

When the repo is forked, the forks do not have access to the self-hosted
CI, so it should not block actions in the forked environment.

### Modifications:

Conditionalize Darwin job execution.

### Result:

macOS CI will not queue on forks.
2025-12-23 08:11:06 +00:00
Johannes Weiss 1268babbc1 Allowlist 6.3 to be allowable for Swift Concurrency takeover (#3467)
Make 6.3 fast
2025-12-19 13:19:04 +00:00
aryan-25 b0895c094d Update integration test allocation thresholds for nightly-main (#3465)
### Motivation:

From
https://github.com/apple/swift-nio/actions/runs/20197120190/job/57982705638
onwards, the Integration Test benchmark runs have been failing for
`Linux (nightly-main)` due to allocations for seven tests falling below
the defined thresholds.

### Modifications:

Updated thresholds for the tests where the number of allocations were
out-of-threshold (seven in total).

### Result:

The Integration Test run for `Linux (nightly-main)` no longer fails.
2025-12-17 17:14:42 +00:00
aryan-25 a1605a3303 Use explicit class name in all Self.[un]wrap{In}{Out}bound{In}{Out} calls (#3463)
### Motivation:

Swift does not currently generic specialize static methods on final
classes that have no parent classes when the method is implemented in a
protocol. This means that calling such methods through `Self` will not
involve a generic specialization, whereas using the explicit type name
will.

This pattern manifests in the `[un]wrap{In}{Out}bound{In}{Out}` static
methods defined in the
[`ChannelInboundHandler`](https://github.com/apple/swift-nio/blob/27146d484478b1bb0f150e848758f3a34ed9cbd0/Sources/NIOCore/TypeAssistedChannelHandler.swift#L60)
and
[`ChannelOutboundHandler`](https://github.com/apple/swift-nio/blob/27146d484478b1bb0f150e848758f3a34ed9cbd0/Sources/NIOCore/TypeAssistedChannelHandler.swift#L95)
protocols and their use from all channel handler classes. As such, we
should replace the `Self` part in all
`Self.[un]wrap{In}{Out}bound{In}{Out}` calls with the explicit class
name.

### Modifications:

Replaced all `Self.[un]wrap{In}{Out}bound{In}{Out}` calls to use the
explicit class name.

### Result:

Eliminates unnecessary overhead.
2.92.0
2025-12-15 17:36:11 +00:00
Vladimir Kukushkin 27146d4844 Extract macOS benchmarks into a separate reusable workflow (#3461)
Extract macOS benchmarks into a separate reusable workflow to completely
remove them if they are not meant to be used.

### Motivation:

macOS Benchmarks is an opt-in workflow. If no macOS versions selected to
run benchmarks on, there is now a skipped matrix jobs, creating
unnecessary visual and cognitive noise during workflow results analysis:

<img width="391" height="369" alt="Screenshot 2025-12-10 at 14 22 50"
src="https://github.com/user-attachments/assets/959ff2a1-3872-4bff-97ac-1dca6d2d4ea5"
/>

### Modifications:

macOS benchmarks workflow is extracted into a separate reusable
workflow.

### Result:

- If a repo is not running macOS benchmarks, there is no extra skipped
jobs.
- When willing to run macOS benchmarks, `macos_benchmarks.yml` workflow
should be used.
- This is a breaking change in the reusable workflows interface, so all
the repos (just the `swift-log`?) need to adopt the new reusable
workflow.


Test runs:
- old workflow run with [unused macOS benchmarks skipped and
visible](https://github.com/apple/swift-nio/actions/runs/20102000384/job/57675171794)
- new workflow run with [no macOS benchmarks
visible](https://github.com/apple/swift-nio/actions/runs/20102837431)
because they are not used
- new workflow run with actually [executed and visible macOS
benchmarks](https://github.com/apple/swift-nio/actions/runs/20103726421)
2025-12-11 11:31:36 +00:00
Mike Drob 03ec49f001 update actions/checkout and introduce dependabot (#3459)
Update the actions/checkout action to the latest release (v6.0.1)

### Motivation:

https://github.com/actions/checkout?tab=readme-ov-file#whats-new

> * Improved credential security: persist-credentials now stores
credentials in a separate file under $RUNNER_TEMP instead of directly in
.git/config
> * No workflow changes required — git fetch, git push, etc. continue to
work automatically
> * Running authenticated git commands from a [Docker container
action](https://docs.github.com/actions/sharing-automations/creating-actions/creating-a-docker-container-action)
requires Actions Runner
[v2.329.0](https://github.com/actions/runner/releases/tag/v2.329.0) or
later
> * Updated to the node24 runtime
This requires a minimum Actions Runner version of
[v2.327.1](https://github.com/actions/runner/releases/tag/v2.327.1) to
run.

### Modifications:

Updated the dependency version and added dependabot to take care of
future upgrades.

### Result:

Our workflows will be more secure!
2025-12-08 16:25:40 +00:00
Adam Fowler 888f4affd6 Add ByteBuffer.readableBytesUInt8Span (#3458)
Add  ByteBuffer.readableBytesUInt8Span

### Motivation:

Provide access to readableBytes as a Span<UInt8>

### Modifications:

Added computed property `ByteBuffer.readableBytesUInt8Span`. I also
attempted to add `ByteBuffer.mutableReadableBytesUInt8Span` but due to
an apparently faulty exclusivity issue I couldn't get this to work. See
https://github.com/swiftlang/swift/issues/81218.

### Result:

You can access the readable bytes of a ByteBuffer as a Span<UInt8>
2025-12-05 15:32:17 +00:00
George Barnett 663ddc80f2 Dont hold lock over continuation in TokenBucket (#3455)
Motivation:

The various 'withMumbleContinuation' APIs are supposed to be invoked
synchronously with the caller. This assumption allows a lock to be
acquired before the call and released from the body of the
'withMumbleContinuation' after e.g. storing the continuation. However
this isn't the case and the job may be re-enqueued on the executor
meaning that this is pattern is vulnerable to deadlocks.

Modifications:

- Drop and reacquire the lock in TokenBucket.
- Switch to NIOLockedValueBox

Result:

Lower chance of deadlock
2.91.0
2025-12-01 15:46:46 +00:00
Cory BenfieldandGeorge Barnett 2bc6627d74 Add some benchmarks for UDP and UDP metadata (#3457)
Motivation:

I'm unhappy with AddressedEnvelope.Metadata, and I plan to change it.
Before I change it, I want some way to get a clear picture of whether my
changes make things better or worse.

Modifications:

Added a bunch of benchmarks

Result:

Some data will be available.

---------

Co-authored-by: George Barnett <gbarnett@apple.com>
2025-12-01 13:40:11 +00:00
Rafael Cepeda b60d141920 Document when channel.remoteAddress field can be nil (#3456)
Added extra documentation to `channel.remoteAddress` field to capture a
scenario where this field might be `nil`

### Motivation:

It is somewhat common for users to have code like
`channel.remoteAddress!` in their implementation, as it is a reasonable
assumption to think a socket connection will have an associated remote
address. However, in at least one known situation this might not be the
case. When that happens, user's code might crash due to the force unwrap
of the optional field.

### Modifications:

Introduced more documentation to make it clear that users should be
prepared to handle the `nil` scenario.

### Result:

Less frequent mishandling of `channel.remoteAddress`.
2025-12-01 11:09:56 +00:00
3405691582andRick Newton-Rogers 2bc71a6aff Partial OpenBSD support. (#3394)
### Motivation:

Additional partial platform support.

### Modifications: 

* Create a new CNIOBSD module for OpenBSD, for general cleanliness, and
make use of it throughout. Some of the changes are borrowed directly
from CNIOLinux; some may technically be unnecessary; I am erring
somewhat on expediency to functionality.

* Since `malloc_size` is unavailable on OpenBSD, and some of the helpers
make use of ManagedBufferPointer which makes use of it, mark some of
this as unavailable as well.

* Usual pthread optional typing changes, since pthread types are
pointers on this platform.

* Add conditionals to exclude other functionality not available here,
like IP_RECVPKTINFO or IP_PKTINFO.

* d_ino is a backwards-compatibility macro valued token for dirent, so
instead, just expand with a conditional.

* Use kqueue on OpenBSD. This necessitates adding some conditionals for
type and feature compatibility.

* The vsock API is unavailable on OpenBSD.

### Result:

Tested the NIOTCPEchoClient and NIOTCPEchoServer appears to work and
that swift-nio-ssh (hopefully wlog) builds with a local repository with
these changes.

Because NIOFS/_NIOFileSystem depend on some non-portable components,
such as extended attributes, sendfile, non-portable linkat flags, and
renameat2, this is only just partial OpenBSD support, which means that
`swift build` on the full swift-nio project won't build cleanly, but at
least the portable parts can be used to build servers and clients. This
commit will mark the linked bug as fixed; I will open a new bug for file
system support.

Fixes #3383.

---------

Co-authored-by: Rick Newton-Rogers <rnro@apple.com>
2025-11-26 09:07:02 +00:00
George Barnett cda536de37 Dont hold lock over continuation in NIOThrowingAsyncSequenceProducer (#3454)
Motivation:

The various 'withMumbleContinuation' APIs are supposed to be invoked
synchronously with the caller. This assumption allows a lock to be
acquired before the call and released from the body of the
'withMumbleContinuation' after e.g. storing the continuation. However
this isn't the case and the job may be re-enqueued on the executor
meaning that this is pattern is vulnerable to deadlocks.

Modifications:

- Drop and reacquire the lock in the NIOThrowingAsyncSequenceProducer.
- Merge 'next()' and 'next(for:)' methods in the state machine into a
single func; this reduces the amount of duplicated logic across the two
functions.

Result:

Lower chance of deadlock
2025-11-25 09:39:54 +00:00
George Barnett d2feeaa363 Handle cancellation between dropping and reaquiring lock (#3453)
Motivation:

This is a follow up to 5bf841dd to handle the yield task being cancelled
between dropping the lock and re-acquiring it a moment later in the
'withContinuation' block.

Modifications:

- Add an extra cancellation check when yielding with a continuation.

Result:

Fewer issues
2025-11-25 09:16:35 +00:00
George Barnett 5bf841dde5 Drop and reacquire lock over continuation call (#3452)
Motivation:

The various 'withMumbleContinuation' APIs are supposed to be invoked
synchronously with the caller. This assumption allows a lock to be
acquired before the call and released from the body of the
'withMumbleContinuation' after e.g. storing the continuation. However
this isn't the case and the job may be re-enqueued on the executor
meaning that this is pattern is vulnerable to deadlocks.

Modifications:

- Drop and reacquire the lock in the NIOAsyncWriter

Result:

Lower chance of deadlock
2025-11-24 11:13:50 +00:00
Johannes WeissandGeorge Barnett 3eea09220e do not waste time when metrics delegate is not set (#3441)
### Motivation:

Unfortunately, even if no metics delegate has been configured,
significant work is being done _each EL tick_. Concretely:

- 2 `NIODealine.now()` calls which are expensive particularly on some
hypervisors (may cause a VMEXIT!)
- Some unnecessary calculations

### Modifications:

- Only perform the metrics delegate work if a delegate has actually been
set

### Result:

If no delegate is set, each EL tick will now be faster.

Co-authored-by: George Barnett <gbarnett@apple.com>
2.90.1
2025-11-20 16:13:15 +00:00
George Barnett 888c16a7d3 Add some slack to the TCPEcho benchmark (#3451)
Motivation:

TCPEcho has some run-to-run variation which makes CI flaky.

Modifications:

- Add a little slack to TCPEcho; it has a low total alloc count so some
slack is fine as it's less than a per iteration allocation

Result:

CI less flaky
2025-11-20 15:25:04 +00:00