Adds Swift Concurrency Availability to macOS 10.15, iOS 13, tvOS 13, and watchOS 6 (#2004)

* Change Swift Concurrency Availability

* Label GitHub Action Jobs

* Incorporate the insights from Johannes Weiss

* Remove GitHub Actions

* Incorporate the feedback of Cory Benfield

Co-authored-by: Cory Benfield <lukasa@apple.com>
This commit is contained in:
Paul Schmiedmayer 2021-12-03 18:35:01 +01:00 committed by GitHub
parent 0697d5a599
commit fa3b2a2e66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 244 additions and 29 deletions

View File

@ -15,8 +15,8 @@
import NIOCore
import NIOHTTP1
#if compiler(>=5.5) && canImport(_Concurrency)
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
#if canImport(_Concurrency) && compiler(>=5.5.2)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
struct AsyncChannelIO<Request, Response> {
let channel: Channel

View File

@ -16,9 +16,9 @@ import NIOPosix
import NIOHTTP1
import Dispatch
#if compiler(>=5.5) && canImport(_Concurrency)
#if canImport(_Concurrency) && compiler(>=5.5.2)
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
func makeHTTPChannel(host: String, port: Int, group: EventLoopGroup) async throws -> AsyncChannelIO<HTTPRequestHead, NIOHTTPClientResponseFull> {
let channel = try await ClientBootstrap(group: group).connect(host: host, port: port).get()
try await channel.pipeline.addHTTPClientHandlers().get()
@ -27,7 +27,7 @@ func makeHTTPChannel(host: String, port: Int, group: EventLoopGroup) async throw
return try await AsyncChannelIO<HTTPRequestHead, NIOHTTPClientResponseFull>(channel).start()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
func main() async {
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
do {
@ -67,7 +67,7 @@ func main() async {
let dg = DispatchGroup()
dg.enter()
if #available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) {
if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) {
Task {
await main()
dg.leave()
@ -77,5 +77,5 @@ if #available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) {
}
dg.wait()
#else
print("ERROR: Concurrency only supported on Swift >= 5.5.")
print("ERROR: The NIO Async Await Demo supports Swift >= 5.5.2.")
#endif

View File

@ -0,0 +1,215 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2021 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#if compiler(>=5.5) && !compiler(>=5.5.2)
extension EventLoopFuture {
/// Get the value/error from an `EventLoopFuture` in an `async` context.
///
/// This function can be used to bridge an `EventLoopFuture` into the `async` world. Ie. if you're in an `async`
/// function and want to get the result of this future.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@inlinable
public func get() async throws -> Value {
return try await withUnsafeThrowingContinuation { cont in
self.whenComplete { result in
switch result {
case .success(let value):
cont.resume(returning: value)
case .failure(let error):
cont.resume(throwing: error)
}
}
}
}
}
extension EventLoopGroup {
/// Shuts down the event loop gracefully.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@inlinable
public func shutdownGracefully() async throws {
return try await withCheckedThrowingContinuation { cont in
self.shutdownGracefully { error in
if let error = error {
cont.resume(throwing: error)
} else {
cont.resume()
}
}
}
}
}
extension EventLoopPromise {
/// Complete a future with the result (or error) of the `async` function `body`.
///
/// This function can be used to bridge the `async` world into an `EventLoopPromise`.
///
/// - parameters:
/// - body: The `async` function to run.
/// - returns: A `Task` which was created to `await` the `body`.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@discardableResult
@inlinable
public func completeWithTask(_ body: @escaping @Sendable () async throws -> Value) -> Task<Void, Never> {
Task {
do {
let value = try await body()
self.succeed(value)
} catch {
self.fail(error)
}
}
}
}
extension Channel {
/// Shortcut for calling `write` and `flush`.
///
/// - parameters:
/// - data: the data to write
/// - promise: the `EventLoopPromise` that will be notified once the `write` operation completes,
/// or `nil` if not interested in the outcome of the operation.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@inlinable
public func writeAndFlush<T>(_ any: T) async throws {
try await self.writeAndFlush(any).get()
}
/// Set `option` to `value` on this `Channel`.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@inlinable
public func setOption<Option: ChannelOption>(_ option: Option, value: Option.Value) async throws {
try await self.setOption(option, value: value).get()
}
/// Get the value of `option` for this `Channel`.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@inlinable
public func getOption<Option: ChannelOption>(_ option: Option) async throws -> Option.Value {
return try await self.getOption(option).get()
}
}
extension ChannelOutboundInvoker {
/// Register on an `EventLoop` and so have all its IO handled.
///
/// - returns: the future which will be notified once the operation completes.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func register(file: StaticString = #file, line: UInt = #line) async throws {
try await self.register(file: file, line: line).get()
}
/// Bind to a `SocketAddress`.
/// - parameters:
/// - to: the `SocketAddress` to which we should bind the `Channel`.
/// - returns: the future which will be notified once the operation completes.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func bind(to address: SocketAddress, file: StaticString = #file, line: UInt = #line) async throws {
try await self.bind(to: address, file: file, line: line).get()
}
/// Connect to a `SocketAddress`.
/// - parameters:
/// - to: the `SocketAddress` to which we should connect the `Channel`.
/// - returns: the future which will be notified once the operation completes.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func connect(to address: SocketAddress, file: StaticString = #file, line: UInt = #line) async throws {
try await self.connect(to: address, file: file, line: line).get()
}
/// Shortcut for calling `write` and `flush`.
///
/// - parameters:
/// - data: the data to write
/// - returns: the future which will be notified once the `write` operation completes.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func writeAndFlush(_ data: NIOAny, file: StaticString = #file, line: UInt = #line) async throws {
try await self.writeAndFlush(data, file: file, line: line).get()
}
/// Close the `Channel` and so the connection if one exists.
///
/// - parameters:
/// - mode: the `CloseMode` that is used
/// - returns: the future which will be notified once the operation completes.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func close(mode: CloseMode = .all, file: StaticString = #file, line: UInt = #line) async throws {
try await self.close(mode: mode, file: file, line: line).get()
}
/// Trigger a custom user outbound event which will flow through the `ChannelPipeline`.
///
/// - parameters:
/// - event: the event itself.
/// - returns: the future which will be notified once the operation completes.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func triggerUserOutboundEvent(_ event: Any, file: StaticString = #file, line: UInt = #line) async throws {
try await self.triggerUserOutboundEvent(event, file: file, line: line).get()
}
}
extension ChannelPipeline {
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func addHandler(_ handler: ChannelHandler,
name: String? = nil,
position: ChannelPipeline.Position = .last) async throws {
try await self.addHandler(handler, name: name, position: position).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func removeHandler(_ handler: RemovableChannelHandler) async throws {
try await self.removeHandler(handler).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func removeHandler(name: String) async throws {
try await self.removeHandler(name: name).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func removeHandler(context: ChannelHandlerContext) async throws {
try await self.removeHandler(context: context).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func context(handler: ChannelHandler) async throws -> ChannelHandlerContext {
return try await self.context(handler: handler).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func context(name: String) async throws -> ChannelHandlerContext {
return try await self.context(name: name).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@inlinable
public func context<Handler: ChannelHandler>(handlerType: Handler.Type) async throws -> ChannelHandlerContext {
return try await self.context(handlerType: handlerType).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func addHandlers(_ handlers: [ChannelHandler],
position: ChannelPipeline.Position = .last) async throws {
try await self.addHandlers(handlers, position: position).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public func addHandlers(_ handlers: ChannelHandler...,
position: ChannelPipeline.Position = .last) async throws {
try await self.addHandlers(handlers, position: position)
}
}
#endif

View File

@ -18,14 +18,14 @@ public typealias NIOSendable = Swift.Sendable
public typealias NIOSendable = Any
#endif
#if compiler(>=5.5) && canImport(_Concurrency)
#if compiler(>=5.5.2) && canImport(_Concurrency)
extension EventLoopFuture {
/// Get the value/error from an `EventLoopFuture` in an `async` context.
///
/// This function can be used to bridge an `EventLoopFuture` into the `async` world. Ie. if you're in an `async`
/// function and want to get the result of this future.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@inlinable
public func get() async throws -> Value {
return try await withUnsafeThrowingContinuation { cont in
@ -43,7 +43,7 @@ extension EventLoopFuture {
extension EventLoopGroup {
/// Shuts down the event loop gracefully.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@inlinable
public func shutdownGracefully() async throws {
return try await withCheckedThrowingContinuation { cont in
@ -66,7 +66,7 @@ extension EventLoopPromise {
/// - parameters:
/// - body: The `async` function to run.
/// - returns: A `Task` which was created to `await` the `body`.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@discardableResult
@inlinable
public func completeWithTask(_ body: @escaping @Sendable () async throws -> Value) -> Task<Void, Never> {
@ -88,21 +88,21 @@ extension Channel {
/// - data: the data to write
/// - promise: the `EventLoopPromise` that will be notified once the `write` operation completes,
/// or `nil` if not interested in the outcome of the operation.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@inlinable
public func writeAndFlush<T>(_ any: T) async throws {
try await self.writeAndFlush(any).get()
}
/// Set `option` to `value` on this `Channel`.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@inlinable
public func setOption<Option: ChannelOption>(_ option: Option, value: Option.Value) async throws {
try await self.setOption(option, value: value).get()
}
/// Get the value of `option` for this `Channel`.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@inlinable
public func getOption<Option: ChannelOption>(_ option: Option) async throws -> Option.Value {
return try await self.getOption(option).get()
@ -113,7 +113,7 @@ extension ChannelOutboundInvoker {
/// Register on an `EventLoop` and so have all its IO handled.
///
/// - returns: the future which will be notified once the operation completes.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func register(file: StaticString = #file, line: UInt = #line) async throws {
try await self.register(file: file, line: line).get()
}
@ -122,7 +122,7 @@ extension ChannelOutboundInvoker {
/// - parameters:
/// - to: the `SocketAddress` to which we should bind the `Channel`.
/// - returns: the future which will be notified once the operation completes.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func bind(to address: SocketAddress, file: StaticString = #file, line: UInt = #line) async throws {
try await self.bind(to: address, file: file, line: line).get()
}
@ -131,7 +131,7 @@ extension ChannelOutboundInvoker {
/// - parameters:
/// - to: the `SocketAddress` to which we should connect the `Channel`.
/// - returns: the future which will be notified once the operation completes.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func connect(to address: SocketAddress, file: StaticString = #file, line: UInt = #line) async throws {
try await self.connect(to: address, file: file, line: line).get()
}
@ -141,7 +141,7 @@ extension ChannelOutboundInvoker {
/// - parameters:
/// - data: the data to write
/// - returns: the future which will be notified once the `write` operation completes.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func writeAndFlush(_ data: NIOAny, file: StaticString = #file, line: UInt = #line) async throws {
try await self.writeAndFlush(data, file: file, line: line).get()
}
@ -151,7 +151,7 @@ extension ChannelOutboundInvoker {
/// - parameters:
/// - mode: the `CloseMode` that is used
/// - returns: the future which will be notified once the operation completes.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func close(mode: CloseMode = .all, file: StaticString = #file, line: UInt = #line) async throws {
try await self.close(mode: mode, file: file, line: line).get()
}
@ -161,58 +161,58 @@ extension ChannelOutboundInvoker {
/// - parameters:
/// - event: the event itself.
/// - returns: the future which will be notified once the operation completes.
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func triggerUserOutboundEvent(_ event: Any, file: StaticString = #file, line: UInt = #line) async throws {
try await self.triggerUserOutboundEvent(event, file: file, line: line).get()
}
}
extension ChannelPipeline {
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func addHandler(_ handler: ChannelHandler,
name: String? = nil,
position: ChannelPipeline.Position = .last) async throws {
try await self.addHandler(handler, name: name, position: position).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func removeHandler(_ handler: RemovableChannelHandler) async throws {
try await self.removeHandler(handler).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func removeHandler(name: String) async throws {
try await self.removeHandler(name: name).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func removeHandler(context: ChannelHandlerContext) async throws {
try await self.removeHandler(context: context).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func context(handler: ChannelHandler) async throws -> ChannelHandlerContext {
return try await self.context(handler: handler).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func context(name: String) async throws -> ChannelHandlerContext {
return try await self.context(name: name).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@inlinable
public func context<Handler: ChannelHandler>(handlerType: Handler.Type) async throws -> ChannelHandlerContext {
return try await self.context(handlerType: handlerType).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func addHandlers(_ handlers: [ChannelHandler],
position: ChannelPipeline.Position = .last) async throws {
try await self.addHandlers(handlers, position: position).get()
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func addHandlers(_ handlers: ChannelHandler...,
position: ChannelPipeline.Position = .last) async throws {
try await self.addHandlers(handlers, position: position)