NIO: split out `FileDescriptor` and `Selectable` (#1444)

A `FileDescriptor` is not necessarily selectable (e.g. Windows), and a
selectable is not necessarily a `FileDescriptor` (e.g. Windows).

This will allow a basic `Selector` on Windows which will use `WSASelect`
to select on sockets for some basic functionality even though
IOCompletionPorts are preferred.
This commit is contained in:
Saleem Abdulrasool 2020-03-18 08:48:40 -07:00 committed by GitHub
parent 2673be6ae3
commit 834a5f2185
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 121 additions and 80 deletions

View File

@ -206,7 +206,7 @@ extension sockaddr_storage {
/// Base class for sockets.
///
/// This should not be created directly but one of its sub-classes should be used, like `ServerSocket` or `Socket`.
class BaseSocket: Selectable, BaseSocketProtocol {
class BaseSocket: BaseSocketProtocol {
typealias SelectableType = BaseSocket
private var descriptor: CInt
@ -214,13 +214,6 @@ class BaseSocket: Selectable, BaseSocketProtocol {
return descriptor >= 0
}
func withUnsafeFileDescriptor<T>(_ body: (CInt) throws -> T) throws -> T {
guard self.isOpen else {
throw IOError(errnoCode: EBADF, reason: "file descriptor already closed!")
}
return try body(descriptor)
}
/// Returns the local bound `SocketAddress` of the socket.
///
/// - returns: The local bound address.
@ -244,8 +237,8 @@ class BaseSocket: Selectable, BaseSocketProtocol {
try addr.withMutableSockAddr { addressPtr, size in
var size = socklen_t(size)
try withUnsafeFileDescriptor { fd in
try body(fd, addressPtr, &size)
try self.withUnsafeHandle {
try body($0, addressPtr, &size)
}
}
return addr.convert()
@ -272,7 +265,7 @@ class BaseSocket: Selectable, BaseSocketProtocol {
#if !os(Linux)
if setNonBlocking {
do {
try BaseSocket.setNonBlocking(fileDescriptor: sock)
try Posix.setNonBlocking(socket: sock)
} catch {
// best effort close
try? Posix.close(descriptor: sock)
@ -325,8 +318,8 @@ class BaseSocket: Selectable, BaseSocketProtocol {
///
/// throws: An `IOError` if the operation failed.
final func setNonBlocking() throws {
return try withUnsafeFileDescriptor { fd in
try BaseSocket.setNonBlocking(fileDescriptor: fd)
return try self.withUnsafeHandle {
try Posix.setNonBlocking(socket: $0)
}
}
@ -345,11 +338,11 @@ class BaseSocket: Selectable, BaseSocketProtocol {
// most socket options settings so for the time being we'll just ignore this. Let's revisit for NIO 2.0.
return
}
return try withUnsafeFileDescriptor { fd in
return try self.withUnsafeHandle {
var val = value
try Posix.setsockopt(
socket: fd,
socket: $0,
level: level,
optionName: name,
optionValue: &val,
@ -366,7 +359,7 @@ class BaseSocket: Selectable, BaseSocketProtocol {
/// - name: The name of the option to set.
/// - throws: An `IOError` if the operation failed.
final func getOption<T>(level: Int32, name: Int32) throws -> T {
return try withUnsafeFileDescriptor { fd in
return try self.withUnsafeHandle { fd in
var length = socklen_t(MemoryLayout<T>.size)
let storage = UnsafeMutableRawBufferPointer.allocate(byteCount: MemoryLayout<T>.stride,
alignment: MemoryLayout<T>.alignment)
@ -390,7 +383,7 @@ class BaseSocket: Selectable, BaseSocketProtocol {
/// - address: The `SocketAddress` to which the socket should be bound.
/// - throws: An `IOError` if the operation failed.
final func bind(to address: SocketAddress) throws {
try withUnsafeFileDescriptor { fd in
try self.withUnsafeHandle { fd in
func doBind(ptr: UnsafePointer<sockaddr>, bytes: Int) throws {
try Posix.bind(descriptor: fd, ptr: ptr, bytes: bytes)
}
@ -425,13 +418,22 @@ class BaseSocket: Selectable, BaseSocketProtocol {
///
/// - throws: An `IOError` if the operation failed.
final func takeDescriptorOwnership() throws -> CInt {
return try withUnsafeFileDescriptor { fd in
return try self.withUnsafeHandle {
self.descriptor = -1
return fd
return $0
}
}
}
extension BaseSocket: Selectable {
func withUnsafeHandle<T>(_ body: (CInt) throws -> T) throws -> T {
guard self.isOpen else {
throw IOError(errnoCode: EBADF, reason: "file descriptor already closed!")
}
return try body(self.descriptor)
}
}
extension BaseSocket: CustomStringConvertible {
var description: String {
return "BaseSocket { fd=\(self.descriptor) }"

View File

@ -350,13 +350,6 @@ public enum ChannelError: Error {
extension ChannelError: Equatable { }
/// `NIOFailedToSetSocketNonBlockingError` indicates that NIO was unable to set a socket to non-blocking mode, either
/// when connecting a socket as a client or when accepting a socket as a server.
///
/// This error should never happen because a socket should always be able to be set to non-blocking mode. Unfortunately,
/// we have seen this happen on Darwin.
public struct NIOFailedToSetSocketNonBlockingError: Error {}
/// The removal of a `ChannelHandler` using `ChannelPipeline.removeHandler` has been attempted more than once.
public struct NIOAttemptedToRemoveHandlerMultipleTimesError: Error {}

View File

@ -12,18 +12,37 @@
//
//===----------------------------------------------------------------------===//
extension NIOFileHandle: Selectable {
struct SelectableFileHandle {
var handle: NIOFileHandle
var isOpen: Bool {
return handle.isOpen
}
init(_ handle: NIOFileHandle) {
self.handle = handle
}
func close() throws {
try handle.close()
}
}
extension SelectableFileHandle: Selectable {
func withUnsafeHandle<T>(_ body: (CInt) throws -> T) throws -> T {
return try self.handle.withUnsafeFileDescriptor(body)
}
}
final class PipePair: SocketProtocol {
typealias SelectableType = NIOFileHandle
typealias SelectableType = SelectableFileHandle
let inputFD: NIOFileHandle
let outputFD: NIOFileHandle
let inputFD: SelectableFileHandle
let outputFD: SelectableFileHandle
init(inputFD: NIOFileHandle, outputFD: NIOFileHandle) throws {
self.inputFD = inputFD
self.outputFD = outputFD
self.inputFD = SelectableFileHandle(inputFD)
self.outputFD = SelectableFileHandle(outputFD)
try self.ignoreSIGPIPE()
for fileHandle in [inputFD, outputFD] {
try fileHandle.withUnsafeFileDescriptor {
@ -34,7 +53,7 @@ final class PipePair: SocketProtocol {
func ignoreSIGPIPE() throws {
for fileHandle in [self.inputFD, self.outputFD] {
try fileHandle.withUnsafeFileDescriptor {
try fileHandle.withUnsafeHandle {
try PipePair.ignoreSIGPIPE(descriptor: $0)
}
}
@ -53,14 +72,14 @@ final class PipePair: SocketProtocol {
}
func write(pointer: UnsafeRawBufferPointer) throws -> IOResult<Int> {
return try self.outputFD.withUnsafeFileDescriptor { fd in
try Posix.write(descriptor: fd, pointer: pointer.baseAddress!, size: pointer.count)
return try self.outputFD.withUnsafeHandle {
try Posix.write(descriptor: $0, pointer: pointer.baseAddress!, size: pointer.count)
}
}
func writev(iovecs: UnsafeBufferPointer<IOVector>) throws -> IOResult<Int> {
return try self.outputFD.withUnsafeFileDescriptor { fd in
try Posix.writev(descriptor: fd, iovecs: iovecs)
return try self.outputFD.withUnsafeHandle {
try Posix.writev(descriptor: $0, iovecs: iovecs)
}
}
@ -69,8 +88,8 @@ final class PipePair: SocketProtocol {
}
func read(pointer: UnsafeMutableRawBufferPointer) throws -> IOResult<Int> {
return try self.inputFD.withUnsafeFileDescriptor { fd in
try Posix.read(descriptor: fd, pointer: pointer.baseAddress!, size: pointer.count)
return try self.inputFD.withUnsafeHandle {
try Posix.read(descriptor: $0, pointer: pointer.baseAddress!, size: pointer.count)
}
}

View File

@ -12,8 +12,12 @@
//
//===----------------------------------------------------------------------===//
/// Represents a selectable resource which can be registered to a `Selector` to be notified once there are some events ready for it.
/// Represents a selectable resource which can be registered to a `Selector` to
/// be notified once there are some events ready for it.
///
/// - warning: `Selectable`s are not thread-safe, only to be used on the appropriate `EventLoop`.
protocol Selectable: FileDescriptor {
/// - warning:
/// `Selectable`s are not thread-safe, only to be used on the appropriate
/// `EventLoop`.
protocol Selectable {
func withUnsafeHandle<T>(_: (CInt) throws -> T) throws -> T
}

View File

@ -410,9 +410,9 @@ internal class Selector<R: Registration> {
assert(interested.contains(.reset))
assert(oldInterested?.contains(.reset) ?? true)
try selectable.withUnsafeFileDescriptor { fd in
try selectable.withUnsafeHandle {
try newKQueueFilters.calculateKQueueFilterSetChanges(previousKQueueFilterSet: oldKQueueFilters,
fileDescriptor: fd,
fileDescriptor: $0,
kqueueApplyEventChangeSet)
}
}
@ -431,7 +431,7 @@ internal class Selector<R: Registration> {
throw IOError(errnoCode: EBADF, reason: "can't register on selector as it's \(self.lifecycleState).")
}
try selectable.withUnsafeFileDescriptor { fd in
try selectable.withUnsafeHandle { fd in
assert(registrations[Int(fd)] == nil)
#if os(Linux)
var ev = Epoll.epoll_event()
@ -457,7 +457,7 @@ internal class Selector<R: Registration> {
throw IOError(errnoCode: EBADF, reason: "can't re-register on selector as it's \(self.lifecycleState).")
}
assert(interested.contains(.reset), "must register for at least .reset but tried registering for \(interested)")
try selectable.withUnsafeFileDescriptor { fd in
try selectable.withUnsafeHandle { fd in
var reg = registrations[Int(fd)]!
#if os(Linux)
@ -487,7 +487,7 @@ internal class Selector<R: Registration> {
}
// temporary workaround to stop us delivering outdated events
self.deregistrationsHappened = true
try selectable.withUnsafeFileDescriptor { fd in
try selectable.withUnsafeHandle { fd in
guard let reg = registrations.removeValue(forKey: Int(fd)) else {
return
}

View File

@ -53,8 +53,8 @@
/// - backlog: The backlog to use.
/// - throws: An `IOError` if creation of the socket failed.
func listen(backlog: Int32 = 128) throws {
try withUnsafeFileDescriptor { fd in
_ = try Posix.listen(descriptor: fd, backlog: backlog)
try withUnsafeHandle {
_ = try Posix.listen(descriptor: $0, backlog: backlog)
}
}
@ -65,7 +65,7 @@
/// - returns: A `Socket` once a new connection was established or `nil` if this `ServerSocket` is in non-blocking mode and there is no new connection that can be accepted when this method is called.
/// - throws: An `IOError` if the operation failed.
func accept(setNonBlocking: Bool = false) throws -> Socket? {
return try withUnsafeFileDescriptor { fd in
return try withUnsafeHandle { fd in
#if os(Linux)
let flags: Int32
if setNonBlocking {

View File

@ -80,7 +80,7 @@ typealias IOVector = iovec
/// Private helper function to handle connection attempts.
private func connectSocket<T>(addr: T) throws -> Bool {
return try withUnsafeFileDescriptor { fd in
return try withUnsafeHandle { fd in
var addr = addr
return try withUnsafePointer(to: &addr) { ptr in
try ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) { ptr in
@ -107,8 +107,8 @@ typealias IOVector = iovec
/// - returns: The `IOResult` which indicates how much data could be written and if the operation returned before all could be written (because the socket is in non-blocking mode).
/// - throws: An `IOError` if the operation failed.
func write(pointer: UnsafeRawBufferPointer) throws -> IOResult<Int> {
return try withUnsafeFileDescriptor { fd in
try Posix.write(descriptor: fd, pointer: pointer.baseAddress!, size: pointer.count)
return try withUnsafeHandle {
try Posix.write(descriptor: $0, pointer: pointer.baseAddress!, size: pointer.count)
}
}
@ -119,8 +119,8 @@ typealias IOVector = iovec
/// - returns: The `IOResult` which indicates how much data could be written and if the operation returned before all could be written (because the socket is in non-blocking mode).
/// - throws: An `IOError` if the operation failed.
func writev(iovecs: UnsafeBufferPointer<IOVector>) throws -> IOResult<Int> {
return try withUnsafeFileDescriptor { fd in
try Posix.writev(descriptor: fd, iovecs: iovecs)
return try withUnsafeHandle {
try Posix.writev(descriptor: $0, iovecs: iovecs)
}
}
@ -132,8 +132,8 @@ typealias IOVector = iovec
/// - returns: The `IOResult` which indicates how much data could be written and if the operation returned before all could be written (because the socket is in non-blocking mode).
/// - throws: An `IOError` if the operation failed.
func sendto(pointer: UnsafeRawBufferPointer, destinationPtr: UnsafePointer<sockaddr>, destinationSize: socklen_t) throws -> IOResult<Int> {
return try withUnsafeFileDescriptor { fd in
try Posix.sendto(descriptor: fd, pointer: UnsafeMutableRawPointer(mutating: pointer.baseAddress!),
return try withUnsafeHandle {
try Posix.sendto(descriptor: $0, pointer: UnsafeMutableRawPointer(mutating: pointer.baseAddress!),
size: pointer.count, destinationPtr: destinationPtr,
destinationSize: destinationSize)
}
@ -146,8 +146,8 @@ typealias IOVector = iovec
/// - returns: The `IOResult` which indicates how much data could be read and if the operation returned before all could be read (because the socket is in non-blocking mode).
/// - throws: An `IOError` if the operation failed.
func read(pointer: UnsafeMutableRawBufferPointer) throws -> IOResult<Int> {
return try withUnsafeFileDescriptor { fd in
try Posix.read(descriptor: fd, pointer: pointer.baseAddress!, size: pointer.count)
return try withUnsafeHandle {
try Posix.read(descriptor: $0, pointer: pointer.baseAddress!, size: pointer.count)
}
}
@ -160,7 +160,7 @@ typealias IOVector = iovec
/// - returns: The `IOResult` which indicates how much data could be received and if the operation returned before all could be received (because the socket is in non-blocking mode).
/// - throws: An `IOError` if the operation failed.
func recvfrom(pointer: UnsafeMutableRawBufferPointer, storage: inout sockaddr_storage, storageLen: inout socklen_t) throws -> IOResult<(Int)> {
return try withUnsafeFileDescriptor { fd in
return try withUnsafeHandle { fd in
try storage.withMutableSockAddr { (storagePtr, _) in
try Posix.recvfrom(descriptor: fd, pointer: pointer.baseAddress!,
len: pointer.count,
@ -179,8 +179,8 @@ typealias IOVector = iovec
/// - returns: The `IOResult` which indicates how much data could be send and if the operation returned before all could be send (because the socket is in non-blocking mode).
/// - throws: An `IOError` if the operation failed.
func sendFile(fd: Int32, offset: Int, count: Int) throws -> IOResult<Int> {
return try withUnsafeFileDescriptor { desc in
try Posix.sendfile(descriptor: desc, fd: fd, offset: off_t(offset), count: count)
return try withUnsafeHandle {
try Posix.sendfile(descriptor: $0, fd: fd, offset: off_t(offset), count: count)
}
}
@ -191,8 +191,8 @@ typealias IOVector = iovec
/// - returns: The `IOResult` which indicates how many messages could be received and if the operation returned before all messages could be received (because the socket is in non-blocking mode).
/// - throws: An `IOError` if the operation failed.
func recvmmsg(msgs: UnsafeMutableBufferPointer<MMsgHdr>) throws -> IOResult<Int> {
return try withUnsafeFileDescriptor { fd in
try Posix.recvmmsg(sockfd: fd, msgvec: msgs.baseAddress!, vlen: CUnsignedInt(msgs.count), flags: 0, timeout: nil)
return try withUnsafeHandle {
try Posix.recvmmsg(sockfd: $0, msgvec: msgs.baseAddress!, vlen: CUnsignedInt(msgs.count), flags: 0, timeout: nil)
}
}
@ -203,8 +203,8 @@ typealias IOVector = iovec
/// - returns: The `IOResult` which indicates how many messages could be send and if the operation returned before all messages could be send (because the socket is in non-blocking mode).
/// - throws: An `IOError` if the operation failed.
func sendmmsg(msgs: UnsafeMutableBufferPointer<MMsgHdr>) throws -> IOResult<Int> {
return try withUnsafeFileDescriptor { fd in
try Posix.sendmmsg(sockfd: fd, msgvec: msgs.baseAddress!, vlen: CUnsignedInt(msgs.count), flags: 0)
return try withUnsafeHandle {
try Posix.sendmmsg(sockfd: $0, msgvec: msgs.baseAddress!, vlen: CUnsignedInt(msgs.count), flags: 0)
}
}
@ -214,8 +214,8 @@ typealias IOVector = iovec
/// - how: the mode of `Shutdown`.
/// - throws: An `IOError` if the operation failed.
func shutdown(how: Shutdown) throws {
return try withUnsafeFileDescriptor { fd in
try Posix.shutdown(descriptor: fd, how: how)
return try withUnsafeHandle {
try Posix.shutdown(descriptor: $0, how: how)
}
}
}

View File

@ -538,6 +538,29 @@ internal enum Posix {
}
}
/// `NIOFailedToSetSocketNonBlockingError` indicates that NIO was unable to set a socket to non-blocking mode, either
/// when connecting a socket as a client or when accepting a socket as a server.
///
/// This error should never happen because a socket should always be able to be set to non-blocking mode. Unfortunately,
/// we have seen this happen on Darwin.
public struct NIOFailedToSetSocketNonBlockingError: Error {}
internal extension Posix {
static func setNonBlocking(socket: CInt) throws {
let flags = try Posix.fcntl(descriptor: socket, command: F_GETFL, value: 0)
do {
let ret = try Posix.fcntl(descriptor: socket, command: F_SETFL, value: flags | O_NONBLOCK)
assert(ret == 0, "unexpectedly, fcntl(\(socket), F_SETFL, \(flags) | O_NONBLOCK) returned \(ret)")
} catch let error as IOError {
if error.errnoCode == EINVAL {
// Darwin seems to sometimes do this despite the docs claiming it can't happen
throw NIOFailedToSetSocketNonBlockingError()
}
throw error
}
}
}
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
internal enum KQueue {

View File

@ -2469,7 +2469,7 @@ public final class ChannelTests: XCTestCase {
guard let channel = channel as? SocketChannel else {
throw ThisIsNotASocketChannelError()
}
try channel.socket.withUnsafeFileDescriptor { fd in
try channel.socket.withUnsafeHandle { fd in
var pollFd: pollfd = .init(fd: fd, events: Int16(POLLIN), revents: 0)
let nfds = try Posix.poll(fds: &pollFd, nfds: 1, timeout: -1)
XCTAssertEqual(1, nfds)

View File

@ -43,7 +43,7 @@ final class SALChannelTest: XCTestCase, SALTest {
try self.assertWrite(expectedFD: .max, expectedBytes: buffer, return: .processed(buffer.readableBytes))
try self.assertWritev(expectedFD: .max, expectedBytes: [buffer, buffer], return: .processed(2 * buffer.readableBytes))
try self.assertDeregister { selectable in
try selectable.withUnsafeFileDescriptor {
try selectable.withUnsafeHandle {
XCTAssertEqual(.max, $0)
}
return true

View File

@ -329,7 +329,7 @@ public final class SocketChannelTest : XCTestCase {
let serverSock = try Socket(protocolFamily: AF_INET, type: Posix.SOCK_STREAM)
try serverSock.bind(to: SocketAddress(ipAddress: "127.0.0.1", port: 0))
let serverChannelFuture = try serverSock.withUnsafeFileDescriptor {
let serverChannelFuture = try serverSock.withUnsafeHandle {
ServerBootstrap(group: group).withBoundSocket(descriptor: dup($0))
}
try serverSock.close()
@ -338,7 +338,7 @@ public final class SocketChannelTest : XCTestCase {
let clientSock = try Socket(protocolFamily: AF_INET, type: Posix.SOCK_STREAM)
let connected = try clientSock.connect(to: serverChannel.localAddress!)
XCTAssertEqual(connected, true)
let clientChannelFuture = try clientSock.withUnsafeFileDescriptor {
let clientChannelFuture = try clientSock.withUnsafeHandle {
ClientBootstrap(group: group).withConnectedSocket(descriptor: dup($0))
}
try clientSock.close()
@ -356,7 +356,7 @@ public final class SocketChannelTest : XCTestCase {
let serverSock = try Socket(protocolFamily: AF_INET, type: Posix.SOCK_DGRAM)
try serverSock.bind(to: SocketAddress(ipAddress: "127.0.0.1", port: 0))
let serverChannelFuture = try serverSock.withUnsafeFileDescriptor {
let serverChannelFuture = try serverSock.withUnsafeHandle {
DatagramBootstrap(group: group).withBoundSocket(descriptor: dup($0))
}
try serverSock.close()
@ -523,14 +523,14 @@ public final class SocketChannelTest : XCTestCase {
func testSocketFlagNONBLOCKWorks() throws {
var socket = try assertNoThrowWithValue(try ServerSocket(protocolFamily: PF_INET, setNonBlocking: true))
XCTAssertNoThrow(try socket.withUnsafeFileDescriptor { fd in
XCTAssertNoThrow(try socket.withUnsafeHandle { fd in
let flags = try assertNoThrowWithValue(Posix.fcntl(descriptor: fd, command: F_GETFL, value: 0))
XCTAssertEqual(O_NONBLOCK, flags & O_NONBLOCK)
})
XCTAssertNoThrow(try socket.close())
socket = try assertNoThrowWithValue(ServerSocket(protocolFamily: PF_INET, setNonBlocking: false))
XCTAssertNoThrow(try socket.withUnsafeFileDescriptor { fd in
XCTAssertNoThrow(try socket.withUnsafeHandle { fd in
var flags = try assertNoThrowWithValue(Posix.fcntl(descriptor: fd, command: F_GETFL, value: 0))
XCTAssertEqual(0, flags & O_NONBLOCK)
let ret = try assertNoThrowWithValue(Posix.fcntl(descriptor: fd, command: F_SETFL, value: flags | O_NONBLOCK))
@ -638,13 +638,13 @@ public final class SocketChannelTest : XCTestCase {
type: Posix.SOCK_STREAM,
setNonBlocking: false))
// check initial flags
XCTAssertNoThrow(try s.withUnsafeFileDescriptor { fd in
XCTAssertNoThrow(try s.withUnsafeHandle { fd in
let flags = try Posix.fcntl(descriptor: fd, command: F_GETFL, value: 0)
XCTAssertEqual(0, flags & O_NONBLOCK)
})
// set other random flag
XCTAssertNoThrow(try s.withUnsafeFileDescriptor { fd in
XCTAssertNoThrow(try s.withUnsafeHandle { fd in
let oldFlags = try Posix.fcntl(descriptor: fd, command: F_GETFL, value: 0)
let ret = try Posix.fcntl(descriptor: fd, command: F_SETFL, value: oldFlags | O_ASYNC)
XCTAssertEqual(0, ret)
@ -656,7 +656,7 @@ public final class SocketChannelTest : XCTestCase {
XCTAssertNoThrow(try s.setNonBlocking())
// check both are enabled
XCTAssertNoThrow(try s.withUnsafeFileDescriptor { fd in
XCTAssertNoThrow(try s.withUnsafeHandle { fd in
let flags = try Posix.fcntl(descriptor: fd, command: F_GETFL, value: 0)
XCTAssertEqual(O_ASYNC, flags & O_ASYNC)
XCTAssertEqual(O_NONBLOCK, flags & O_NONBLOCK)

View File

@ -285,7 +285,7 @@ class HookedSocket: Socket, UserKernelInterface {
}
override func write(pointer: UnsafeRawBufferPointer) throws -> IOResult<Int> {
return try self.withUnsafeFileDescriptor { fd in
return try self.withUnsafeHandle { fd in
var buffer = ByteBufferAllocator().buffer(capacity: pointer.count)
buffer.writeBytes(pointer)
try self.userToKernel.waitForEmptyAndSet(.write(fd, buffer))
@ -299,7 +299,7 @@ class HookedSocket: Socket, UserKernelInterface {
}
override func writev(iovecs: UnsafeBufferPointer<IOVector>) throws -> IOResult<Int> {
return try self.withUnsafeFileDescriptor { fd in
return try self.withUnsafeHandle { fd in
let buffers = iovecs.map { iovec -> ByteBuffer in
var buffer = ByteBufferAllocator().buffer(capacity: iovec.iov_len)
buffer.writeBytes(UnsafeRawBufferPointer(start: iovec.iov_base, count: iovec.iov_len))