swift-nio-transport-services/Sources/NIOTransportServices/NIOTSEventLoopGroup.swift

128 lines
5.3 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2022 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 canImport(Network)
import Foundation
import NIOCore
import NIOConcurrencyHelpers
import Dispatch
import Network
import Atomics
/// An `EventLoopGroup` containing `EventLoop`s specifically designed for use with
/// Network.framework's post-sockets networking API.
///
/// These `EventLoop`s provide highly optimised and powerful networking tools for
/// the Darwin platforms. They have a number of advantages over the regular
/// `SelectableEventLoop` that NIO uses on other platforms. In particular:
///
/// - The use of `DispatchQueue`s to schedule tasks allows the Darwin kernels to make
/// intelligent scheduling decisions, as well as to maintain QoS and ensure that
/// tasks required to handle networking in your application are given appropriate
/// priority by the system.
/// - Network.framework provides powerful tools for observing network state and managing
/// connections on devices with highly fluid networking environments, such as laptops
/// and mobile devices. These tools can be exposed to `Channel`s using this backend.
/// - Network.framework brings the networking stack into userspace, reducing the overhead
/// of most network operations by removing syscalls, and greatly increasing the safety
/// and security of the network stack.
/// - The applications networking needs are more effectively communicated to the kernel,
/// allowing mobile devices to change radio configuration and behaviour as needed to
/// take advantage of the various interfaces available on mobile devices.
///
/// In general, when building applications whose primary purpose is to be deployed on Darwin
/// platforms, the ``NIOTSEventLoopGroup`` should be preferred over the
/// `MultiThreadedEventLoopGroup`. In particular, on iOS, the ``NIOTSEventLoopGroup`` is the
/// preferred networking backend.
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public final class NIOTSEventLoopGroup: EventLoopGroup {
private let index = ManagedAtomic(0)
private let eventLoops: [NIOTSEventLoop]
/// Construct a ``NIOTSEventLoopGroup`` with a specified number of loops and QoS.
///
/// - parameters:
/// - loopCount: The number of independent loops to use. Defaults to `1`.
/// - defaultQoS: The default QoS for tasks enqueued on this loop. Defaults to `.default`.
public init(loopCount: Int = 1, defaultQoS: DispatchQoS = .default) {
precondition(loopCount > 0)
self.eventLoops = (0..<loopCount).map { _ in NIOTSEventLoop(qos: defaultQoS) }
}
public func next() -> EventLoop {
return self.eventLoops[abs(index.loadThenWrappingIncrement(ordering: .relaxed) % self.eventLoops.count)]
}
/// Shuts down all of the event loops, rendering them unable to perform further work.
public func shutdownGracefully(queue: DispatchQueue, _ callback: @escaping (Error?) -> Void) {
let g = DispatchGroup()
let q = DispatchQueue(label: "nio.transportservices.shutdowngracefullyqueue", target: queue)
var error: Error? = nil
for loop in self.eventLoops {
g.enter()
loop.closeGently().recover { err in
q.sync { error = err }
}.whenComplete { (_: Result<Void, Error>) in
g.leave()
}
}
g.notify(queue: q) {
callback(error)
}
}
public func makeIterator() -> EventLoopIterator {
return EventLoopIterator(self.eventLoops)
}
}
/// A TLS provider to bootstrap TLS-enabled connections with `NIOClientTCPBootstrap`.
///
/// Example:
///
/// // Creating the "universal bootstrap" with the `NIOTSClientTLSProvider`.
/// let tlsProvider = NIOTSClientTLSProvider()
/// let bootstrap = NIOClientTCPBootstrap(NIOTSConnectionBootstrap(group: group), tls: tlsProvider)
///
/// // Bootstrapping a connection using the "universal bootstrapping mechanism"
/// let connection = bootstrap.enableTLS()
/// .connect(host: "example.com", port: 443)
/// .wait()
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public struct NIOTSClientTLSProvider: NIOClientTLSProvider {
public typealias Bootstrap = NIOTSConnectionBootstrap
let tlsOptions: NWProtocolTLS.Options
/// Construct the TLS provider.
public init(tlsOptions: NWProtocolTLS.Options = NWProtocolTLS.Options()) {
self.tlsOptions = tlsOptions
}
/// Enable TLS on the bootstrap. This is not a function you will typically call as a user, it is called by
/// `NIOClientTCPBootstrap`.
public func enableTLS(_ bootstrap: NIOTSConnectionBootstrap) -> NIOTSConnectionBootstrap {
return bootstrap.tlsOptions(self.tlsOptions)
}
}
#endif
#if swift(>=5.5) && canImport(_Concurrency) && canImport(Network)
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension NIOTSEventLoopGroup: @unchecked Sendable {}
#endif