Skip to content
Open
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
6 changes: 4 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ let package = Package(
)

for target in package.targets {
#if compiler(<6.2)
// Needed since Sendable checking with isolated methods is not working correctly before 6.2
if target.swiftSettings == nil {
target.swiftSettings = []
}
#if compiler(<6.2)
// Needed since Sendable checking with isolated methods is not working correctly before 6.2
target.swiftSettings?.append(.swiftLanguageMode(.v5))
#else
target.swiftSettings?.append(.enableUpcomingFeature("NonisolatedNonsendingByDefault"))
#endif
}
34 changes: 34 additions & 0 deletions Sources/ConcurrencyHelpers/NonSendingIteratorNext.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftServiceLifecycle open source project
//
// Copyright (c) 2026 Apple Inc. and the SwiftServiceLifecycle project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftServiceLifecycle project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension AsyncIteratorProtocol {
@usableFromInline
package mutating func nonSendingNext() async rethrows -> Element? {
guard #available(macOS 15.0, *) else {
// On older Apple platforms, we cannot pass through the isolation
// That means next() runs on task isolation
// That is not safe to the compiler, because the caller of this send function may have called
// it concurrently
// However, that shouldn't happen in practise since the iterator itself is not Sendable

nonisolated(unsafe) var this = self
defer {
self = this
}
return try await this.next()
}
return try await self.next(isolation: #isolation)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//

import AsyncAlgorithms
import ConcurrencyHelpers

@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension AsyncSequence where Self: Sendable, Element: Sendable {
Expand Down Expand Up @@ -82,7 +83,7 @@ where Base.Element: Sendable {
return nil
}

let value = try await self._iterator.next()
let value = try await self._iterator.nonSendingNext()

switch value {
case .base(let element):
Expand Down Expand Up @@ -157,7 +158,7 @@ struct AsyncMapNilSequence<Base: AsyncSequence & Sendable>: AsyncSequence, Senda

@inlinable
mutating func next() async rethrows -> Element? {
let value = try await self._iterator.next()
let value = try await self._iterator.nonSendingNext()

if let value {
return .element(value)
Expand Down
4 changes: 2 additions & 2 deletions Sources/ServiceLifecycle/Docs.docc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,5 @@ let package = Package(

- ``cancelOnGracefulShutdown(_:)``
- ``withGracefulShutdownHandler(operation:onGracefulShutdown:)``
- ``withGracefulShutdownHandler(operation:onGracefulShutdown:)-1x21p``
- ``withTaskCancellationOrGracefulShutdownHandler(operation:onCancelOrGracefulShutdown:)-81m01``
- ``withGracefulShutdownHandler(isolation:operation:onGracefulShutdown:)``
- ``withTaskCancellationOrGracefulShutdownHandler(isolation:operation:onCancelOrGracefulShutdown:)``
2 changes: 1 addition & 1 deletion Sources/ServiceLifecycle/ServiceGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,7 @@ public actor ServiceGroup: Sendable, Service {
group.addTask {
return await withTaskCancellationHandler {
var iterator = channel.makeAsyncIterator()
if let addedService = await iterator.next() {
if let addedService = await iterator.nonSendingNext() {
return .newServiceAdded(addedService)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/UnixSignals/UnixSignalsSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public struct UnixSignalsSequence: AsyncSequence, Sendable {
}

public mutating func next() async -> UnixSignal? {
return await self.iterator.next()
return await self.iterator.nonSendingNext()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ final class AsyncCancelOnGracefulShutdownSequenceTests: XCTestCase {

var iterator = resultStream.makeAsyncIterator()

await XCTAsyncAssertEqual(await iterator.next(), 1)
await XCTAsyncAssertEqual(await iterator.nonSendingNext(), 1)

gracefulShutdownTrigger.triggerGracefulShutdown()

await XCTAsyncAssertEqual(await iterator.next(), nil)
await XCTAsyncAssertEqual(await iterator.nonSendingNext(), nil)
}
}
}
Expand All @@ -61,11 +61,11 @@ final class AsyncCancelOnGracefulShutdownSequenceTests: XCTestCase {
var iterator = resultStream.makeAsyncIterator()
baseContinuation.yield(1)

await XCTAsyncAssertEqual(await iterator.next(), 1)
await XCTAsyncAssertEqual(await iterator.nonSendingNext(), 1)

baseContinuation.finish()

await XCTAsyncAssertEqual(await iterator.next(), nil)
await XCTAsyncAssertEqual(await iterator.nonSendingNext(), nil)
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions Tests/ServiceLifecycleTests/GracefulShutdownTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ final class GracefulShutdownTests: XCTestCase {

var iterator = stream.makeAsyncIterator()

await XCTAsyncAssertEqual(await iterator.next(), "onGracefulShutdown")
await XCTAsyncAssertEqual(await iterator.next(), "operation")
await XCTAsyncAssertEqual(await iterator.nonSendingNext(), "onGracefulShutdown")
await XCTAsyncAssertEqual(await iterator.nonSendingNext(), "operation")
}

func testWithGracefulShutdownHandler_whenNested() async {
Expand Down Expand Up @@ -96,15 +96,15 @@ final class GracefulShutdownTests: XCTestCase {

var iterator = stream.makeAsyncIterator()

await XCTAsyncAssertEqual(await iterator.next(), "outerOperation")
await XCTAsyncAssertEqual(await iterator.next(), "innerOperation")
await XCTAsyncAssertEqual(await iterator.next(), "innerOperation")
await XCTAsyncAssertEqual(await iterator.nonSendingNext(), "outerOperation")
await XCTAsyncAssertEqual(await iterator.nonSendingNext(), "innerOperation")
await XCTAsyncAssertEqual(await iterator.nonSendingNext(), "innerOperation")

gracefulShutdownTestTrigger.triggerGracefulShutdown()

await XCTAsyncAssertEqual(await iterator.next(), "outerOnGracefulShutdown")
await XCTAsyncAssertEqual(await iterator.next(), "innerOnGracefulShutdown")
await XCTAsyncAssertEqual(await iterator.next(), "innerOnGracefulShutdown")
await XCTAsyncAssertEqual(await iterator.nonSendingNext(), "outerOnGracefulShutdown")
await XCTAsyncAssertEqual(await iterator.nonSendingNext(), "innerOnGracefulShutdown")
await XCTAsyncAssertEqual(await iterator.nonSendingNext(), "innerOnGracefulShutdown")
}
} onGracefulShutdown: {
continuation.yield("outerOnGracefulShutdown")
Expand Down
Loading
Loading