Skip to content

Commit ca136e8

Browse files
authored
Move Channel abstractions to NIOCore. (#1920)
Motivation: The most important API surface area in NIO are the Channel abstractions. These are shared in all NIO programs, and are also used by several projects to implement their I/O abstraction. There are several moving parts to this abstraction, all of which are moving: - Channel itself - ChannelPipeline - ChannelHandler As these all move, they force several other pieces of API to move with them. Most notably they force us to move NIOAny, which also forces us to move FileHandle and FileRegion. That also forces us to bring over part of our syscall abstraction. This duplication is acceptable due to its minimal surface area, but it is definitely a flaw in our abstraction design that we had to do that at all. We also need to move the channel option abstraction, AddressedEnvelope, and the DeadChannel. Modifications: - Moved a bunch of the Channel abstraction over. - Moved Channel-associated types. Result: Channel will be part of NIOCore.
1 parent 2a65f2b commit ca136e8

29 files changed

+472
-258
lines changed

Sources/NIO/BaseStreamSocketChannel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ class BaseStreamSocketChannel<Socket: SocketProtocol>: BaseSocketChannel<Socket>
247247
return
248248
}
249249

250-
let data = data.forceAsIOData()
250+
let data = self.unwrapData(data, as: IOData.self)
251251

252252
if !self.pendingWrites.add(data: data, promise: promise) {
253253
self.pipeline.syncOperations.fireChannelWritabilityChanged()

Sources/NIO/Bootstrap.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ public final class ServerBootstrap {
362362
ctxEventLoop.assertInEventLoop()
363363
future.flatMap { (_) -> EventLoopFuture<Void> in
364364
ctxEventLoop.assertInEventLoop()
365-
guard !context.pipeline.destroyed else {
365+
guard context.channel.isActive else {
366366
return context.eventLoop.makeFailedFuture(ChannelError.ioOnClosedChannel)
367367
}
368368
context.fireChannelRead(data)

Sources/NIO/Embedded.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ class EmbeddedChannelCore: ChannelCore {
237237
}
238238

239239
deinit {
240-
assert(self.pipeline.destroyed, "leaked an open EmbeddedChannel, maybe forgot to call channel.finish()?")
240+
assert(!self.isOpen && !self.isActive,
241+
"leaked an open EmbeddedChannel, maybe forgot to call channel.finish()?")
241242
isOpen = false
242243
closePromise.succeed(())
243244
}
@@ -631,7 +632,7 @@ public final class EmbeddedChannel: Channel {
631632
}
632633
let elem = buffer.removeFirst()
633634
guard let t = self._channelCore.tryUnwrapData(elem, as: T.self) else {
634-
throw WrongTypeError(expected: T.self, actual: type(of: elem.forceAs(type: Any.self)))
635+
throw WrongTypeError(expected: T.self, actual: type(of: self._channelCore.tryUnwrapData(elem, as: Any.self)!))
635636
}
636637
return t
637638
}

Sources/NIO/FileDescriptor.swift

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the SwiftNIO open source project
44
//
5-
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
5+
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors
66
// Licensed under Apache License v2.0
77
//
88
// See LICENSE.txt for license information
@@ -12,26 +12,6 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
public protocol FileDescriptor {
16-
17-
/// Will be called with the file descriptor if still open, if not it will
18-
/// throw an `IOError`.
19-
///
20-
/// The ownership of the file descriptor must not escape the `body` as it's completely managed by the
21-
/// implementation of the `FileDescriptor` protocol.
22-
///
23-
/// - parameters:
24-
/// - body: The closure to execute if the `FileDescriptor` is still open.
25-
/// - throws: If either the `FileDescriptor` was closed before or the closure throws by itself.
26-
func withUnsafeFileDescriptor<T>(_ body: (CInt) throws -> T) throws -> T
27-
28-
/// `true` if this `FileDescriptor` is open (which means it was not closed yet).
29-
var isOpen: Bool { get }
30-
31-
/// Close this `FileDescriptor`.
32-
func close() throws
33-
}
34-
3515
extension FileDescriptor {
3616
internal static func setNonBlocking(fileDescriptor: CInt) throws {
3717
let flags = try Posix.fcntl(descriptor: fileDescriptor, command: F_GETFL, value: 0)

Sources/NIO/Interfaces.swift renamed to Sources/NIO/NetworkDevices.swift

Lines changed: 1 addition & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the SwiftNIO open source project
44
//
5-
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
5+
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors
66
// Licensed under Apache License v2.0
77
//
88
// See LICENSE.txt for license information
@@ -11,12 +11,6 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14-
//
15-
// Interfaces.swift
16-
// NIO
17-
//
18-
// Created by Cory Benfield on 27/02/2018.
19-
//
2014

2115
#if os(Linux) || os(FreeBSD) || os(Android)
2216
import CNIOLinux
@@ -371,158 +365,3 @@ extension NIONetworkDevice: Hashable {
371365
hasher.combine(self.interfaceIndex)
372366
}
373367
}
374-
375-
/// A representation of a single network interface on a system.
376-
@available(*, deprecated, renamed: "NIONetworkDevice")
377-
public final class NIONetworkInterface {
378-
// This is a class because in almost all cases this will carry
379-
// four structs that are backed by classes, and so will incur 4
380-
// refcount operations each time it is copied.
381-
382-
/// The name of the network interface.
383-
public let name: String
384-
385-
/// The address associated with the given network interface.
386-
public let address: SocketAddress
387-
388-
/// The netmask associated with this address, if any.
389-
public let netmask: SocketAddress?
390-
391-
/// The broadcast address associated with this socket interface, if it has one. Some
392-
/// interfaces do not, especially those that have a `pointToPointDestinationAddress`.
393-
public let broadcastAddress: SocketAddress?
394-
395-
/// The address of the peer on a point-to-point interface, if this is one. Some
396-
/// interfaces do not have such an address: most of those have a `broadcastAddress`
397-
/// instead.
398-
public let pointToPointDestinationAddress: SocketAddress?
399-
400-
/// If the Interface supports Multicast
401-
public let multicastSupported: Bool
402-
403-
/// The index of the interface, as provided by `if_nametoindex`.
404-
public let interfaceIndex: Int
405-
406-
/// Create a brand new network interface.
407-
///
408-
/// This constructor will fail if NIO does not understand the format of the underlying
409-
/// socket address family. This is quite common: for example, Linux will return AF_PACKET
410-
/// addressed interfaces on most platforms, which NIO does not currently understand.
411-
#if os(Windows)
412-
internal init?(_ pAdapter: UnsafeMutablePointer<IP_ADAPTER_ADDRESSES>,
413-
_ pAddress: UnsafeMutablePointer<IP_ADAPTER_UNICAST_ADDRESS>) {
414-
self.name = String(decodingCString: pAdapter.pointee.FriendlyName,
415-
as: UTF16.self)
416-
417-
guard let address = pAddress.pointee.Address.lpSockaddr.convert() else {
418-
return nil
419-
}
420-
self.address = address
421-
422-
// TODO: convert the prefix length to the mask itself
423-
let v4mask: (UINT8) -> SocketAddress? = { _ in
424-
var buffer: [CChar] =
425-
Array<CChar>(repeating: 0, count: Int(INET_ADDRSTRLEN))
426-
var mask: sockaddr_in = sockaddr_in()
427-
mask.sin_family = ADDRESS_FAMILY(AF_INET)
428-
_ = buffer.withUnsafeMutableBufferPointer {
429-
try! NIOBSDSocket.inet_ntop(af: .inet, src: &mask,
430-
dst: $0.baseAddress!,
431-
size: INET_ADDRSTRLEN)
432-
}
433-
return SocketAddress(mask)
434-
}
435-
let v6mask: (UINT8) -> SocketAddress? = { _ in
436-
var buffer: [CChar] =
437-
Array<CChar>(repeating: 0, count: Int(INET6_ADDRSTRLEN))
438-
var mask: sockaddr_in6 = sockaddr_in6()
439-
mask.sin6_family = ADDRESS_FAMILY(AF_INET6)
440-
_ = buffer.withUnsafeMutableBufferPointer {
441-
try! NIOBSDSocket.inet_ntop(af: .inet6, src: &mask,
442-
dst: $0.baseAddress!,
443-
size: INET6_ADDRSTRLEN)
444-
}
445-
return SocketAddress(mask)
446-
}
447-
448-
switch pAddress.pointee.Address.lpSockaddr.pointee.sa_family {
449-
case ADDRESS_FAMILY(AF_INET):
450-
self.netmask = v4mask(pAddress.pointee.OnLinkPrefixLength)
451-
self.interfaceIndex = Int(pAdapter.pointee.IfIndex)
452-
case ADDRESS_FAMILY(AF_INET6):
453-
self.netmask = v6mask(pAddress.pointee.OnLinkPrefixLength)
454-
self.interfaceIndex = Int(pAdapter.pointee.Ipv6IfIndex)
455-
default:
456-
return nil
457-
}
458-
459-
// TODO(compnerd) handle broadcast/ppp/multicast information
460-
self.broadcastAddress = nil
461-
self.pointToPointDestinationAddress = nil
462-
self.multicastSupported = false
463-
}
464-
#else
465-
internal init?(_ caddr: ifaddrs) {
466-
self.name = String(cString: caddr.ifa_name)
467-
468-
guard caddr.ifa_addr != nil else {
469-
return nil
470-
}
471-
472-
guard let address = caddr.ifa_addr!.convert() else {
473-
return nil
474-
}
475-
self.address = address
476-
477-
if let netmask = caddr.ifa_netmask {
478-
self.netmask = netmask.convert()
479-
} else {
480-
self.netmask = nil
481-
}
482-
483-
if (caddr.ifa_flags & UInt32(IFF_BROADCAST)) != 0, let addr = caddr.broadaddr {
484-
self.broadcastAddress = addr.convert()
485-
self.pointToPointDestinationAddress = nil
486-
} else if (caddr.ifa_flags & UInt32(IFF_POINTOPOINT)) != 0, let addr = caddr.dstaddr {
487-
self.broadcastAddress = nil
488-
self.pointToPointDestinationAddress = addr.convert()
489-
} else {
490-
self.broadcastAddress = nil
491-
self.pointToPointDestinationAddress = nil
492-
}
493-
494-
if (caddr.ifa_flags & UInt32(IFF_MULTICAST)) != 0 {
495-
self.multicastSupported = true
496-
} else {
497-
self.multicastSupported = false
498-
}
499-
500-
do {
501-
self.interfaceIndex = Int(try Posix.if_nametoindex(caddr.ifa_name))
502-
} catch {
503-
return nil
504-
}
505-
}
506-
#endif
507-
}
508-
509-
@available(*, deprecated, renamed: "NIONetworkDevice")
510-
extension NIONetworkInterface: CustomDebugStringConvertible {
511-
public var debugDescription: String {
512-
let baseString = "Interface \(self.name): address \(self.address)"
513-
let maskString = self.netmask != nil ? " netmask \(self.netmask!)" : ""
514-
return baseString + maskString
515-
}
516-
}
517-
518-
@available(*, deprecated, renamed: "NIONetworkDevice")
519-
extension NIONetworkInterface: Equatable {
520-
public static func ==(lhs: NIONetworkInterface, rhs: NIONetworkInterface) -> Bool {
521-
return lhs.name == rhs.name &&
522-
lhs.address == rhs.address &&
523-
lhs.netmask == rhs.netmask &&
524-
lhs.broadcastAddress == rhs.broadcastAddress &&
525-
lhs.pointToPointDestinationAddress == rhs.pointToPointDestinationAddress &&
526-
lhs.interfaceIndex == rhs.interfaceIndex
527-
}
528-
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftNIO open source project
4+
//
5+
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import NIOConcurrencyHelpers
16+
17+
/// A `SelectableChannel` is a `Channel` that can be used with a `Selector` which notifies a user when certain events
18+
/// are possible. On UNIX a `Selector` is usually an abstraction of `select`, `poll`, `epoll` or `kqueue`.
19+
///
20+
/// - warning: `SelectableChannel` methods and properties are _not_ thread-safe (unless they also belong to `Channel`).
21+
internal protocol SelectableChannel: Channel {
22+
/// The type of the `Selectable`. A `Selectable` is usually wrapping a file descriptor that can be registered in a
23+
/// `Selector`.
24+
associatedtype SelectableType: Selectable
25+
26+
var isOpen: Bool { get }
27+
28+
/// The event(s) of interest.
29+
var interestedEvent: SelectorEventSet { get }
30+
31+
/// Called when the `SelectableChannel` is ready to be written.
32+
func writable()
33+
34+
/// Called when the `SelectableChannel` is ready to be read.
35+
func readable()
36+
37+
/// Called when the read side of the `SelectableChannel` hit EOF.
38+
func readEOF()
39+
40+
/// Called when the write side of the `SelectableChannel` hit EOF.
41+
func writeEOF()
42+
43+
/// Called when the `SelectableChannel` was reset (ie. is now unusable)
44+
func reset()
45+
46+
func register(selector: Selector<NIORegistration>, interested: SelectorEventSet) throws
47+
48+
func deregister(selector: Selector<NIORegistration>, mode: CloseMode) throws
49+
50+
func reregister(selector: Selector<NIORegistration>, interested: SelectorEventSet) throws
51+
}
52+
53+
/// Multicast is not supported on this interface.
54+
public struct NIOMulticastNotSupportedError: Error {
55+
public var device: NIONetworkDevice
56+
57+
public init(device: NIONetworkDevice) {
58+
self.device = device
59+
}
60+
}
61+
62+
/// Multicast has not been properly implemented on this channel.
63+
public struct NIOMulticastNotImplementedError: Error {
64+
public init() {}
65+
}

Sources/NIO/SocketChannel.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ final class ServerSocketChannel: BaseSocketChannel<ServerSocket> {
296296
override public func channelRead0(_ data: NIOAny) {
297297
self.eventLoop.assertInEventLoop()
298298

299-
let ch = data.forceAsOther() as SocketChannel
299+
let ch = self.unwrapData(data, as: SocketChannel.self)
300300
ch.eventLoop.execute {
301301
ch.register().flatMapThrowing {
302302
guard ch.isOpen else {
@@ -669,7 +669,7 @@ final class DatagramChannel: BaseSocketChannel<Socket> {
669669
}
670670
/// Buffer a write in preparation for a flush.
671671
override func bufferPendingWrite(data: NIOAny, promise: EventLoopPromise<Void>?) {
672-
let data = data.forceAsByteEnvelope()
672+
let data = self.unwrapData(data, as: AddressedEnvelope<ByteBuffer>.self)
673673

674674
if !self.pendingWrites.add(envelope: data, promise: promise) {
675675
assert(self.isActive)

Sources/NIO/Utilities.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public enum System {
123123
}
124124

125125
while let concreteInterface = interface {
126-
if let nioInterface = NIONetworkInterface(concreteInterface.pointee) {
126+
if let nioInterface = NIONetworkInterface._construct(from: concreteInterface.pointee) {
127127
interfaces.append(nioInterface)
128128
}
129129
interface = concreteInterface.pointee.ifa_next
File renamed without changes.

0 commit comments

Comments
 (0)