Add bindings for {send,recv}mmsg.
This commit is contained in:
parent
8b74d21562
commit
a2bb047224
|
@ -11,3 +11,19 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#define _GNU_SOURCE
|
||||
#include <c_nio_linux.h>
|
||||
|
||||
_Static_assert(sizeof(CNIOLinux_mmsghdr) == sizeof(struct mmsghdr));
|
||||
|
||||
int CNIOLinux_sendmmsg(int sockfd, CNIOLinux_mmsghdr *msgvec, unsigned int vlen, int flags) {
|
||||
// This is technically undefined behaviour, but it's basically fine because these types are the same size, and we
|
||||
// don't think the compiler is inclined to blow anything up here.
|
||||
return sendmmsg(sockfd, (struct mmsghdr *)msgvec, vlen, flags);
|
||||
}
|
||||
|
||||
int CNIOLinux_recvmmsg(int sockfd, CNIOLinux_mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout) {
|
||||
// This is technically undefined behaviour, but it's basically fine because these types are the same size, and we
|
||||
// don't think the compiler is inclined to blow anything up here.
|
||||
return recvmmsg(sockfd, (struct mmsghdr *)msgvec, vlen, flags, timeout);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,32 @@
|
|||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/timerfd.h>
|
||||
#endif
|
||||
#include <sys/socket.h>
|
||||
|
||||
// Some explanation is required here.
|
||||
//
|
||||
// Due to SR-6772, we cannot get Swift code to directly see any of the mmsg structures or
|
||||
// functions. However, we *can* get C code built by SwiftPM to see them. For this reason we
|
||||
// elect to provide a selection of shims to enable Swift code to use recv_mmsg and send_mmsg.
|
||||
// Mostly this is fine, but to minimise the overhead we want the Swift code to be able to
|
||||
// create the msgvec directly without requiring further memory fussiness in our C shim.
|
||||
// That requires us to also construct a C structure that has the same layout as struct mmsghdr.
|
||||
//
|
||||
// Conveniently glibc has pretty strict ABI stability rules, and this structure is part of the
|
||||
// glibc ABI, so we can just reproduce the structure definition here and feel confident that it
|
||||
// will be sufficient.
|
||||
//
|
||||
// If SR-6772 ever gets resolved we can remove this shim.
|
||||
//
|
||||
// https://bugs.swift.org/browse/SR-6772
|
||||
|
||||
typedef struct {
|
||||
struct msghdr msg_hdr;
|
||||
unsigned int msg_len;
|
||||
} CNIOLinux_mmsghdr;
|
||||
|
||||
int CNIOLinux_sendmmsg(int sockfd, CNIOLinux_mmsghdr *msgvec, unsigned int vlen, int flags);
|
||||
int CNIOLinux_recvmmsg(int sockfd, CNIOLinux_mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the SwiftNIO open source project
|
||||
//
|
||||
// Copyright (c) 2017-2018 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// This is a companion to System.swift that provides only Linux specials: either things that exist
|
||||
// only on Linux, or things that have Linux-specific extensions.
|
||||
import CNIOLinux
|
||||
|
||||
#if os(Linux)
|
||||
internal enum TimerFd {
|
||||
public static let TFD_CLOEXEC = CNIOLinux.TFD_CLOEXEC
|
||||
public static let TFD_NONBLOCK = CNIOLinux.TFD_NONBLOCK
|
||||
|
||||
@inline(never)
|
||||
public static func timerfd_settime(fd: Int32, flags: Int32, newValue: UnsafePointer<itimerspec>, oldValue: UnsafeMutablePointer<itimerspec>?) throws {
|
||||
_ = try wrapSyscall({
|
||||
CNIOLinux.timerfd_settime(fd, flags, newValue, oldValue)
|
||||
})
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
public static func timerfd_create(clockId: Int32, flags: Int32) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
CNIOLinux.timerfd_create(clockId, flags)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
internal enum EventFd {
|
||||
public static let EFD_CLOEXEC = CNIOLinux.EFD_CLOEXEC
|
||||
public static let EFD_NONBLOCK = CNIOLinux.EFD_NONBLOCK
|
||||
public typealias eventfd_t = CNIOLinux.eventfd_t
|
||||
|
||||
@inline(never)
|
||||
public static func eventfd_write(fd: Int32, value: UInt64) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
CNIOLinux.eventfd_write(fd, value)
|
||||
})
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
public static func eventfd_read(fd: Int32, value: UnsafeMutablePointer<UInt64>) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
CNIOLinux.eventfd_read(fd, value)
|
||||
})
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
public static func eventfd(initval: Int32, flags: Int32) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
CNIOLinux.eventfd(0, Int32(EFD_CLOEXEC | EFD_NONBLOCK))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
internal enum Epoll {
|
||||
public typealias epoll_event = CNIOLinux.epoll_event
|
||||
public static let EPOLL_CTL_ADD = CNIOLinux.EPOLL_CTL_ADD
|
||||
public static let EPOLL_CTL_MOD = CNIOLinux.EPOLL_CTL_MOD
|
||||
public static let EPOLL_CTL_DEL = CNIOLinux.EPOLL_CTL_DEL
|
||||
public static let EPOLLIN = CNIOLinux.EPOLLIN
|
||||
public static let EPOLLOUT = CNIOLinux.EPOLLOUT
|
||||
public static let EPOLLERR = CNIOLinux.EPOLLERR
|
||||
public static let EPOLLRDHUP = CNIOLinux.EPOLLRDHUP
|
||||
public static let EPOLLET = CNIOLinux.EPOLLET
|
||||
|
||||
@inline(never)
|
||||
public static func epoll_create(size: Int32) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
CNIOLinux.epoll_create(size)
|
||||
})
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
public static func epoll_ctl(epfd: Int32, op: Int32, fd: Int32, event: UnsafeMutablePointer<epoll_event>) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
CNIOLinux.epoll_ctl(epfd, op, fd, event)
|
||||
})
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
public static func epoll_wait(epfd: Int32, events: UnsafeMutablePointer<epoll_event>, maxevents: Int32, timeout: Int32) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
return CNIOLinux.epoll_wait(epfd, events, maxevents, timeout)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
internal enum LinuxSocket {
|
||||
@inline(never)
|
||||
public static func sendmmsg(sockfd: CInt, msgvec: UnsafeMutablePointer<CNIOLinux_mmsghdr>, vlen: CUnsignedInt, flags: CInt) throws -> IOResult<Int> {
|
||||
return try wrapSyscallMayBlock({
|
||||
return Int(CNIOLinux.CNIOLinux_sendmmsg(sockfd, msgvec, vlen, flags))
|
||||
})
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
public static func recvmmsg(sockfd: CInt, msgvec: UnsafeMutablePointer<CNIOLinux_mmsghdr>, vlen: CUnsignedInt, flags: CInt, timeout: UnsafeMutablePointer<timespec>?) throws -> IOResult<Int> {
|
||||
return try wrapSyscallMayBlock({
|
||||
return Int(CNIOLinux.CNIOLinux_recvmmsg(sockfd, msgvec, vlen, flags, timeout))
|
||||
})
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -12,8 +12,16 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if os(Linux)
|
||||
import struct CNIOLinux.CNIOLinux_mmsghdr
|
||||
#endif
|
||||
|
||||
public typealias IOVector = iovec
|
||||
|
||||
#if os(Linux)
|
||||
internal typealias MMsgHdr = CNIOLinux_mmsghdr
|
||||
#endif
|
||||
|
||||
// TODO: scattering support
|
||||
final class Socket : BaseSocket {
|
||||
static var writevLimitBytes: Int {
|
||||
|
@ -90,4 +98,22 @@ final class Socket : BaseSocket {
|
|||
|
||||
return try Posix.sendfile(descriptor: self.descriptor, fd: fd, offset: offset, count: count)
|
||||
}
|
||||
|
||||
#if os(Linux)
|
||||
func recvmmsg(msgs: UnsafeMutableBufferPointer<MMsgHdr>) throws -> IOResult<Int> {
|
||||
guard self.open else {
|
||||
throw IOError(errnoCode: EBADF, reason: "can't read from socket as it's not open anymore.")
|
||||
}
|
||||
|
||||
return try LinuxSocket.recvmmsg(sockfd: self.descriptor, msgvec: msgs.baseAddress!, vlen: CUnsignedInt(msgs.count), flags: 0, timeout: nil)
|
||||
}
|
||||
|
||||
func sendmmsg(msgs: UnsafeMutableBufferPointer<MMsgHdr>) throws -> IOResult<Int> {
|
||||
guard self.open else {
|
||||
throw IOError(errnoCode: EBADF, reason: "can't write to socket as it's not open anymore.")
|
||||
}
|
||||
|
||||
return try LinuxSocket.sendmmsg(sockfd: self.descriptor, msgvec: msgs.baseAddress!, vlen: CUnsignedInt(msgs.count), flags: 0)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#else
|
||||
let badOS = { fatalError("unsupported OS") }()
|
||||
#endif
|
||||
import CNIOLinux
|
||||
|
||||
private func isBlacklistedErrno(_ code: Int32) -> Bool {
|
||||
switch code {
|
||||
|
@ -40,7 +39,7 @@ private func isBlacklistedErrno(_ code: Int32) -> Bool {
|
|||
|
||||
/* Sorry, we really try hard to not use underscored attributes. In this case however we seem to break the inlining threshold which makes a system call take twice the time, ie. we need this exception. */
|
||||
@inline(__always)
|
||||
private func wrapSyscallMayBlock<T: FixedWidthInteger>(_ fn: () throws -> T , where function: StaticString = #function) throws -> IOResult<T> {
|
||||
internal func wrapSyscallMayBlock<T: FixedWidthInteger>(_ fn: () throws -> T , where function: StaticString = #function) throws -> IOResult<T> {
|
||||
while true {
|
||||
let res = try fn()
|
||||
if res == -1 {
|
||||
|
@ -62,7 +61,7 @@ private func wrapSyscallMayBlock<T: FixedWidthInteger>(_ fn: () throws -> T , wh
|
|||
|
||||
/* Sorry, we really try hard to not use underscored attributes. In this case however we seem to break the inlining threshold which makes a system call take twice the time, ie. we need this exception. */
|
||||
@inline(__always)
|
||||
private func wrapSyscall<T: FixedWidthInteger>(_ fn: () throws -> T, where function: StaticString = #function) throws -> T {
|
||||
internal func wrapSyscall<T: FixedWidthInteger>(_ fn: () throws -> T, where function: StaticString = #function) throws -> T {
|
||||
while true {
|
||||
let res = try fn()
|
||||
if res == -1 {
|
||||
|
@ -303,89 +302,10 @@ internal enum Posix {
|
|||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if os(Linux)
|
||||
internal enum TimerFd {
|
||||
public static let TFD_CLOEXEC = CNIOLinux.TFD_CLOEXEC
|
||||
public static let TFD_NONBLOCK = CNIOLinux.TFD_NONBLOCK
|
||||
|
||||
@inline(never)
|
||||
public static func timerfd_settime(fd: Int32, flags: Int32, newValue: UnsafePointer<itimerspec>, oldValue: UnsafeMutablePointer<itimerspec>?) throws {
|
||||
_ = try wrapSyscall({
|
||||
CNIOLinux.timerfd_settime(fd, flags, newValue, oldValue)
|
||||
})
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
public static func timerfd_create(clockId: Int32, flags: Int32) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
CNIOLinux.timerfd_create(clockId, flags)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
internal enum EventFd {
|
||||
public static let EFD_CLOEXEC = CNIOLinux.EFD_CLOEXEC
|
||||
public static let EFD_NONBLOCK = CNIOLinux.EFD_NONBLOCK
|
||||
public typealias eventfd_t = CNIOLinux.eventfd_t
|
||||
|
||||
@inline(never)
|
||||
public static func eventfd_write(fd: Int32, value: UInt64) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
CNIOLinux.eventfd_write(fd, value)
|
||||
})
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
public static func eventfd_read(fd: Int32, value: UnsafeMutablePointer<UInt64>) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
CNIOLinux.eventfd_read(fd, value)
|
||||
})
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
public static func eventfd(initval: Int32, flags: Int32) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
CNIOLinux.eventfd(0, Int32(EFD_CLOEXEC | EFD_NONBLOCK))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
internal enum Epoll {
|
||||
public typealias epoll_event = CNIOLinux.epoll_event
|
||||
public static let EPOLL_CTL_ADD = CNIOLinux.EPOLL_CTL_ADD
|
||||
public static let EPOLL_CTL_MOD = CNIOLinux.EPOLL_CTL_MOD
|
||||
public static let EPOLL_CTL_DEL = CNIOLinux.EPOLL_CTL_DEL
|
||||
public static let EPOLLIN = CNIOLinux.EPOLLIN
|
||||
public static let EPOLLOUT = CNIOLinux.EPOLLOUT
|
||||
public static let EPOLLERR = CNIOLinux.EPOLLERR
|
||||
public static let EPOLLRDHUP = CNIOLinux.EPOLLRDHUP
|
||||
public static let EPOLLET = CNIOLinux.EPOLLET
|
||||
|
||||
@inline(never)
|
||||
public static func epoll_create(size: Int32) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
CNIOLinux.epoll_create(size)
|
||||
})
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
public static func epoll_ctl(epfd: Int32, op: Int32, fd: Int32, event: UnsafeMutablePointer<epoll_event>) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
CNIOLinux.epoll_ctl(epfd, op, fd, event)
|
||||
})
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
public static func epoll_wait(epfd: Int32, events: UnsafeMutablePointer<epoll_event>, maxevents: Int32, timeout: Int32) throws -> Int32 {
|
||||
return try wrapSyscall({
|
||||
return CNIOLinux.epoll_wait(epfd, events, maxevents, timeout)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
||||
internal enum KQueue {
|
||||
|
||||
// TODO: Figure out how to specify a typealias to the kevent struct without run into trouble with the swift compiler
|
||||
|
|
Loading…
Reference in New Issue