diff --git a/Package.swift b/Package.swift index 8ce3f41..2f6b553 100644 --- a/Package.swift +++ b/Package.swift @@ -3,12 +3,6 @@ import PackageDescription let package = Package( name: "swift-service-lifecycle", - platforms: [ - .macOS(.v10_15), - .iOS(.v13), - .watchOS(.v6), - .tvOS(.v13), - ], products: [ .library( name: "ServiceLifecycle", @@ -30,7 +24,7 @@ let package = Package( ), .package( url: "https://github.com/apple/swift-async-algorithms.git", - from: "1.0.0" + from: "1.0.4" ), ], targets: [ diff --git a/Sources/ServiceLifecycle/AsyncCancelOnGracefulShutdownSequence.swift b/Sources/ServiceLifecycle/AsyncCancelOnGracefulShutdownSequence.swift index 47fef51..0144264 100644 --- a/Sources/ServiceLifecycle/AsyncCancelOnGracefulShutdownSequence.swift +++ b/Sources/ServiceLifecycle/AsyncCancelOnGracefulShutdownSequence.swift @@ -14,6 +14,7 @@ import AsyncAlgorithms +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension AsyncSequence where Self: Sendable, Element: Sendable { /// Creates an asynchronous sequence that is cancelled once graceful shutdown has triggered. /// @@ -24,6 +25,7 @@ extension AsyncSequence where Self: Sendable, Element: Sendable { } /// An asynchronous sequence that is cancelled once graceful shutdown has triggered. +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public struct AsyncCancelOnGracefulShutdownSequence: AsyncSequence, Sendable where Base.Element: Sendable { @usableFromInline @@ -101,6 +103,7 @@ where Base.Element: Sendable { /// This is just a helper extension and sequence to allow us to get the `nil` value as an element of the sequence. /// We need this since merge is only finishing when both upstreams are finished but we need to finish when either is done. /// In the future, we should move to something in async algorithms if it exists. +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension AsyncSequence where Self: Sendable, Element: Sendable { @inlinable func mapNil() -> AsyncMapNilSequence { @@ -109,6 +112,7 @@ extension AsyncSequence where Self: Sendable, Element: Sendable { } @usableFromInline +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) struct AsyncMapNilSequence: AsyncSequence, Sendable where Base.Element: Sendable { @usableFromInline enum ElementOrEnd: Sendable { diff --git a/Sources/ServiceLifecycle/AsyncGracefulShutdownSequence.swift b/Sources/ServiceLifecycle/AsyncGracefulShutdownSequence.swift index 209776c..24ce9de 100644 --- a/Sources/ServiceLifecycle/AsyncGracefulShutdownSequence.swift +++ b/Sources/ServiceLifecycle/AsyncGracefulShutdownSequence.swift @@ -18,6 +18,7 @@ /// /// - Note: This sequence respects cancellation and thus is `throwing`. @usableFromInline +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) struct AsyncGracefulShutdownSequence: AsyncSequence, Sendable { @usableFromInline typealias Element = CancellationWaiter.Reason diff --git a/Sources/ServiceLifecycle/CancellationWaiter.swift b/Sources/ServiceLifecycle/CancellationWaiter.swift index 1476fec..565fc95 100644 --- a/Sources/ServiceLifecycle/CancellationWaiter.swift +++ b/Sources/ServiceLifecycle/CancellationWaiter.swift @@ -14,6 +14,7 @@ /// An actor that provides a function to wait on cancellation/graceful shutdown. @usableFromInline +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) actor CancellationWaiter { @usableFromInline enum Reason: Sendable { diff --git a/Sources/ServiceLifecycle/GracefulShutdown.swift b/Sources/ServiceLifecycle/GracefulShutdown.swift index 56daca0..7d210ce 100644 --- a/Sources/ServiceLifecycle/GracefulShutdown.swift +++ b/Sources/ServiceLifecycle/GracefulShutdown.swift @@ -37,6 +37,7 @@ import ConcurrencyHelpers /// - handler: The handler which is invoked once graceful shutdown has been triggered. // Unsafely inheriting the executor is safe to do here since we are not calling any other async method // except the operation. This makes sure no other executor hops would occur here. +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public func withGracefulShutdownHandler( isolation: isolated (any Actor)? = #isolation, operation: () async throws -> T, @@ -58,6 +59,7 @@ public func withGracefulShutdownHandler( } @available(*, deprecated, message: "Use the method with the isolation parameter instead.") +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) @_disfavoredOverload public func withGracefulShutdownHandler( operation: () async throws -> T, @@ -83,6 +85,7 @@ public func withGracefulShutdownHandler( // Swift versions since the semantics changed. @_disfavoredOverload @_unsafeInheritExecutor +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public func withGracefulShutdownHandler( operation: () async throws -> T, onGracefulShutdown handler: @Sendable @escaping () -> Void @@ -127,6 +130,7 @@ public func withGracefulShutdownHandler( /// - handler: The handler which is invoked once graceful shutdown or task cancellation has been triggered. // Unsafely inheriting the executor is safe to do here since we are not calling any other async method // except the operation. This makes sure no other executor hops would occur here. +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public func withTaskCancellationOrGracefulShutdownHandler( isolation: isolated (any Actor)? = #isolation, operation: () async throws -> T, @@ -139,6 +143,7 @@ public func withTaskCancellationOrGracefulShutdownHandler( } } @available(*, deprecated, message: "Use the method with the isolation parameter instead.") +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) @_disfavoredOverload public func withTaskCancellationOrGracefulShutdownHandler( operation: () async throws -> T, @@ -152,6 +157,7 @@ public func withTaskCancellationOrGracefulShutdownHandler( } #else @available(*, deprecated, message: "Use the method with the isolation parameter instead.") +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) @_disfavoredOverload @_unsafeInheritExecutor public func withTaskCancellationOrGracefulShutdownHandler( @@ -172,6 +178,7 @@ public func withTaskCancellationOrGracefulShutdownHandler( /// graceful shutdown is triggered then this method will throw a `CancellationError`. /// /// - Throws: `CancellationError` if the task is cancelled. +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public func gracefulShutdown() async throws { switch await AsyncGracefulShutdownSequence().first(where: { _ in true }) { case .cancelled: @@ -193,6 +200,7 @@ enum ValueOrGracefulShutdown: Sendable { /// Cancels the closure when a graceful shutdown was triggered. /// /// - Parameter operation: The actual operation. +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public func cancelWhenGracefulShutdown( _ operation: @Sendable @escaping () async throws -> T ) async rethrows -> T { @@ -243,12 +251,14 @@ public func cancelWhenGracefulShutdown( // renamed pattern has been shown to cause compiler crashes in 5.x compilers. @available(*, deprecated, message: "renamed to cancelWhenGracefulShutdown") #endif +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public func cancelOnGracefulShutdown( _ operation: @Sendable @escaping () async throws -> T ) async rethrows -> T? { return try await cancelWhenGracefulShutdown(operation) } +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension Task where Success == Never, Failure == Never { /// A Boolean value that indicates whether the task is gracefully shutting down /// @@ -263,6 +273,7 @@ extension Task where Success == Never, Failure == Never { } @_spi(TestKit) +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public enum TaskLocals { @TaskLocal @_spi(TestKit) @@ -270,6 +281,7 @@ public enum TaskLocals { } @_spi(TestKit) +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public final class GracefulShutdownManager: @unchecked Sendable { struct Handler { /// The id of the handler. diff --git a/Sources/ServiceLifecycle/Service.swift b/Sources/ServiceLifecycle/Service.swift index 000303f..8c5a1b6 100644 --- a/Sources/ServiceLifecycle/Service.swift +++ b/Sources/ServiceLifecycle/Service.swift @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// /// This is the basic protocol that a service has to implement. +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public protocol Service: Sendable { /// This method is called when the ``ServiceGroup`` is starting all the services. /// diff --git a/Sources/ServiceLifecycle/ServiceGroup.swift b/Sources/ServiceLifecycle/ServiceGroup.swift index 580313a..48e882d 100644 --- a/Sources/ServiceLifecycle/ServiceGroup.swift +++ b/Sources/ServiceLifecycle/ServiceGroup.swift @@ -17,6 +17,7 @@ import UnixSignals import AsyncAlgorithms /// A ``ServiceGroup`` is responsible for running a number of services, setting up signal handling and signalling graceful shutdown to the services. +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public actor ServiceGroup: Sendable, Service { /// The internal state of the ``ServiceGroup``. private enum State { @@ -893,6 +894,7 @@ public actor ServiceGroup: Sendable, Service { } // This should be removed once we support Swift 5.9+ +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension AsyncStream { fileprivate static func makeStream( of elementType: Element.Type = Element.self, diff --git a/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift b/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift index 30b21ff..c1beef8 100644 --- a/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift +++ b/Sources/ServiceLifecycle/ServiceGroupConfiguration.swift @@ -18,6 +18,7 @@ import UnixSignals let deprecatedLoggerLabel = "service-lifecycle-deprecated-method-logger" /// The configuration for the ``ServiceGroup``. +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public struct ServiceGroupConfiguration: Sendable { /// The group's logging configuration. public struct LoggingConfiguration: Sendable { diff --git a/Sources/ServiceLifecycleTestKit/GracefulShutdown.swift b/Sources/ServiceLifecycleTestKit/GracefulShutdown.swift index e37c0c5..bfdf247 100644 --- a/Sources/ServiceLifecycleTestKit/GracefulShutdown.swift +++ b/Sources/ServiceLifecycleTestKit/GracefulShutdown.swift @@ -18,6 +18,7 @@ /// /// It is passed to the `operation` closure of the ``testGracefulShutdown(operation:)`` method and allows /// to trigger the graceful shutdown for testing purposes. +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public struct GracefulShutdownTestTrigger: Sendable { private let gracefulShutdownManager: GracefulShutdownManager @@ -35,6 +36,7 @@ public struct GracefulShutdownTestTrigger: Sendable { /// /// Call the code that you want to test inside the `operation` closure and trigger the graceful shutdown by calling ``GracefulShutdownTestTrigger/triggerGracefulShutdown()`` /// on the ``GracefulShutdownTestTrigger`` that is passed to the `operation` closure. +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public func testGracefulShutdown(operation: (GracefulShutdownTestTrigger) async throws -> T) async rethrows -> T { let gracefulShutdownManager = GracefulShutdownManager() return try await TaskLocals.$gracefulShutdownManager.withValue(gracefulShutdownManager) { diff --git a/Sources/UnixSignals/UnixSignalsSequence.swift b/Sources/UnixSignals/UnixSignalsSequence.swift index 49d0e81..4627792 100644 --- a/Sources/UnixSignals/UnixSignalsSequence.swift +++ b/Sources/UnixSignals/UnixSignalsSequence.swift @@ -33,6 +33,7 @@ import ConcurrencyHelpers /// /// - Important: There can only be a single signal handler for a signal installed. So you should avoid creating multiple handlers /// for the same signal. +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public struct UnixSignalsSequence: AsyncSequence, Sendable { private static let queue = DispatchQueue(label: "com.service-lifecycle.unix-signals") @@ -74,6 +75,7 @@ public struct UnixSignalsSequence: AsyncSequence, Sendable { } } +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension UnixSignalsSequence { fileprivate final class Storage: @unchecked Sendable { private let stateMachine: LockedValueBox @@ -169,6 +171,7 @@ extension UnixSignalsSequence { } } +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension UnixSignalsSequence { fileprivate struct StateMachine { private enum State { diff --git a/Tests/ServiceLifecycleTests/GracefulShutdownTests.swift b/Tests/ServiceLifecycleTests/GracefulShutdownTests.swift index a330a92..6569087 100644 --- a/Tests/ServiceLifecycleTests/GracefulShutdownTests.swift +++ b/Tests/ServiceLifecycleTests/GracefulShutdownTests.swift @@ -16,6 +16,7 @@ import ServiceLifecycle import ServiceLifecycleTestKit import XCTest +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) final class GracefulShutdownTests: XCTestCase { func testWithGracefulShutdownHandler() async { var cont: AsyncStream.Continuation! @@ -255,6 +256,7 @@ final class GracefulShutdownTests: XCTestCase { } } + @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) func testWaitForGracefulShutdown() async throws { try await testGracefulShutdown { gracefulShutdownTestTrigger in try await withThrowingTaskGroup(of: Void.self) { group in @@ -274,6 +276,7 @@ final class GracefulShutdownTests: XCTestCase { } } + @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) func testWaitForGracefulShutdown_WhenAlreadyShutdown() async throws { try await testGracefulShutdown { gracefulShutdownTestTrigger in gracefulShutdownTestTrigger.triggerGracefulShutdown() @@ -355,6 +358,7 @@ final class GracefulShutdownTests: XCTestCase { } } + @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) func testCancelWhenGracefulShutdownSurvivesCancellation() async throws { await withTaskGroup(of: Void.self) { group in group.addTask { @@ -375,6 +379,7 @@ final class GracefulShutdownTests: XCTestCase { } } + @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) func testCancelWhenGracefulShutdownSurvivesErrorThrown() async throws { struct MyError: Error, Equatable {}