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:
parent
0697d5a599
commit
fa3b2a2e66
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue